Swift Web 開發之 Vapor - 模版 Leaf(三)

模版引擎,對如今的 Web 開發極爲重要,幾乎全部主流 Web 框架都會支持一種或多種模版引擎,模版引擎能夠分離用戶界面和業務邏輯,工做原理主要是一種翻譯,後端對特定的標記、語法、變量等渲染後再輸送給瀏覽器,現在模版引擎已經很是強大,在相關領域能夠幫助開發者節約不少時間精力,好比緩存、設計分層、插件化。不一樣的模版引擎變幻無窮,各類語言也是層出不窮,好比 PHP 模版引擎中的老大哥 Smarty,Python 的 Jinja2,也是 Flask 中內置的模版引擎,現在前端也有新生模版引擎,依賴前端的性能提高,像後端同樣處理模版語言渲染數據。javascript

Leaf 做爲 Vapor 官方提供的組件之一原生集成在 Vapor 中,Leaf 模版文件以 .leaf 結尾,模版語法夾雜在 HTML 之間,咱們能夠直接使用而不須要引入其餘外部依賴。html

渲染

咱們能夠在路由中進行模版的渲染,同時附帶給模版傳參數,參數以 Dict 的形式放在第二個位置,而後在 .leaf 模版文件中就能拿到對應的數據。前端

drop.get { req in
    return try drop.view.make("index.leaf", ['greeting': "Hello world!"])
}複製代碼

標記 (#)

Leaf 使用 # 做爲模版語法的標記,筆者也很苦惱爲啥 Vapor 的做者要用井號作語法標記,在 Github 也有人提出 # 會與 HTML/CSS 有衝突(issue #11)。java

咱們能夠用 #() 來輸出 HTML, 這裏須要注意的是這樣輸出 Leaf 會對其中的 HTML 進行轉義,舉個例子,以下代碼輸出到瀏覽器,HTML 源代碼真是內容實際上是:hello ,並無被 A 標籤包起來。node

#(<a>hello</a>)複製代碼

若是想輸出原始 HTML 怎麼辦呢?咱們能夠用 raw() {} 來輸出不會被轉移的 HTML,也就是說到瀏覽器會被解析成對應的 HTML 代碼,以下會在瀏覽器輸出:<a>hello</a>git

#raw() { <a>hello</a> }複製代碼

須要注意的是不要讓用戶輸入內容用 #raw(){ } 進行原始輸出,好比評論,以避免形成惡意代碼執行的漏洞!github

變量

#(variable)複製代碼

比較

#equal(leaf, leaf)複製代碼

判斷

下面這段代碼邏輯就是 if:elif:elseswift

#if(entering) {
  Hello, there!
} ##if(leaving) {
  Goodbye!
} ##else() {
  I've been here the whole time.
}複製代碼

循環

#loop(users, "user") {
  Hello, #(user.name)! </br >
}複製代碼

基本循環用 #loop ,第一個參數傳入數組,第二個參數寫出循環內部的實例變量名,假設個人 users 內容是 ["Objective-C"、"Swift"、"Vapor"],輸出內容在瀏覽器看起來就會是這個樣子:後端

Hello, Objective-C!數組

Hello, Swift!

Hello, Vapor!

模塊化

咱們構建一些多頁面 Web 程序,經常會有每一個頁面或者一種頁面部分類似的內容,好比網站的頭部、腳部,頭部通常用來放導航欄,腳部放一些版權聲明、三方連接等等,那麼在整個網站任何頁面中這兩部分的內容基本都是一致的,那麼咱們能夠把這兩部分的內容抽出一個單獨的 .leaf 文件存放,並在須要的地方引入進來就能夠了。

Leaf 提供了四個方法來引入/包含其餘模版:

  • Import: #import("template")
  • Export: #export("template") { Leaf/HTML }
  • Extend: #extend("template")
  • Embed: #embed("template")

這四個標籤都不須要補全 .leaf 後綴,Leaf 會自動查找對應文件。

#import() 用來聲明一個插入點在當前模版。

#extend() 用來繼承一個模版,使用模版的內容,而後就只能#export() 填充以前聲明的插入點。

#embed("body") 纔是用來引入一個模版的內容到當前位置。

舉個栗子 🌰:

/// base.leaf
<html>#import("html-content")</html>

/// index.leaf
#extend("base")
#export("html-content") {
    Hello
}複製代碼

渲染 index.leaf 的內容以下: <html>Hello</html>index.leaf 頁繼承了 base.leaf 的內容, 並把 Hello 這個字符串被插入到了 <html> 標記中間。注意以前的描述,繼承以後只能使用 #export() 填充,也就是在 index.leaf 文件中其餘地方任何內容將不會被渲染。

而後是 #embed() 的用法,能夠直接引入另外一個文件:

/// html-content.leaf
Hello

/// index.leaf
<html>#embed("html-content")</html>複製代碼

上面代碼渲染 index.leaf 後的結果和以前是同樣的。

自定義標籤

有時候官方提供的標籤功能不夠咱們使用,固然 Leaf 給咱們留出了自定義的空間,咱們能夠聲明本身的標籤而後在 .leaf 模版種使用,就像 #equal()#import() 同樣。

咱們能夠經過聲明一個類來自定義一種標籤,借用一下官方的例子,定義在 Leaf/Tag/Models/Index.swift:,該代碼定義了一個方法用來獲取數組中指定下標的一個元素:

class Index: BasicTag {
    let name = "index1"

    func run(arguments: [Argument]) throws -> Node? {
        guard
            arguments.count == 2,
            let array = arguments[0].value?.nodeArray,
            let index = arguments[1].value?.int,
            index < array.count
            else { return nil }
        return array[index]
    }
}複製代碼

而後再向 droplet 註冊它:

if let leaf = drop.view as? LeafRenderer {
    leaf.stem.register(Index())
}複製代碼

有了自定義標籤的功能,咱們能夠豐富不少本身的功能邏輯,甚至定義一個快速解析 Markdown 的標籤,用起來會至關方便。

語法高亮

根據 Vapor 官方提示,VSCode 和 Atom 有對應的語法高亮插件,若有須要能夠試試。

  • Visual Studio Code - html-leaf
  • Atom - language-leaf
  • Xcode - 目前沒有很好支持的插件,不過能夠設置 Xcode 編輯器語法高亮爲 HTML,稍會有改善

結語

另外若是你喜歡相似於 Django 和 Mustache 式的語法,能夠看看 Stencil,也是一個純 Swift 寫的模版引擎。

這是 [Swift Web 開發之 Vapor] 系列的第三篇,說了說 Vapor 中自帶的 Leaf 模版引擎,按照筆者目前的使用狀況來看其實 Leaf 還不太成熟,雖然還有太多須要優化改進的地方,不過我相信以後必定會愈來愈好的。因此不要懼怕,趕忙來寫 Swift Server Side 吧!

以前開的坑在用 Vapor 寫一個博客程序 NSPress,若是你們有興趣歡迎討論。

相關文章
相關標籤/搜索