爲何要引入pug,pug有什麼特別之處呢?有一些嵌套層次較深的頁面,可能會出現巢狀嵌套,以下圖所示javascript
在後期維護和修改時,一不當心少了一個尖括號,或者某個標籤的開始和閉合沒有對應上,就會致使DOM結構的混亂甚至是錯誤。因此,有人發明了HAML,它最大的特點就是使用縮進排列來代替成對標籤。受HAML的啓發,pug進行了javascript的實現。css
Pug原名不叫Pug,是大名鼎鼎的jade,後來因爲商標的緣由,改成Pug,哈巴狗。其實只是換個名字,語法都與jade同樣。醜話說在前面,Pug有它自己的缺點——可移植性差,調試困難,性能並不出色,但使用它能夠加快開發效率。本文將詳細介紹pug模板引擎html
使用npm安裝pugjava
$ npm install pug
但運行pug命令時,提示pug命令未找到jquery
這時,須要安裝pug命令行工具pug-clinpm
[注意]必定要全局安裝pug-cli,不然沒法正常編譯json
npm install pug-cli -g
再運行pug命令時,正常執行bootstrap
在學習pug基礎語法以前,首先要了解pug的命令行的使用api
【基礎編譯】數組
將以下內容輸入文件中,並命名爲index.pug
html
head
title aaa
body
在命令行中敲入pug index.pug便可實現基礎編譯
在當前目錄下生成一個index.html,是index.pug編譯後的結果
【sublime兩列設置】
可是,這樣查看並不方便。下面將sublime設置爲兩列放置,將index.pug和index.html分別放置在左右兩列,方便查看
【自動編譯】
使用pug -w功能能夠實現自動編譯
更改index.pug文件並保存後,index.html文件會實時更新爲最新的編譯的文件
【標準版HTML】
如上所示,默認地,pug編譯出的HTML文件是壓縮版的。若是要編譯標準版的HTML文件,須要設置-P參數
pug index.html -P
【路徑設置】
若是並不但願在當前目錄下輸入編譯後的HTML文件,而是有自定義目錄的需求,則須要設置-o參數
以下設置,index.html將輸入到a目錄下面,若是a目錄不存在,則會新建a目錄
pug index.pug -o a
【重命名】
默認地,編譯後的HTML與pug文件同名。若是須要重命名,則能夠進行以下設置
經過以下設置,能夠同時設置路徑和名稱
[注意]這裏的路徑必須提早創建好,不然不會成功
pug <xx.pug> <xx/xx.html>
最終,test.html文件被保存到templates目錄下
【批量編譯】
假設,編譯href目錄下全部的pug文件
下面介紹關於結構的基礎語法
【樹狀】
在默認狀況下,在每行文本的開頭(或者緊跟白字符的部分)書寫這個 HTML 標籤的名稱。使用縮進來表示標籤間的嵌套關係,這樣能夠構建一個 HTML 代碼的樹狀結構
ul
li Item A
li Item B
li Item C
【內聯】
爲了節省空間, Pug 嵌套標籤提供了一種內聯式語法
a: img
【自閉合】
Pug知道哪些元素是自閉合的,也能夠經過在標籤後加上 /
來明確聲明此標籤是自閉合的
img input img/ input/
【DOCTYPE】
HTML5的DOCTYPE書寫以下
doctype html
也能夠自定義一個 doctype 字面值
doctype html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN"
Pug 提供了三種經常使用的方式來放置內容
【管道文本】
這是最簡單的向模板添加純文本的方法。只須要在每行前面加一個 |
字符,這個字符在類 Unix 系統下經常使用做「管道」功能,所以得名
| 純文本固然也能夠包括 <strong>HTML</strong> 內容。
p
| 但它必須單獨起一行。
【標籤內文本】
這其實是最多見的狀況,文本只須要和標籤名隔開一個空格便可
p 純文本固然也能夠包括 <strong>HTML</strong> 內容。
【嵌入大段文本】
有時可能想要寫一個大段文本塊。好比嵌入腳本或者樣式。只需在標籤後面接一個 .
便可
[注意]不要有空格
script. if (usingPug) console.log('這是明智的選擇。') else console.log('請用 Pug。')
標籤屬性和 HTML 語法很是類似,它們的值就是普通的 JavaScript 表達式。能夠用逗號做爲屬性分隔符,也能夠不加逗號
a(href='baidu.com') 百度 = '\n' a(class='button' href='baidu.com') 百度 = '\n' a(class='button', href='baidu.com') 百度
【多行屬性】
若是有不少屬性,能夠把它們分幾行寫
input( type='checkbox' name='agreement' checked )
【長屬性】
若是有一個很長的屬性,而且JavaScript運行時引擎支持ES2015模板字符串,可使用它來寫屬性值
input(data-json=` { "很是": "長的", "數據": true } `)
【特殊字符】
若是屬性名稱中含有某些奇怪的字符,可能會與 JavaScript 語法產生衝突的話,能夠將它們使用 ""
或者 ''
括起來。還可使用逗號來分割不一樣的屬性
div(class='div-class', (click)='play()') div(class='div-class' '(click)'='play()')
【轉義屬性】
默認狀況下,全部的屬性都通過轉義(即把特殊字符轉換成轉義序列)來防止諸如跨站腳本攻擊之類的攻擊方式。若是要使用特殊符號,須要使用 !=
而不是 =
[注意]未經轉義的緩存代碼十分危險。必須正確處理和過濾用戶的輸入來避免跨站腳本攻擊
div(escaped="<code>") div(unescaped!="<code>")
【布爾值】
在Pug中,布爾值屬性是通過映射的,這樣布爾值(true
和false)
就能接受了。沒有指定值時,默認是true
input(type='checkbox' checked) = '\n' input(type='checkbox' checked=true) = '\n' input(type='checkbox' checked=false) = '\n' input(type='checkbox' checked=true.toString())
【行內樣式】
style
(樣式)屬性能夠是一個字符串(就像其餘普通的屬性同樣)還能夠是一個對象
a(style={color: 'red', background: 'green'})
【類和ID】
類可使用 .classname
語法來定義,ID 可使用 #idname
語法來定義
考慮到使用 div
做爲標籤名這種行爲實在是太常見了,因此若是省略掉標籤名稱的話,它就是默認值
a.button .content ="\n" a#main-link #content
標籤支持一種標籤嵌入的寫法,形式以下
#[標籤名(標籤屬性) 標籤內容]
對於內聯標籤來講,這種寫法比較簡單
p.
這是一個很長很長並且還很無聊的段落,尚未結束,是的,很是很是地長。
忽然出現了一個 #[strong 充滿力量感的單詞],這確實讓人難以 #[em 忽視]。
【空格調整】
Pug 默認會去除一個標籤先後的全部空格,而標籤嵌入功能能夠在須要嵌入的位置上處理先後空格
p
| 若是我不用嵌入功能來書寫,一些標籤好比 strong strong | 和 em em | 可能會產生意外的結果。 p. 若是用了嵌入,在 #[strong strong] 和 #[em em] 旁的空格就會讓我舒服多了。
【單行註釋】
單行註釋和 JavaScript 相似,可是必須獨立一行
// 一些內容 p foo p bar
【不輸出註釋】
只須要加上一個橫槓,就可使用不輸出註釋
//- 這行不會出如今結果裏 p foo p bar
【塊註釋】
body // 隨便寫多少字 都不要緊。
【條件註釋】
Pug 沒有特殊的語法來表示條件註釋(conditional comments)。不過由於全部以 <
開頭的行都會被看成純文本,所以直接寫一個 HTML 風格的條件註釋也是沒問題的
<!--[if IE 8]> <html lang="en" class="lt-ie9"> <![endif]--> <!--[if gt IE 8]><!--> <html lang="en"> <!--<![endif]-->
如下是關於模板邏輯的語法
【不輸出的代碼】
用 -
開始一段不直接進行輸出的代碼
- for (var x = 0; x < 3; x++) li item
【輸出的代碼】
用=
開始一段帶有輸出的代碼,它應該是能夠被求值的一個JavaScript表達式。爲安全起見,它將被HTML轉義
p = '這個代碼被 <轉義> 了!' p= '這個代碼被 <轉義> 了!'
【不轉義的輸出代碼】
用 !=
開始一段不轉義的,帶有輸出的代碼。這將不會作任何轉義,因此用於執行用戶的輸入將會不安全
p != '這段文字 <strong>沒有</strong> 被轉義!' p!= '這段文字' + ' <strong>沒有</strong> 被轉義!'
【內容變量】
使用=或#{}來進行變量的真實值替換
- var title = "On Dogs: Man's Best Friend";
- var author = "enlore"; - var theGreat = "<span>轉義!</span>"; h1= title p #{author} 筆下源於真情的創做。 p 這是安全的:#{theGreat}
在 #{
和 }
裏面的代碼也會被求值、轉義,並最終嵌入到模板的渲染輸出中
- var msg = "not my inside voice";
p This is #{msg.toUpperCase()}
Pug 足夠聰明來分辨到底哪裏纔是嵌入表達式的結束,因此不用擔憂表達式中有 }
,也不須要額外的轉義
p 不要轉義 #{'}'}!
若是須要表示一個 #{
文本,能夠轉義它,也能夠用嵌入功能來生成
p Escaping works with \#{interpolation}
p Interpolation works with #{'#{interpolation}'} too!
使用!{}嵌入沒有轉義的文本進入模板中
- var riskyBusiness = "<em>我但願經過外籍教師 Peter 找一位英語筆友。</em>";
.quote
p 李華:!{riskyBusiness}
[注意]若是直接使用用戶提供的數據,未進行轉義的內容可能會帶來安全風險
【屬性變量】
若是要在屬性當中使用變量的話,須要進行以下操做
- var url = 'pug-test.html'; a(href='/' + url) 連接 = '\n' - url = 'https://example.com/' a(href=url) 另外一個連接
若是JavaScript運行時支持 ECMAScript 2015 模板字符串,還可使用下列方式來簡化屬性值
- var btnType = 'info' - var btnSize = 'lg' button(type='button' class='btn btn-' + btnType + ' btn-' + btnSize) = '\n' button(type='button' class=`btn btn-${btnType} btn-${btnSize}`)
&attributes
語法能夠將一個對象轉化爲一個元素的屬性列表
div#foo(data-bar="foo")&attributes({'data-foo': 'bar'}) - var attributes = {}; - attributes.class = 'baz'; div#foo(data-bar="foo")&attributes(attributes)
【變量來源】
變量來源有三種,分別是pug文件內部、命令行參數和外部JSON文件
一、pug文件內部
二、命令行參數
使用--obj參數,就能夠跟隨一個對象形式的參數
三、外部JSON文件
使用-O,跟隨一個JSON文件的路徑便可
這三種方式,pug文件內部的變量優先級最多,而外部JSON文件和命令行傳參優先級相同
以下所示,外部JSON文件和命令行傳參兩種方式都存在,因爲--obj寫在-w後面,最終以命令行傳參爲準
Pug 的條件判斷的通常形式的括號是可選的,因此能夠省略掉開頭的 -
,效果徹底相同。相似一個常規的 JavaScript 語法形式
【if else】
- var user = { description: 'foo bar baz' } - var authorised = false #user if user.description h2.green 描述 p.description= user.description else if authorised h2.blue 描述 p.description. 用戶沒有添加描述。 不寫點什麼嗎…… else h2.red 描述 p.description 用戶沒有描述
Pug 一樣也提供了它的反義版本 unless
unless user.isAnonymous
p 您已經以 #{user.name} 的身份登陸。
【switch】
case
是 JavaScript 的 switch
指令的縮寫,而且它接受以下的形式
- var friends = 10 case friends when 0 p 您沒有朋友 when 1 p 您有一個朋友 default p 您有 #{friends} 個朋友
在某些狀況下,若是不想輸出任何東西的話,能夠明確地加上一個原生的 break
語句
- var friends = 0 case friends when 0 - break when 1 p 您的朋友不多 default p 您有 #{friends} 個朋友
也可使用塊展開的語法
- var friends = 1 case friends when 0: p 您沒有朋友 when 1: p 您有一個朋友 default: p 您有 #{friends} 個朋友
Pug 目前支持兩種主要的迭代方式: each
和 while
【each】
這是 Pug 的首選迭代方式
ul each val in [1, 2, 3, 4, 5] li= val
能夠在迭代時得到索引值
ul each val, index in ['〇', '一', '二'] li= index + ': ' + val
可以迭代對象中的鍵值
ul each val, index in {1:'一',2:'二',3:'三'} li= index + ': ' + val
用於迭代的對象或數組僅僅是個 JavaScript 表達式,所以它能夠是變量、函數調用的結果,又或者其餘
- var values = []; ul each val in values.length ? values : ['沒有內容'] li= val
還能添加一個 else
塊,這個語句塊將會在數組與對象沒有可供迭代的值時被執行
- var values = []; ul each val in values li= val else li 沒有內容
[注意]也可使用 for
做爲 each
的別稱
【while】
也可使用 while
來建立一個循環
- var n = 0; ul while n < 4 li= n++
混入是一種容許在 Pug 中重複使用一整個代碼塊的方法
//- 定義 mixin list ul li foo li bar li baz //- 使用 +list +list
混入能夠被編譯成函數形式,並傳遞一些參數
mixin pet(name) li.pet= name ul +pet('貓') +pet('狗') +pet('豬')
混入也能夠把一整個代碼塊像內容同樣傳遞進來
mixin article(title) .article .article-wrapper h1= title if block block else p 沒有提供任何內容。 +article('Hello world') +article('Hello world') p 這是我 p 隨便寫的文章
混入也能夠隱式地,從「標籤屬性」獲得一個參數 attributes
也能夠直接用 &attributes
方法來傳遞 attributes
參數
mixin link(href, name) a(class!=attributes.class href=href)= name +link('/foo', 'foo')(class="btn")
[注意]+link(class="btn")
等價於 +link()(class="btn")
,由於 Pug 會判斷括號內的內容是屬性仍是參數。但最好使用後面的寫法,明確地傳遞空的參數,確保第一對括號內始終都是參數列表
能夠用剩餘參數(rest arguments)語法來表示參數列表最後傳入若干個長度不定的參數
mixin list(id, ...items) ul(id=id) each item in items li= item +list('my-list', 1, 2, 3, 4)
包含(include)功能容許把另外的文件內容插入進來
//- index.pug doctype html html include includes/head.pug body h1 個人網站 p 歡迎來到我這簡陋得不能再簡陋的網站。 include includes/foot.pug
//- includes/head.pug head title 個人網站 script(src='/javascripts/jquery.js') script(src='/javascripts/app.js')
//- includes/foot.pug footer#footer p Copyright (c) foobar
被包含的若是不是 Pug 文件,那麼就只會看成文本內容來引入
//- index.pug doctype html html head style include style.css body h1 個人網站 p 歡迎來到我這簡陋得不能再簡陋的網站。 script include script.js
/* style.css */ h1 { color: red; }
// script.js console.log('真了不得!');
【覆蓋】
Pug 支持使用 block
和 extends
關鍵字進行模板的繼承。一個稱之爲「塊」(block)的代碼塊,能夠被子模板覆蓋、替換。這個過程是遞歸的。
Pug 的塊能夠提供一份默認內容,固然這是可選的
//- layout.pug html head
meta(charset="UTF-8") title 個人站點 - #{title} block scripts script(src='/jquery.js') body block content block foot #footer p 一些頁腳的內容
如今來擴展這個佈局:只須要簡單地建立一個新文件,並以下所示用一句 extends
來指出這個被繼承的模板的路徑。如今能夠定義若干個新的塊來覆蓋父模板裏對應的「父塊」。值得注意的是,由於這裏的 foot
塊 沒有 被重定義,因此會依然輸出「一些頁腳的內容」
//- pet.pug p= petName
//- page-a.pug extends layout.pug block scripts script(src='/jquery.js') script(src='/pets.js') block content h1= title - var pets = ['貓', '狗'] each petName in pets include pet.pug
一樣,也能夠覆蓋一個塊並在其中提供一些新的塊。以下所示,content
塊如今暴露出兩個新的塊 sidebar
和 primary
用來被擴展。固然,它的子模板也能夠把整個 content
給覆蓋掉
//- sub-layout.pug extends layout.pug block content .sidebar block sidebar p 什麼都沒有 .primary block primary p 什麼都沒有
//- page-b.pug extends sub-layout.pug block content .sidebar block sidebar p 什麼都沒有 .primary block primary p 什麼都沒有
【擴展】
Pug 容許去替換(默認的行爲)、prepend
(向頭部添加內容),或者 append
(向尾部添加內容)一個塊。 假設有一份默認的腳本要放在 head
塊中,並且但願將它應用到 每個頁面,能夠進行以下操做
//- layout.pug html head block head script(src='/vendor/jquery.js') script(src='/vendor/caustic.js') body block content
如今假設有一個頁面,那是一個 JavaScript 編寫的遊戲。但願把一些遊戲相關的腳本也像默認的那些腳本同樣放進去,那麼只要簡單地 append
這個塊:
//- page.pug extends layout.pug block prepend head script(src='/vendor/three.js') block append head script(src='/game.js')
當使用 block append
或者 block prepend
時,block
關鍵字是可省略的:
//- page.pug extends layout.pug prepend head script(src='/vendor/three.js') append head script(src='/game.js')
//- index.pug doctype html html head meta(charset="UTF-8") title= documentTitle each val in srcStyles link(href= baseStyle +'/' + val) body header.hd nav.hd-navbar.m-navbar.m-navbar_primary .hd-navbar-tel 聯繫方式: #{tel} ul.hd-navbar-nav each val in mainNavItem li.Hnn-item.m-btn.m-btn_info a(href="#")= val section.main h1.main-title 個人文檔 p.main-content. 這是一個很長很長並且還很無聊的段落,尚未結束,是的,很是很是地長。 忽然出現了一個 #[strong 充滿力量感的單詞],這確實讓人難以 #[em 忽視]。 footer.ft p Copyright (c) 小火柴的藍色理想 each val in srcScripts script(src=baseScript + '/' + val)
//- data.json { "documentTitle":"測試文檔", "tel":"400-888-8888", "mainNavItem":['登陸','註冊','關於','幫助'], "baseStyle":'style', "srcStyles":['bootstrap.css','main.css'], "baseScript":'/js', "srcScripts":['jquery.js','app.js'] }