從零搭建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.js中配置plugins屬性。
const merge = require(webpack-merge);const baseWebpackConfig = require(./webpack.base.js);const HtmlWebpackPlugin = require(html-webpack-plugin);module.exports = merge(baseWebpackConfig, { mode: production, plugins: [ new HtmlWebpackPlugin({ template: public/index.html, inject: body, minify: { html5: true }, hash: false }) ]});
(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.js文件看起來應該是這樣:
const merge = require(webpack-merge);const baseWebpackConfig = require(./webpack.base.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: { html5: true }, hash: false }) ]});
3.編譯前清理dist目錄
現在,如果你修改了你的業務代碼,然後重新編譯,你會發現在dist/js文件夾出現多個js文件。因為導出的js文件hash已經不相同,每次編譯都會增加新的js文件,原來的文件沒有被刪除。所以,我們需要在編譯前進行清理dist文件夾。
(1)安裝clean-webpack-plugin
npm install --save-dev clean-webpack-plugin
(2)修改webpack.prod.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: { html5: true }, hash: false }), 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.js,我們添加一個入口配置。entry有2個入口。
entry: { app: ./app/index.js, vendor:[react,react-dom], },
重新執行npm run build,再看看:
的確,react和react-dom 被編譯成vendor.js。可是,你會發現,app.js並沒有減少,還是96.9KB。因為我們還缺少一步,就是抽離app.js中公共的代碼。
webpack3版本是通過配置CommonsChunkPlugin插件來抽離公共的模塊。webpack4版本,官方廢棄了CommonsChunkPlugin,而是改用配置optimization.splitChunks的方式,更加方便。
在webpack.prod.js增加如下代碼:
optimization: { splitChunks: { chunks: "all", minChunks: 1, minSize: 0, cacheGroups: { vendor: { test: "vendor", name: "vendor" } } } }
cacheGroups對象,定義了需要被抽離的模塊,其中test屬性是比較關鍵的一個值,他可以是一個字元串,也可以是正則表達式,還可以是函數。如果定義的是字元串,會匹配入口模塊名稱,會從其他模塊中把包含這個模塊的抽離出來。name是抽離後生成的名字,和入口文件模塊名稱相同,這樣抽離出來的新生成的vendor模塊會覆蓋被抽離的vendor模塊,雖然他們都叫vendor。
重新執行npm run build你看到app.js的體積變小了 才1kb。
注意查看生成的文件的hash,接下去我們隨意修改app/index.js的代碼。重新執行npm run build編譯。看看編譯後的結果:
看到了嗎,app的hash發生了改變(它不能被瀏覽器緩存),而vendor沒有改變(它會被瀏覽器緩存),這達到了我們預期的結果。
5.整合dev環境
我們不可能每次修改代碼都去手動編譯,等編譯出來再去打開文件查看效果。webpack提供了開發環境服務,並且支持熱更新,這在開發環境是非常有必要的。
webpack-dev-server這個模塊提供了開發服務的支持,通過在webpack.config.js文件配置devServer可以方便地整合webpack-dev-server。
(1)安裝webpack-dev-server
npm install --save-dev webpack-dev-server
(2)在config中添加webpack.dev.js文件
const path = require(path);const merge = require(webpack-merge);const baseWebpackConfig = require(./webpack.base.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",
(4)執行dev命令
npm run dev
打開http://localhost:8080查看,你可以嘗試改動index.js的代碼,瀏覽器自動更新了,說明整合webpack-dev-server成功。
你可能注意到,對於css相關的技術棧,我隻字未提,別急,下一節我們會詳細針對css相關的技術棧進行整合。
推薦閱讀:
※下一代零配置打包工具 Parcel 初體驗
※Tree-Shaking性能優化實踐 - 原理篇
※webpack 源碼解讀(3)--CommonsChunkPlugin
※Start React with Webpack
※web性能優化-提高網頁載入速度