利用 webpack 處理開發與線上環境靜態資源切換問題

前言

webpack,做爲一個處理模塊加載、資源依賴管理、構建化的工具,已經逐漸成爲了前端工程化領域的新貴。其創造性的把每一個靜態資源歸爲一個 module(模塊)並能被其強大的 loader 所加載的這種方式,成功的開闢了前端工程界的另外一大生態。基於其官網文檔的完善度較高,這篇文章就不對 webpack 的主要內容作過多的介紹,而是迴歸到本文的主題,即經過介紹幾款 webpack 相關的插件,來解決一個常見的工程問題:如何作到靜態資源路徑能夠在不一樣的環境下自動切換。javascript

問題說明

這究竟是個怎樣的問題?設想一下,在使用 webpack 打包編譯以後,它會生成一個 js 文件,隨後咱們須要在 html 或者模板文件裏指定這個文件的路徑確保其被正確的引入,css

<script type="javascript" src="app.js"></script>

對於開發環境下的單入口文件(稍後會介紹 webpack 打包到多個入口的解決方案 ),這個標籤內的引入文件路徑徹底能夠寫死,並且在 webpack-dev-server 熱替換機制的幫助下,咱們也無須經過對打包生成的文件添加 hash 值來處理因瀏覽器緩存的緣故引發的引用不到最新資源。html

但在產品模式下,咱們很是有必要在 webpack 的 output 屬性裏的 filename 裏配置一個 chunkhash 來變向的爲靜態資源注入版本號,以下,前端

output: {
 filename: [name].[chunkhash].js,
}

以便上線以後頁面能夠引入版本更新後的代碼。chunkhash 是一個基於文件內容,經過摘要算法(如md5)生成的一個被稱之爲文件指紋的序號,即只有當文件內容發生改變的時候,這個值纔會相應更改。vue

經過給靜態資源注入 hash 值來做爲版本號的好處主要有兩個:java

  1. 實現 long term caching 策略。當發佈新版本時,咱們只須要更新更改了的資源。這比起將新版資源存放在例如/v1.3/xx.js這種帶版本號的路徑或文件夾下的部署方式會顯得更科學一點:減小手動配置版本號的額外操做、已經緩存過且緩存還沒有過時的瀏覽器只需請求更新過的資源,確保未變動過的資源能夠依舊從緩存內讀取。node

  2. 實現非覆蓋式發佈策略。張雲龍老師的原文中提到的這種平滑的版本升級方式更加完美的解決了靜態資源部署至CDN出現的問題。react

這個時候咱們再來看下線上的 script 引入,webpack

<script type="javascript" src="http://xxx.cdn.com/app.82076244596568c8c929.js
"></script>

Fine, 也許你會說我能夠手動 copy/paste 這個版本號當你須要從開發切到產品環境,額額,單個入口文件這麼處理雖是能夠,但想象下當有多個入口文件的時候。。。(感受個人左手大拇指肌腱炎又要犯了。。),這麼經典的問題webpack早已準備好了它的解決方案。git

從 webpack 的編譯數據裏獲取開發與生產的資源路徑對應關係

這一部分的工做能夠說是解決這個問題的一個核心環節,即咱們須要經過 webpack 來生成相似以下一張對應關係圖:

{
    'app.js': 'http://xxx.cdn.com/app.82076244596568c8c929.js'
}

像在 webpack 的 plugin 屬性裏配置以下,咱們就能夠經過返回 webpack 的編譯數據裏獲取到帶有 chunkhash 的文件信息:

// webpack.config.js
module.exports = {
  ...
  plugins: [
    function() {
      this.plugin("done", function(stats) {
        require("fs").writeFileSync(
          path.join(__dirname, "..", "stats.json"),
          JSON.stringify(stats.toJson()));
      });
    }
  ]
}

stats.json require 到項目中,經過讀取 publicPathassetsByChunkName 屬性,能夠獲得開發與線上環境資源路徑的對應關係。

webpack 官方也推薦了幾個有一樣效果,我我的以爲更好用的插件:assets-webpack-plugin 或者 webpack-manifest-plugin 來生成出一個 JSON 對應關係文件。

切換資源路徑

接下來的工做基本上就是如何利用這個對應關係來切換對應環境下的路徑。這個還要取決於你的頁面是否會涉及到服務端的渲染。

服務端渲染資源路徑

以 node 做爲服務端語言,handlebars(或者ejs)爲模板語言爲例,咱們經過編寫模板語言的 helper 來讀取由 assets-webpack-plugin 生成的 stats.json,在不一樣的環境下實現路徑切換:

stats.json -- webpack 跑開發配置

{
    "app": {
        "js": "app.js"
    }
}

stats.json -- webpack 跑生產配置

{
    "app": {
        "js": "http://xxx.cdn.com/app.82076244596568c8c929.js"
    }
}

example.handlebars

<script type="text/javascript" src="{{app.js}}"></script>

後臺經過 require stats.json 數據並傳入到模板便可實現根據環境動態渲染資源路徑。

若是你的後臺是使用 Rails 來搭建的話,那麼這篇文章更詳細的介紹了處理這種狀況下處理資源切換的問題

前端渲染頁面模板

若是你的項目不依賴任何後端渲染,那麼 html-webpack-plugin 這款插件能夠爲你動態生成一個帶有 css、js 等資源路徑的 html 文件。

html-webpack-plugin 具體的用法能夠點擊這裏,其中 inject 這個屬性可讓你將 script 標籤插入到 dom 的指定位置。爲了可以更大權限的將 webpack 編譯過的資源能夠插入到 html 文件的任意位置,咱們能夠在 HtmlWebpackPlugin 裏指定的 template 文件裏寫入以下代碼:

<% for (var css in htmlWebpackPlugin.files.css) { %>
  <link href="<%= htmlWebpackPlugin.files.css[css] %>" rel="stylesheet">
  <% } %>
<% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
<script src="<%= htmlWebpackPlugin.files.chunks[chunk].entry %>"></script>
<% } %>

就能夠一樣實現靜態資源的切換,因此對於前端渲染模板的這種狀況,咱們無須再生成一個 json 文件,對於使用諸如 react、vue 這種框架,僅使用這個插件也是極好的。

htmlWebpackPlugin 具體還有哪些屬性能夠配置,能夠參考下這個 default template 查看完整例子

相關文章
相關標籤/搜索