深入淺出webpack學習(14)–為單頁應用生成HTML

fsrookie發表於2019-02-16

在事件專案中,一個頁面常常有很多資源要載入,舉出一個實戰中的例子要求如下:

  1. 專案採用ES6語言加react框架;
  2. 給頁面假如google analytics, 這部分程式碼需要內嵌進head標籤裡去。
  3. 給頁面假如Disqus使用者評論, 這部分程式碼需要非同步載入以提升首屏載入速度。
  4. 壓縮和分類JavaScript和css程式碼,提升載入速度。

以下是釋出到線上的程式碼 :

<html>
<head>
  <meta charset="UTF-8">
  <!--注入 Chunk app 依賴的 CSS-->
  <style rel="stylesheet">h1{color:red}</style>
  <!--內嵌 google_analytics 中的 JavaScript 程式碼-->
  <script>
(function(i,s,o,g,r,a,m){i[`GoogleAnalyticsObject`]=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,`script`,`https://www.google-analytics.com/analytics.js`,`ga`);
ga(`create`, `UA-XXXXX-Y`, `auto`);
ga(`send`, `pageview`);
  </script>
  <!--非同步載入 Disqus 評論-->
  <script async="" src="https://dive-into-webpack.disqus.com/embed.js"></script>
</head>
<body>
<div id="app"></div>
<!--匯入 app 依賴的 JS-->
<script src="app_746f32b2.js"></script>
<!--Disqus 評論容器-->
<div id="disqus_thread"></div>
</body>
</html>

構建出的目錄結構為:

dist
├── app_792b446e.js
└── index.html

可以看到部分程式碼被內嵌進入了HTML的head標籤中,部分檔案的檔名稱被打上根據檔案內容算出的Hash值,並且載入這些檔案的URL地址也被正常的注入到了HTML中。如果還是手寫HTML檔案去完成以上要求,這就會使工作變得複雜、易錯、專案難以維護。

解決方案

推薦一個用於方便的解決以上為的webpack外掛web-webpack-plugin
首先,修改webpack配置:

const path = require(`path`);
const UglifyJsPlugin = require(`webpack/lib/optimize/UglifyJsPlugin`);
const ExtractTextPlugin = require(`extract-text-webpack-plugin`);
const DefinePlugin = require(`webpack/lib/DefinePlugin`);
const { WebPlugin } = require(`web-webpack-plugin`);

module.exports = {
    entry: {
        app: `./main.js` //app的js入口檔案
    },
    output: {
        filename: `[name]_[chunkhash:8].js`給輸出的檔案加上hash值
        path: path.resolve(__dirname, `./dist`)
    },
    module: {
        rules: [
            {
                test: /.js$/,
                use: [`babel-loader`],
                //排除node_modules目錄下的檔案
                //該目錄下的檔案都是ES5語法,沒必要再通過babel去轉換
                exclude: path.resolve(__dirname, "node_modules")
            },
            {
                test: /.css/, //增加對css檔案的支援
                //提取出chunk中的css程式碼到單獨的檔案中
                use: ExtractTextPlugin.extract({
                    use: ["css-loader?minimize"] //壓縮css程式碼
                })
            }
        ]
    },
    plugins: [
        //使用本文的主角WebPlugin,一個WebPlugin對應一個HTML檔案
        new WebPlugin({
            template: "./template.html", //HTML模板檔案所在的檔案路徑
            filename: "index.html" //輸出HTML的檔名稱
        }),
        new ExtractTextPlugin({
            filename: `[name]_[contenthash:8].css`,//給輸出的css檔名稱加上hash值
        }),
        new DefinePlugin({
            //定義NODE_ENV環境變數為production,以去除原始碼中只有開發時才需要的部分
            "process.env": {
                NODE_ENV: JSON.stringify("production")
            }
        }),
        //壓縮輸出的JavaScript程式碼
        new UglifyJsPlugin({
            // 最緊湊的輸出
            beautify: false,
            // 刪除所有的註釋
            comments: false,
            compress: {
                // 在UglifyJs刪除沒有用到的程式碼時不輸出警告
                warnings: false,
                // 刪除所有的 `console` 語句,可以相容ie瀏覽器
                drop_console: true,
                // 內嵌定義了但是隻用到一次的變數
                collapse_vars: true,
                // 提取出出現多次但是沒有定義成變數去引用的靜態值
                reduce_vars: true,
              }
        })
    ]
}

以上配置中,大多數都是按照前面已經講過的內容增加的配置如

  1. 增加對css檔案的支援, 提取出chunk中的css程式碼到單獨的檔案中,壓縮css程式碼;
  2. 定義NODE_ENV環境變數為production,以去除原始碼中只有開發時才需要的部分;
  3. 給輸出的檔名稱加上hash值;
  4. 壓縮輸出的JavaScript帶啊。

相關文章