【webpack系列】從零搭建 webpack4+react 腳手架(二)
在上一節我們已經搭建了一個最基本的webpack環境, 這一節我們帶著上節的一些疑問,繼續優化我們的react工程。 1.整合html-webpack-plugin public的index.html應該自動編譯到dist目錄,並且所有的js引用是自動新增的。你可以使用html-webpack-plugin外掛來處理這個優化。html檔案如何也同步到dist目錄?bundle.js檔案修改了,萬一被瀏覽器快取了怎麼辦?如何為匯出的檔案加md5?如何把js引用自動新增到html?非業務程式碼和業務程式碼如何分開打包?如何搭建開發環境?如何實現開發環境的熱更新?
(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相關的技術棧進行整合。