前言
我們接著上一篇文章react+webpack4.x搭建前端項目之配置抽取和區分環境來進行多頁面打包的配置
這里小編推薦一個福利,更多精彩內容請點擊鏈接,點擊這里
首先修改src下的代碼,項目的結構目錄
源碼見修改src下代碼,添加測試a,b模塊完整代碼0.0.2
開發環境多頁面的配置
新建build/module-entry.js
,獲取額外打包的模塊名,和入口entry配置
const glob = require("glob")
// 獲取各個模塊名稱
function getModuleList(){
const moduleList = glob.sync("././src/modules/*");
for(let index = 0 ; index < moduleList.length ; index++){
const item = moduleList[index];
const tmpList = item.split("/");
moduleList[index] = tmpList[tmpList.length-1];
}
return moduleList;
}
// 獲取webpack entry
function getBuildEntry(){
const moduleList = getModuleList();
let entry = {};
for(let index in moduleList){
const moduleName = moduleList[index]
entry[moduleName] = "./src/modules/" + moduleName + "/index.js"
}
// 額外添加./src/index的配置(把這個也當做一個頁面)
entry["app"] = "./src/index.js"
return entry
}
module.exports = {
getModuleList,
getBuildEntry,
};
然后我們修改webpack.dev.config.js
組裝htmlwebpackplugin
多頁面模板和輸出模板
const { getModuleList , getBuildEntry} = require("./module-entry")
// 獲取各個模塊
const moduleList = getModuleList();
const HtmlWebpackPluginList = [];
for(let index in moduleList){
const moduleName = moduleList[index]
HtmlWebpackPluginList.push(new HtmlWebpackPlugin({
filename: utils.resolve('./../dist/'+ moduleName+ '/index.html'), // html模板的生成路徑
template: utils.resolve("./../src/modules/" + moduleName + "/index.html"),//html模板
inject: true, // true:默認值,script標簽位于html文件的 body 底部
chunks: [moduleName], // 注入哪個名稱bundel
}))
}
最后把HtmlWebpackPluginList
放入plugins
屬性下,在plugins
數組最后加如下代碼
.concat(HtmlWebpackPluginList)
修改entry
屬性
entry: getBuildEntry(),
執行npm run dev
瀏覽器打開http://localhost:8081
,結果如下圖
發現怎么是b
頁面的內容?
那么是為什么呢?經過排查,打開控制臺選擇elements
看到如下圖
index.html
注入了所有bundle
。a
,b
模塊(頁面)的bundle
也注入到html
頁面中,b
模塊的bundle
在最后邊,把前邊的覆蓋了。這就是問題的根源。
這時候我們需要修改webapck.dev.config.js
默認HtmlWebpackPlugin
的實例的參數,新增chunks: ['app']
new HtmlWebpackPlugin({
filename: utils.resolve('./../dist/index.html'), // html模板的生成路徑
template: 'index.html',//html模板
chunks: ['app'], // 至注入app和app相關的bundle
inject: true, // true:默認值,script標簽位于html文件的 body 底部
}),
這時候重新運行成功解決!
然后我們打開http://localhost:8081/a
,http://localhost:8081/b
頁面正常顯示。
但是存在一個問題!點擊二級路由的時候也可以正常顯示,但是我們直接打開或者刷新二級路由的時候(http://localhost:8081/a/test
)時候發現頁面顯示成http://localhost:8081
這個頁面了
因為這里我們設置的webpack-dev-server
的historyApiFallback
是true,那么它就把所有的地址全部重定向到根目錄的index.html,也就會重定向到http://localhost:8081
打開的頁面了,然后在這下邊尋找路由進行匹配
怎么解決呢?
historyApiFallback
提供的有一個rewrites
屬性,可以使用如rewrites選項進一步控制此行為
詳細使用請看官方文檔
我們這里需要構建rewrites
內容
// 需要在開發環境重寫的規則數組
const rewrites = []; // webpack-dev-server的historyApiFallback中使用
for(let index in moduleList){
const moduleName = moduleList[index]
// 以模塊名開頭的路徑,重定向到 改模塊下的index.html模板文件 比如路徑一以/a開頭,會重定向到a模塊下的index.html
rewrites.push({
from:new RegExp('^\/' + moduleName),
to:utils.resolve('/' + moduleName +'/index.html')
})
}
最后修改historyApiFallback
historyApiFallback: {
rewrites: rewrites
},
重新運行npm run dev
,再次刷新http://localhost:8081/a/test
,頁面顯示正常。那么開發環境的多頁面配置已經完成嘍
打包環境多頁面的配置
和上邊的配置差不多,修改webapck.prod.config.js
,只是對于打包環境添加了壓縮代碼的配置項
const { getModuleList , getBuildEntry} = require("./module-entry")
// 獲取各個模塊
const moduleList = getModuleList();
const HtmlWebpackPluginList = [];
for(let index in moduleList){
const moduleName = moduleList[index]
HtmlWebpackPluginList.push(new HtmlWebpackPlugin({
filename: utils.resolve('./../dist/'+ moduleName+ '/index.html'), // html模板的生成路徑
template: utils.resolve("./../src/modules/" + moduleName + "/index.html"),//html模板
inject: true, // true:默認值,script標簽位于html文件的 body 底部
// html 文件進行壓縮
chunks: [moduleName,'vendors'], // 必須指定,不然會把多有打包的東西插入html模板當中
minify: {
removeComments: true, //去注釋
collapseWhitespace: true, //壓縮空格
removeAttributeQuotes: true //去除屬性引用
},
}))
}
最后把HtmlWebpackPluginList
放入plugins
屬性下,在plugins
屬相后加如下代碼
.concat(HtmlWebpackPluginList)
我們還需要修改webapck.prod.config.js
默認HtmlWebpackPlugin
的實例的參數,新增chunks: ['app']
new HtmlWebpackPlugin({
filename: utils.resolve('./../dist/index.html'), // html模板的生成路徑
template: 'index.html',//html模板
inject: true, // true:默認值,script標簽位于html文件的 body 底部
chunks: ['app'], // 注入app名稱bundel
minify: {
removeComments: true, //去注釋
collapseWhitespace: true, //壓縮空格
removeAttributeQuotes: true //去除屬性引用
}
}),
執行npm run build
測試打包結果,dist目錄如下:
通過webpack-bundle-analyzer
分析webpack的拆包和bundle包之間的依賴關系
得出結論:
這種多頁面打包方式,在整個項目的基礎上打包,webpack會對這些頁面的引用的第三方模塊進行拆包,導致不同的頁面存在相同的bundle包,也就是不同頁面存在共用的bundle包。這樣的好處是可以減小打包的總體積
抽取HtmlWebpackPluginList
在module-entry.js
添加工具方法如下
const HtmlWebpackPlugin = require("html-webpack-plugin")
const utils = require("./utils")
// 獲取htmlwebpackplugin列表
function getHtmlWebpackPluginList(options={}){
const moduleList = getModuleList();
const HtmlWebpackPluginList = [];
for(let index in moduleList){
const moduleName = moduleList[index];
const HtmlWebpackPluginOptions = {
filename: utils.resolve('./../dist/'+ moduleName+ '/index.html'), // html模板的生成路徑
template: utils.resolve("./../src/modules/" + moduleName + "/index.html"),//html模板
inject: true, // true:默認值,script標簽位于html文件的 body 底部
chunks: [moduleName], // 注入哪個名稱bundel
};
if(options.extract){
HtmlWebpackPluginOptions = Object.assign(HtmlWebpackPluginOptions,{
minify: {
removeComments: true, //去注釋
collapseWhitespace: true, //壓縮空格
removeAttributeQuotes: true //去除屬性引用
},
})
}
HtmlWebpackPluginList.push(new HtmlWebpackPlugin(HtmlWebpackPluginOptions))
}
return HtmlWebpackPluginList;
}
導出該方法。然后在webpack.dev.config.js
以及webpack.prod.config.js
中導入getHtmlWebpackPluginList
方法,
const { getModuleList , getBuildEntry,getHtmlWebpackPluginList } = require("./module-entry")
最后.concat(HtmlWebpackPluginList)
替換成.concat(getHtmlWebpackPluginList())
然后在把rewrites
數組提取,在module-entry.js
添加工具方法如下:
// 獲取開發環境重定向的規則,只在開發環境中使用
function getRewritesList(){
// 獲取各個模塊
const moduleList = getModuleList();
// 需要在開發環境重寫的規則數組
const rewrites = []; // webpack-dev-server的historyApiFallback中使用
for(let index in moduleList){
const moduleName = moduleList[index]
// 以模塊名開頭的路徑,重定向到 改模塊下的index.html模板文件 比如路徑一以/a開頭,會重定向到a模塊下的index.html
rewrites.push({
from:new RegExp('^\/' + moduleName),
to:utils.resolve('/' + moduleName +'/index.html')
})
}
return rewrites;
}
導出該方法,然后為webpack.dev.config.js
文件導入getRewritesList
方法
···
const { getModuleList , getBuildEntry,getHtmlWebpackPluginList, getRewritesList} = require("./module-entry")
···
把historyApiFallback: {rewrites: rewrites}
修改成 historyApiFallback: {rewrites: getRewritesList()}
執行npm run build
測試打包結果,一切正常!