1. 程式人生 > >【webpack系列】從零搭建 webpack4+react 腳手架(二)

【webpack系列】從零搭建 webpack4+react 腳手架(二)

html檔案如何也同步到dist目錄?bundle.js檔案修改了,萬一被瀏覽器快取了怎麼辦?如何為匯出的檔案加md5?如何把js引用自動新增到html?非業務程式碼和業務程式碼如何分開打包?如何搭建開發環境?如何實現開發環境的熱更新?

在上一節我們已經搭建了一個最基本的webpack環境, 這一節我們帶著上節的一些疑問,繼續優化我們的react工程。   1.整合html-webpack-plugin   public的index.html應該自動編譯到dist目錄,並且所有的js引用是自動新增的。你可以使用html-webpack-plugin外掛來處理這個優化。
(1)安裝html-webpack-plugin:
npm install --save-dev html-webpack-plugin
(2)在webpack.prod.conf.js中配置plugins屬性。
    const merge = require('webpack-merge');
    const baseWebpackConfig = require('./webpack.base.conf.js');
    const HtmlWebpackPlugin = require('html-webpack-plugin');

    module.exports = merge(baseWebpackConfig, {
        mode: 
'production', plugins: [ new HtmlWebpackPlugin({ template: 'public/index.html', inject: 'body', minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes:
true }, }) ] });
(3)刪除index.html中手動引入的script標籤

index.html的程式碼應該是這樣的:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>從零開始搭建react工程</title>
    </head>
    <body>
    <div id="root"></div>
    </body>
    </html>
(4)重新執行編譯
npm run build

檢視dist資料夾,index.html也被載入進來了,並且已經自動加上了script標籤。

 

2.為匯出的js檔案新增內容hash   當我們的業務有修改,bundle被重新打包,很可能在客戶的電腦上並沒有奏效,你告訴客戶,應該是被快取了,需要重新整理瀏覽器,清理下瀏覽器快取。這也許能解決問題,但是太糟糕了,我們有更好的方式,讓匯出的js檔案加上檔案hash,從而每次修改,轉譯出的js檔名稱都不相同,那麼js檔案當然不會被快取了。
新增檔案hash的方法很簡單,只要修改 output.filename 屬性即可,這裡我們做一個小小的優化,把匯出的檔案存放在js目錄下,並且直接使用name+chunkhash的方式來命名。
filename: "js/[name].[chunkhash:16].js"
其中,name就是模組名稱,我們在entry中進行過配置,chunkhash是檔案內容的hash,webpack預設採用md5的方式對檔案進行hash。16是hash的長度,如果不設定,webpack會設定預設值為20。
現在,你的webpack.prod.conf.js檔案看起來應該是這樣:
    const merge = require('webpack-merge');
    const baseWebpackConfig = require('./webpack.base.conf.js');
    const HtmlWebpackPlugin = require('html-webpack-plugin');

    module.exports = merge(baseWebpackConfig, {
        mode: 'production',
        output: {
            filename: "js/[name].[chunkhash:16].js",
        },
        plugins: [
            new HtmlWebpackPlugin({
                template: 'public/index.html',
                inject: 'body',
                 minify: {
                    removeComments: true,
                    collapseWhitespace: true,
                    removeAttributeQuotes: true
                },
            })
        ]
    });

 

3.編譯前清理dist目錄   現在,如果你修改了你的業務程式碼,然後重新編譯,你會發現在dist/js資料夾出現多個js檔案。因為匯出的js檔案hash已經不相同,每次編譯都會增加新的js檔案,原來的檔案沒有被刪除。所以,我們需要在編譯前進行清理dist資料夾。
(1)安裝clean-webpack-plugin
npm install --save-dev clean-webpack-plugin
(2)修改webpack.prod.conf.js,使用clean-webpack-plugin
    const merge = require('webpack-merge');
    const baseWebpackConfig = require('./webpack.base.js');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const CleanWebpackPlugin = require('clean-webpack-plugin');

    module.exports = merge(baseWebpackConfig, {
        mode: 'production',
        output: {
            filename: "js/[name].[chunkhash:16].js",
        },
        plugins: [
            new HtmlWebpackPlugin({
                template: 'public/index.html',
                inject: 'body',
                minify: {
                    removeComments: true,
                    collapseWhitespace: true,
                    removeAttributeQuotes: true
                },
            }),
            new CleanWebpackPlugin(['../dist'], { allowExternal: true })
        ]
    });
(3)執行試試看
npm run build

編譯過程,注意檢視控制檯輸出,你會發現webpack刪除了dist目錄。

 

4.非業務程式碼單獨打包   在build結束,webpack會在終端顯示打包檔案的大小,我們可以看到這個app.js包大概在96.9KB

 

隨著我們業務程式碼的增加,這個包將會越來越大。
你每次釋出,這個檔案都會被重新下載。你的程式碼有修改,使用者需要重新下載無可厚非。可是,你別忘了這個app.js內還包含了很多不變的程式碼,比如react,react-dom。我們需要把這些不變的程式碼分開打包。
在webpack.base.conf.js,我們新增一個入口配置。entry有2個入口。

    entry: {
            app: './app/index.js',
            framework:['react','react-dom'],
        },

重新執行npm run build,再看看。

的確,react和react-dom 被編譯成framework.js。可是,你會發現,app.js並沒有減少,還是96.9KB。因為我們還缺少一步,就是抽離app.js中公共的程式碼。
webpack3版本是通過配置CommonsChunkPlugin外掛來抽離公共的模組。webpack4版本,官方廢棄了CommonsChunkPlugin,而是改用配置optimization.splitChunks的方式,更加方便。
在webpack.prod.conf.js增加如下程式碼:

    optimization: {
            splitChunks: {
                chunks: "all",
                minChunks: 1,
                minSize: 0,
                cacheGroups: {
                    framework: {
                        test: "framework",
                        name: "framework",
                        enforce: true
                    }
                }
            }
        }

cacheGroups物件,定義了需要被抽離的模組,其中test屬性是比較關鍵的一個值,他可以是一個字串,也可以是正則表示式,還可以是函式。如果定義的是字串,會匹配入口模組名稱,會從其他模組中把包含這個模組的抽離出來。name是抽離後生成的名字,和入口檔案模組名稱相同,這樣抽離出來的新生成的framework模組會覆蓋被抽離的framework模組,雖然他們都叫framework。
重新執行npm run build你看到app.js的體積變小了 才1kb。

注意檢視生成的檔案的hash,接下去我們隨意修改app/index.js的程式碼。重新執行npm run build編譯。看看編譯後的結果:

看到了嗎,app的hash發生了改變(它不能被瀏覽器快取),而framework沒有改變(它會被瀏覽器快取),這達到了我們預期的結果。

5.壓縮js檔案   開啟build後的js檔案看看,js檔案沒有被壓縮。在prod環境,我們希望js已經被壓縮了,這樣做的好處是減少檔案體積,更快地被使用者載入。js壓縮,用到了uglifyjs-webpack-plugin,在optimization內進行配置。
(1)安裝uglifyjs-webpack-plugin
npm install --save-dev uglifyjs-webpack-plugin
(2)在webpack.prod.conf.js頁面上引入
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
(3)optimization內配置minimizer引數
    minimizer: [
        new UglifyJSPlugin()
    ],

你的optimization引數現在應該是這樣:

     optimization: {
            minimizer: [
                new UglifyJSPlugin()
            ],
            splitChunks: {
                chunks: "all",
                minChunks: 1,
                cacheGroups: {
                    framework: {
                        priority: 200,
                        test: "framework",
                        name: "framework",
                        enforce: true,
                        reuseExistingChunk: true
                    },
                    vendor: {
                        priority: 10,
                        test: /node_modules/,
                        name: "vendor",
                        enforce: true,
                        reuseExistingChunk: true
                    }
                }
            }
        }
(4)重新執行npm run build
npm run build

 

6.整合dev環境   我們不可能每次修改程式碼都去手動編譯,等編譯出來再去開啟檔案檢視效果。webpack提供了開發環境服務,並且支援熱更新,這在開發環境是非常有必要的。
webpack-dev-server這個模組提供了開發服務的支援,通過在webpack.dev.conf.js檔案配置devServer可以方便地整合webpack-dev-server。
(1)安裝webpack-dev-server
npm install --save-dev webpack-dev-server
(2)在build中新增webpack.dev.conf.js檔案
    const path = require('path');
    const merge = require('webpack-merge');
    const baseWebpackConfig = require('./webpack.base.conf.js');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const webpack = require('webpack');

    module.exports = merge(baseWebpackConfig, {
        mode: 'development',
        output: {
            filename: "js/[name].[hash:16].js",
        },
        plugins: [
            new HtmlWebpackPlugin({
                template: 'public/index.html',
                inject: 'body',
                minify: {
                    html5: true
                },
                hash: false
            }),
            new webpack.HotModuleReplacementPlugin()
        ],
        devServer: {
            port: '8080',
            contentBase: path.join(__dirname, '../public'),
            compress: true,
            historyApiFallback: true,
            hot: true,
            https: false,
            noInfo: true,
            open: true,
            proxy: {}
        }
    });

HotModuleReplacementPlugin是webpack熱更新的外掛,設定devServer.hot為true,並且在plugins中引入HotModuleReplacementPlugin外掛即可。
還需要注意的是我們開啟了hot,那麼匯出不能使用chunkhash,需要替換為hash。

(3)在package.json增加一個npm scripts
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
(4)執行dev命令
npm run dev

開啟 http://localhost:8080 檢視,你可以嘗試改動index.js的程式碼,瀏覽器自動更新了,說明整合webpack-dev-server成功。
你可能注意到,對於css相關的技術棧,我只字未提,別急,下一節我們會詳細針對css相關的技術棧進行整合。