Render,顧名思義,要進行頁面渲染。Go 語言不但自帶有強大的 http 庫,還自帶了 HTML 模闆引擎。Echo 架構對模闆引擎進行了一些額外處理,并提供了給使用者自定義頁面渲染的接口。本文就相關問題進行探讨。
模闆渲染
Echo 架構的 Context 接口提供了下面的方法進行頁面渲染:
// echo 包中 Context 接口的方法
其中,code 是 HTTP Status,name 是定義的模闆名,data 是模闆可能需要的資料。執行這個方法後,通過資料渲染模闆,并發送帶有 HTTP 狀态的 text/html 響應。可以通過 Echo.Renderer 來注冊模闆,進而允許我們使用任何模闆引擎。
Renderer 接口定義如下:
// Renderer is the interface that wraps the Render function.
這裡可能會有點迷糊,怎麼有兩個 Render 方法,而且它們的簽名還不一樣。這裡的邏輯是這樣的:
- echo.Echo 類型有一個 Renderer 接口類型的字段,用來注冊模闆引擎;
- echo.Context 接口類型有一個 Render 方法,在 Handle 中我們通過調用 Context 的 Render 方法進行模闆渲染;
- 在 Context 的 Render 方法内部(當然是 echo 中 Context 接口的預設實作),會調用 echo.Echo 的字段 Renderer 的 Render 方法,進行具體的模闆渲染;
這裡是具體的渲染源碼:
可見,如果調用了 Context#Render 進行模闆渲染,但并沒有注冊模闆引擎則會報錯(ErrRendererNotRegistered)。
內建标準庫模闆引擎
1、我們先定義一個類型:Template,然後實作 Echo.Renderer 接口,即提供 Render 方法。
type Template
2、接着預編譯一個模闆。定義一個模闆檔案:template/index.html,内容如下:
然後預編譯得到 Template 的執行個體:
3、注冊模闆引擎:
4、在 Handler 中渲染模闆:
"/",
注意這裡的 index 是模闆檔案中
define "index"
,而不是檔案名。
編譯後運作,浏覽器正常顯示:Hello,studygolang!
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5CZ0MmY4IDO4QjYiVDM3gzM1QWNmVWYjRmY4QmN2gDZj9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
通用化定制
一般的,頁面會有一些通用的部分,比如頭部、尾部等。是以業界通常的做法是有一個 layout,而且還可能不止一個 layout,因為普通使用者看到的和背景看到的頭部、尾部一般會不一樣。那這樣的通用化定制需求該如何內建到 Echo 的 Render 中呢?
先考慮隻有一種 layout 的情況。定義一個類型 layoutTemplate,實作 Echo.Renderer 接口:
type layoutTemplate
然後注冊該 Renderer,并在 Handler 中渲染,注意 ctx.Render 的第二個參數,跟上面說的不一樣,我們傳遞的是子模闆的檔案名:index.html。
這裡用到了兩個模闆檔案:layout.html 和 index.html,來源 Hugo 的 soho 這個模闆。
html>
這是 layout.html 的内容,核心在于
{{template "content" .}}
,表示具體内容模闆需要定義 content,是以看看 index.html 檔案:
運作後打開浏覽器通路 http://localhost:2020 :
接下來看看如何處理多個 layout 的情況。
因為 Render 的簽名是固定的,不同的 layout 通過什麼方式告知 Render 呢?觀察 Render 方法的參數:
string, data
可以在 data 和 ctx 上下功夫:
- 将 data 指定為 map[string]interface{},layout 通過 data 傳遞;
- 通過 ctx 的 Set 方法設定 layout,方法内通過 ctx.Get 擷取 layout;
先看第 1 種方式:
// NoNavRender 沒有導航的 layout html 輸出
在 render 包中增加了一個 NoVaRender 函數,該函數要求 data 必須是 map[string]interface{},這樣就可以做到将 layout 傳遞給 Render 方法,不過因為 Render 方法的 data 參數是 interface{} 類型,是以得做類型斷言。
"layout.html"
看看第 2 種方式如何實作:
// NoNavRender 沒有導航的 layout html 輸出
在 Render 中擷取 layout 的值:
"layout.html"
兩種方式個人覺得第 2 種更優雅。不過需要注意的是,兩種方式要注意 layout 不能沖突,也就是不能他用。
另外,我個人建議,data 參數永遠要麼傳遞 nil,要麼傳遞 map[string]interface{} 。個人感覺 Echo 的 Render 方法 data 參數的類型不應該用 interface{} 而是用 map[string]interface{},這樣可以更友善地往 data 中加入更多全局的資料。在簡書項目中,我們會通過其他方式彌補這個問題。
小結
通過本節,你應該掌握了 Render 的使用、內建和大項目 layout 的處理。
額外提一句,因為 Context.Render 方法最終是調用的 Context.HTML 方法進行渲染,是以我們也完全可以抛棄 Render 方法,而是使用自己的 Render。目前簡書的代碼(後續會改掉)和 studygolang 的源碼采用的就是完全抛棄 Context.Render 的方式,主要考慮還是有一些 Render 不能很好滿足的地方,比如上面說的多 layout、data 類型等,不過也是可以解決的。是以還是建議采用 Echo 架構的 Render。
本節完整代碼點這裡: https://github.com/polaris1119/go-echo-example/tree/0cd46e8b1f38317439e95d55e3fe29a173a2e3c1。
覺得不錯,歡迎關注:
點個贊、在看和轉發是最大的支援