基本說明(使用過exress和handlebars的可以略過):express中的handlebars引擎是這麼生成頁面的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
/* layout.hbs * 主模板,所有的的頁面都將替換"{{{body}}}","{{}}"相當於佔位符,由資料進行替換 */ <!DOCTYPE html> <html> <head> <title>{{title}}</title> </head> <body> {{{body}}} </body> </html> /* index.hbs * 單個頁面模板,這裡以首頁為例。"{{>}}"表示引用其他模板來替換,這裡引用名為"partial"的模板 */ <div>index</div> {{>partial}} /* partial.hbs * 一個分頁檔案,被其他模板引用,分頁之間也可以互相引用。 */ <div>123</div> /* index.html * 當瀏覽器請求index.html時,經過handlebars模板引擎處理後生成的頁面 */ <!DOCTYPE html> <html> <head> <title></title> </head> <body> <div>index</div> <div>123</div> </body> </html> |
實現步驟1:模板
react的火熱讓“元件化”的概念持續升溫,不過元件化確實在開發中提供了高可複用的程式碼,大大減少了工作量和bug,確實值得提倡。舉個例子。
1 2 3 4 5 |
<ul class="titles border" id="navigator"> <li class="title">標題1</li> <li class="title">標題2</li> <li class="title">標題3</li> </ul> |
這一段html程式碼,會在幾個頁面用到,如果按照一般的做法把這段程式碼ctrl-c、ctrl-v到要用的頁面。就會出現兩個問題:1.重複程式碼增多(ctrl-c、ctrl-v應該是程式設計師的大忌),當然這不是最重要的,最重要的是第2點——維護性差。如果現在我要把“標題1”改成“標題0”,那麼只能進行全量搜尋然後替換,不僅操作麻煩而且容易出錯。 如果用到了模板技術的話這個問題就很好解決,把上面那一段程式碼寫成一個模板,在handlebars中我們成為分頁,然後需要這段的程式碼的頁面引用這個分頁,如果要修改的話直接修改分頁了。以handlebars為例:
1 2 3 4 5 6 7 8 9 |
//navigator.hbs <ul class="titles border" id="navigator"> <li class="title">標題1</li> <li class="title">標題2</li> <li class="title">標題3</li> </ul> //在index.hbs中引用 {{>navigator}} |
為什麼handlebars?本文所用的後端模板引擎都以handlebars為例,原因是上次聽去哪兒前端團隊做的關於node.js的技術分享,炫耀了一個自己基於handlebars實現的小功能:分頁中引用的css檔案可以全部放到head中。心中一直覬覦這個小功能,直到最近和“元件化”的概念結合在一起考慮,發現這個功能對於實現後端的元件化很有幫助。自己對handlebars也略有研究,所以試著用handlebars來實現一下“元件化”。
實現步驟2:打包
這樣就完美了麼?no~no~no~ 上面的這一段html程式碼中可是有樣式的,按照w3c的規範,樣式應該寫在css檔案中,怎麼實現?自然而然想到兩種解決辦法:
- 在分頁中加入link標籤來引入所需的樣式,想一想html程式碼中到處穿插link標籤是什麼感覺~且不說生成頁面難以維護,瀏覽器渲染速度也會受影響。
- 把分頁所需的樣式放在公共的樣式檔案中,這是目前我們專案的通用做法,純粹的懶人策略,缺點很明顯,很多頁面引用了一些無用的樣式,浪費網路頻寬,尤其當專案變大時這個缺點將更加明顯。 所以最好的解決方法是按需載入,只載入引用元件所需的樣式,當然樣式檔案按分頁拆分得這麼細的話會增加請求數,影響不會太大,如果想優化的話也可以壓縮合併成一個請求,這個後面再說。 handlerbas中常見的擴充套件方式就是編寫helper,我們可以編寫一個helper,
1 2 3 4 5 6 |
//app.js hbs.registerHelper('css', function(str, option) { //在上下文中建立一個陣列用來儲存該頁面需要用到的css檔案 this.cssList = this.cssList || []; this.cssList.push(str); }); |
這個helper的作用就是註冊一個名為”css”的helper,幫我們儲存分頁中用到的css檔案地址。然後我們在主模板layout的head標籤部分遍歷cssList陣列迴圈載入出來。
1 2 3 4 5 6 7 8 9 |
//layout.hbs <head> <title>{{title}}</title> {{#each cssList}} <link rel="stylesheet" href="{{this}}" media="screen" title="no title" charset="utf-8"> {{/each}} </head> ... |
同時原來的分頁改成
1 2 3 4 5 6 7 |
//navigator.hbs {{css '/stylesheets/components/navigator.css'}} <ul class="titles border" id="navigator"> <li class="title">標題1</li> <li class="title">標題2</li> <li class="title">標題3</li> </ul> |
上面寫的只是一個簡單的無邏輯的靜態元件,有些元件可能會有互動效果,比如處理一些點選事件或者對外暴露可操作的介面等,那麼就需要js邏輯來實現了。
實現步驟3:邏輯
其實實現原理也大同小異,也是先註冊一個helper,然後在主模板layout中新增,這裡我們在原來的分頁中引入一個js檔案。具體程式碼如下:
1 2 3 4 5 |
//app.js hbs.registerHelper('js', function(str, option) { this.jsList = this.jsList || []; this.jsList.push(str); }); |
1 2 3 4 5 6 7 8 |
//layout.hbs ... <body> {{{body}}} {{#each jsList}} <script src="{{this}}" charset="utf-8"></script> {{/each}} </body> |
1 2 3 4 5 6 7 8 |
//navigator.hbs {{css '/stylesheets/components/navigator.css'}} {{js '/javascripts/components/navigator.js'}} <ul class="titles border" id="navigator"> <li class="title">標題1</li> <li class="title">標題2</li> <li class="title">標題3</li> </ul> |
現在已經實現將css、js、html封裝成獨立的元件了,不過這樣還是有個問題。如果a元件引用了public.css和a.css檔案,而b元件引用了public.css和b.css檔案,那麼按照上面的做法,會在head寫兩個同樣的link標籤,元件共同依賴的檔案越多,重複的標籤就越多。這當然不是我們所想看到的。
實現步驟4:依賴
為了解決這個問題我們還需要去重複,對剛才的兩個helper改造一下即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//app.js hbs.registerHelper('css', function(str, option) { var cssList = this.cssList || []; if(cssList.indexOf(str)<0) { cssList.push(str); } this.cssList = cssList.concat(); }); hbs.registerHelper('js', function(str, option) { var jsList = this.jsList || []; if(jsList.indexOf(str)<0) { jsList.push(str); } this.jsList = jsList.concat(); }); |
實現步驟5:合併
最後一步優化。為了減少請求,可以將不同元件所需的資原始檔進行合併。這裡推薦一個外掛loader。
以上就是關於後端利用模板實現元件化的探索,實現上雖無問題,但是由於目前沒有新的專案,無法在實際專案中使用,所以不知道會不會帶來其它的“坑”。歡迎有興趣的朋友使用後與我交流~
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式