本文首發于Array_Huang的技術博客——
實用至上
,非經作者同意,請勿轉載。
原文地址:https://segmentfault.com/a/1190000008203380
如果您對本系列文章感興趣,歡迎關注訂閱這里:https://segmentfault.com/blog/array_huang
前言
近年來前端領域發展迅猛,前后端分離早已成為業界共識,各類管控系統(to B)上個SPA什么的也不值一提,但唯獨偏展示類的項目,為了SEO,始終還是需要依賴于服務器端渲染html。
我過往也曾嘗試為SPA彌補SEO,但現在看來,效果雖然達到了,但工作量也大大地增加(因為后端用的PHP,不能做到前后同構)。
雖然無法改變依賴服務器端渲染這一現實,但我們可以去勇敢地擁抱它,用前端的堅船利炮(aka webpack),把服務器端模板層給啃下來!
前導知識
兩個階段
整個前端項目,以本文主題的視角來看,可以分為兩個階段:
純靜態頁面開發階段
在這個階段里,一切開發都跟靜態網站無二致,按UI稿切好頁面搞好交互,要用到ajax請求API的也盡管寫,跟后端的協作點僅在于API文檔。
傳統前端的工作也就到這里為止了,但對我們來說,目前的成果并不是我們最終的交付;因此,注意了,在這個階段我們是可以“偷懶”的,比如說,一些明顯應該由服務器端循環生成的部分(商品列表、文章列表等),我們寫一遍就OK了。
動態頁面改造階段
這就是所謂的“套頁面”,傳統來說是由后端來做的,實際上后端也是苦不堪言,畢竟模板不是自己寫的,有時還是需要改造一番,而這正是我們前端要大力爭取的活。
在這個階段里,我們的主要工作是按照后端模板引擎的規則來撰寫模板變量占位符,當然這里面也不會少了循環輸出和邏輯判斷,另外也可能需要用到后端定義的一些函數,視項目需求而定。
在兩個階段里來回往返
這兩個階段不一定是完全獨立的,有需要的話也是可以做到來回往返的。
那什么時候才叫做“有需要”呢?舉個例子,當你把原先的靜態頁面都改造成需要后端渲染的頁面模板后,卻發現后端此時并未準備好相應的模板變量,而你此時又需要對頁面的UI部分進行修改,那么你就很被動了,因為改好的這些頁面模板根本跑不起來了。有兩種解決方案:
- 參考
API mock
的思路,來個模板變量 mock
,這就相當于一直留在動態頁面改造階段了。 - 回到純靜態頁面開發階段,讓頁面不需要后端渲染也能跑起來。具體怎么做呢?
改造開始
本文著重介紹如何將靜態頁面改造成后端渲染需要的模板。
配合后端模板命名規則生成相應模板文件
不同項目因應本身所使用的后端框架或是其它需求,對模板放置的目錄結構也會有所不一樣,那么,如何構建后端所需要的目錄結構呢?
在靜態網頁階段,我習慣把html/css/js都按照所屬頁面歸到各自的目錄中(公用的css/js也當然是放到公用目錄中),看HtmlWebpackPlugin配置:
pageArr.forEach((page) => {
const htmlPlugin = new HtmlWebpackPlugin({
filename: `${page}/index.html`, // page變量形如'product/index'、'product/detail'
template: path.resolve(dirVars.pagesDir, `./${page}/html.js`),
chunks: [page, 'commons/commons'],
hash: true,
xhtml: true,
});
pluginsConfig.push(htmlPlugin);
});
而在改造階段,則放到后端指定位置:
pageArr.forEach((page) => {
const htmlPlugin = new HtmlWebpackPlugin({
filename: `../../view/frontend/${page}.php`, // 通過控制相對路徑來確定模板的根目錄
template: path.resolve(dirVars.pagesDir, `./${page}/html.js`),
chunks: [page, 'commons/commons'],
hash: true,
xhtml: true,
});
pluginsConfig.push(htmlPlugin);
});
此時我模板目錄結構是這樣的:
│
├─alert
│ index.php
│
├─article
│ detail.php
│ index.php
│
├─index
│ index.php
│
├─product
│ detail.php
│ index.php
│
└─user
edit-password.php
modify-info.php
這里需要注意的是,我的前端項目目錄實際上是作為后端目錄里的一個子目錄來存放的,這樣才能依靠相對路徑來確定模板文件存放的根目錄位置。
處理站內鏈接
對于站內鏈接,我建議在前端模板里使用一個函數來適配兩個階段:
{
/* 拼接系統內部的URL */
constructInsideUrl(url, request, urlTail) {
urlTail = urlTail || '';
let finalUrl = config.PAGE_ROOT_PATH + url;
if (!config.IS_PRODUCTION_MODE) {
finalUrl += '/index.html' + urlTail;
return finalUrl;
}
return `<?php echo cf::constructInsideUrl(array('module' => '${url}'), $isStaticize)?>`;
},
};
在前端模板里這么用:
<a href="<%= constructInsideUrl('index/index') %>">
<img src="<%= require('./logo.png') %>">
</a>
這樣做,就能分別在靜態頁面階段和后端渲染階段生成相應的超鏈接。再者,在后端渲染階段,我們生成出來的也不一定是一個完整的url,可以像我上述代碼一樣,生成調用后端函數的模板代碼,從而靈活滿足后端的一些需求(比如說,我的項目有靜態化的需求,那么,靜態化后的站內鏈接跟動態渲染的又會有所不同了)。
處理模板變量
這一塊其實我要說的不多,無非就是按照后端模板引擎的規則,輸出變量、循環輸出變量、判斷條件輸出變量、調用后端(模板引擎)函數調整輸出變量。
關鍵是,我們需要拿到一份模板變量文檔,跟API文檔類似,它實際上也是一份前后端的數據協議。有了這份文檔,我們才能在后端未完工的情況下,進入動態頁面改造階段,并根據其中內容實現模板變量 mock
。
爭討模板布局渲染權
關于利用模板布局系統對多個頁面共有的部分實現復用,在之前的文章里已經提及了,我設計該系統的思路恰恰是來自于后端模板渲染。那么,在前后端均可以實現模板布局系統的前提下,我們應如何抉擇呢?我的答案是,前端一定要吃下來!
從前端的角度來看:
- 我們在純靜態頁面開發階段的產物就已經是一個個完整的頁面了,再要拆開并不現實。
- 由于在webpack的輔助下這套模板布局系統功能相當強大,因此并沒有給整個項目添加額外的成本。
從后端的角度來看:
- 服務器拼接多個HTML代碼段本身也是有成本(比如磁盤IO成本)的,倒不如渲染一個完整的頁面。
- 在公共組件的分治管理上不會有很大變化,只不過以前是一個一個組件渲染好后再拼在一起,而現在是把各個組件的數據整合在一起來統一渲染罷了。
總結
在后端渲染的項目里使用webpack多頁應用架構是絕對可行的,可不要給老頑固們嚇唬得又回到傳統前端架構了。
示例代碼
諸位看本系列文章,搭配我在Github上的腳手架項目食用更佳哦(笑):Array-Huang/webpack-seed(https://github.com/Array-Huang/webpack-seed
)。
附系列文章目錄(同步更新)
- webpack多頁應用架構系列(一):一步一步解決架構痛點
- webpack多頁應用架構系列(二):webpack配置常用部分有哪些?
- webpack多頁應用架構系列(三):怎么打包公共代碼才能避免重復?
- webpack多頁應用架構系列(四):老式jQuery插件還不能丟,怎么兼容?
- webpack多頁應用架構系列(五):聽說webpack連less/css也能打包?
- webpack多頁應用架構系列(六):聽說webpack連圖片和字體也能打包?
- webpack多頁應用架構系列(七):開發環境、生產環境傻傻分不清楚?
- webpack多頁應用架構系列(八):教練我要寫ES6!webpack怎么整合Babel?
- webpack多頁應用架構系列(九):總有刁民想害朕!ESLint為你阻擊垃圾代碼
- webpack多頁應用架構系列(十):如何打造一個自定義的bootstrap
- webpack多頁應用架構系列(十一):預打包Dll,實現webpack音速編譯
- webpack多頁應用架構系列(十二):利用webpack生成HTML普通網頁&頁面模板
- webpack多頁應用架構系列(十三):構建一個簡單的模板布局系統
- webpack多頁應用架構系列(十四):No復制粘貼!多項目共用基礎設施
- webpack多頁應用架構系列(十五):論前端如何在后端渲染開發模式下夾縫生存
本文首發于Array_Huang的技術博客——
實用至上
,非經作者同意,請勿轉載。
原文地址:https://segmentfault.com/a/1190000008203380
如果您對本系列文章感興趣,歡迎關注訂閱這里:https://segmentfault.com/blog/array_huang