Webpack in Node:前端模板開發工具TDS

SP-Lyu發表於2018-10-16

簡介

一個適合小頁面的模板開發工具,基於webpack,支援熱過載,將css、js打包到一個html模板檔案中。
這個小工具的適用場景不廣,但設計思路能帶來不小的啟發。

具體可移步:github.com/SP-Lyu/TDS
* 單純當作工作小總結寫了,其實可以拆出很多細小但有用的文章

廣告模板工具

TDS其實是為了一些小型的廣告模板服務的,當年接手這一塊只能手動生產這些模板,開發維護起來特別麻煩 (沒錯,本人就是靠發小廣告為生)

<!-- 常見的廣告模板 -->
<!-- Head -->
<style>
  /* Style Sheet */
</style>
<div id="{{ADID}}" class="wrapper">
  <img src="{{IMG}}" />
  <img src="{{IMG}}" />
  <img src="{{IMG}}" />
  <div><a href="{{CLICK_URL}}">{{DESC}}</a></div>
  <div class="logo">
    <!-- LOGO logic -->
  </div>
  <script>
  // monitor
  // animate logic
  var id = {{ADID}};
  var conf = {
    showtime: {{TIME}}
    // ...
  }
  // ...
  </script>
</div>
<!-- Tail -->
複製程式碼

在一些搜尋場景或者網盟場景下面的廣告前端邏輯,往往具備以下幾個特點:

  • 對展現及載入速度要求高
  • 頁面簡單、互動邏輯較少,但公共元件多
  • 迭代速度快,新模板往往能刺激點選率的提升
  • 後端會維護一套模板填入物料,聯調時前後端相互耦合阻塞

所以對於商業廣告展現的前端開發,有這幾點需要關注:

  • 優化展現速度,去除不必要的請求
  • 元件化開發,批量打包更新
  • 前端需要維護一套模擬資料便於開發、測試

著手優化

根據上述的訴求點,最終產出了一個模板開發工具,以命令列的形式完成模板的開發環境初始化、開發、打包、測試等。
命令大全:

tpl -s 切換至不同的業務線  
tpl -l 檢視當前業務線中的模板  
tpl -i <tpl_name> 初始化新模板  
tpl -d <tpl_name> -p <port> [-q] 開發  
tpl -b <tpl_name> [-q] [-u] [-c charset] 打包模板(-u:是否不壓縮HTML檔案 -c:轉換至目標編碼)  
tpl -B [-q] [-u] [-c charset] 打包當前業務線中所有模板  
tpl --delete 刪除模板  
複製程式碼

下面介紹一下這個工具結合實際應用解決的幾個痛點:

展現及載入速度優化

通常的頁面開發,都是前端只保留一個簡單的html,通過CDN、靜態檔案、快取等方式引入CSS與JS檔案,但該方式並不完全適用於廣告展現的應用場景。 廣告頁面 互動少,邏輯簡單 ,即使將css、js程式碼完全算上,亦不過15K左右大小,按照1MB/s的下載速度,傳輸僅需要15ms即可完成,而一般花在請求上的TTFB時間已大大超過這個值了。所以最耗時的不是資源下載,而是請求本身
所以此處的優化思路應該是:css與js以行內引入的方式打包進模板,減少資源請求數,達到展現速度最快的目的。
TDS中採用了以ejs為模板,將打包好的css與js以字串的形式通過webpack引入模板,達到行內引入的目的。 webpack配置:

{
    // ...
    plagins:[
        new MiniCssExtractPlugin({
            filename: "main.css",
        }),
        new HtmlWebpackPlugin({
            files:{
                "css":[`./main.css`],
                "js":[`./main.js`]
            },
            filename: `test.tpl`,
            inject: false,
            template: `test.ejs`,
            title: tpl
        }),
        new OptimizeCSSAssetsPlugin({})
    ]
    // ...
}
複製程式碼

ejs模板引入:

CSS:
<style type="text/css"><%= compilation.assets[htmlWebpackPlugin.files.css[0]].source() %></style>
JS:
<script><%= compilation.assets[htmlWebpackPlugin.files.js[0]].source() %></script>
複製程式碼

元件化開發

元件化的過程中,要考慮到 模組可複用 以及 業務間的模組獨立

模組可複用

因為一些動畫邏輯(抽獎、彈窗、輪播等)在多套模板中是公用的,且隨著時間推移,這些邏輯的批量更改的需求若處理不好,會徒增很多開發量。就需要webpack配合上一定的指令碼,進行批量打包。
在TDS中,通過Node引入webpack進行打包,並通過commander.js,將Node程式命令化,從而達到批量打包的目的。
可以移步packer.js中看到詳細的配置引入。
在開發的過程中,由於想要把熱過載也加入TDS工具中,調研了一下現有的幾種方法,但最終發現,可以直接在Node環境下引入webpack-dev-server啟動熱過載。以下是示例:

const webpack = require("webpack");
const WebpackDevServer = require("webpack-dev-server");

const compiler = webpack({
    //webpack conf
});

const s = new WebpackDevServer(,{
    quiet: false,
    contentBase: './'
});

s.listen(8808, '0.0.0.0', function(){});
複製程式碼

這種方式的引入,比用webpack-dev-middleware + webpack-hot-middleware簡單多了(但不知道為啥官方把它藏得那麼深,可能是因為應用場景少吧)
官方例子

業務間獨立

不同業務需求會存在多個模板,這裡還得考慮一下業務獨立的問題,能更好地將TDS應用於多業務線開發。由於運用了commander.js將TDS命令化,可以進行很多定製,例如將開發區塊以業務線進行區分,加入了workspace的概念,可以執行tpl -s切換工作區間,且之後的一切操作(新增、刪除、打包模板等),都是基於當前工作區間完成的。
每個業務會有自己的初始化模板,存放至templage_xxx資料夾中,新增之後的模板檔案放在src/xxx/下,打包生成的模板則放在out/xxx/下,這樣能保證每個業務相互獨立不干擾。 這個實現起來也十分簡單,建立一個.user_config檔案記錄下當前使用者所處的業務線,以此作為工作區間進行模板配置的讀取、操作即可。

// .user_config
{
    current_workspace: 'buns'
}
複製程式碼

Mock資料構造

一開始開發維護過程中遇到的最蛋疼的問題就是,前端對於這種模板檔案需要自己再去將值回填才能進行除錯,對於前期的相容性、互動、樣式等的測試十分不友好。TDS維護了一套簡單的測試方法:使用HtmlWebpackPlugin打包ejs模板的時候,配置當前的打包選項,可以區分出當前的開發環境以及需要用到的mock資料:

// webpack配置
new HtmlWebpackPlugin({
    files:{
        "css":['out/.tmp/main.css'],
        "js":['out/.tmp/main.js'],
    },
    // ↓當前環境置為開發環境
    dev: true,      
    // ↓將檔案以字串陣列的方式,寫入mock中
    mocks: get_files(`${tpl_path}/mocks/`),
    // ↓將檔案以字串陣列的方式,寫入mock中
    gmocks: get_files(`${tpl_path}/../Gmocks/`),
    filename: tpl + '.html',
    inject: false,
    template: tpl_path + '/' + tpl + '.ejs',
    name: tpl,
    workspace
})
複製程式碼

資料來源直接可以通過ejs檔案中的htmlWebpackPlugin.options.dev選項區分。

<% const ejs_env = htmlWebpackPlugin.options; %>
<% /*公共頭部*/ %>
<%= ejs_env.dev?ejs_env.gmocks['head.html'] : '' %>
<div id="current_show"></div>
<script>
<% if(ejs_env.dev){ %>
window.__g_ad_data = {
    title: '測試資料',
    img: './src/<%= ejs_env.workspace%>/<%= ejs_env.name%>/static/test.png',
    url: 'https://google.com'
};
<% } else {%>
    <!-- 後端填寫資料模組,以Google Ctemplate為例 -->
    window.__g_ad_data = {
        title: '{{TITLE}}',
        img: '{{IMG}}',
        url: '{{URL}}'
    };
<%}%>
</script>
<script>// handle window.__g_ad_data</script>
<% /*公共尾部*/ %>
<%= ejs_env.dev ? ejs_env.gmocks['tail.html'] : '' %>
複製程式碼

這樣就能在自己開發過程中維護一套有效的mock資料,打包專案程式碼時,直接通過環境的判斷就能達到將後端模板欄位打包的目的。
最終打包生成的模板:

<div id="current_show"></div>
<script>
    <!-- 後端填寫資料模組,以Google Ctemplate為例 -->
    window.__g_ad_data = {
        title: '{{TITLE}}',
        img: '{{IMG}}',
        url: '{{URL}}'
    };
</script>
<script>// handle window.__g_ad_data</script>
複製程式碼

產出

最後的整體產出,由於加上了js、css、html的打包邏輯,對比之前的模板體積大約下降了30%左右,且去除了css、js的載入邏輯,頁面的整體載入時間(不算圖片)接近於一次http請求的時間。
且對於開發人員來說,前後端的開發可以徹底分離,不再需要繁瑣的溝通成本。

資料

commander.js
webpack NodeApi
webpack-dev-server NodeApi

* 有問題歡迎留言交流

相關文章