react+webpack4.x搭建前端項目(五)多頁面配置

前言

我們接著上一篇文章react+webpack4.x搭建前端項目之配置抽取和區分環境來進行多頁面打包的配置

這里小編推薦一個福利,更多精彩內容請點擊鏈接,點擊這里

首先修改src下的代碼,項目的結構目錄

QQ截圖20191213143525.png

源碼見修改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,結果如下圖

QQ截圖20191213161552.png

發現怎么是b頁面的內容?

那么是為什么呢?經過排查,打開控制臺選擇elements看到如下圖

QQ截圖20191213161753.png

index.html注入了所有bundleab模塊(頁面)的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 底部
}),

這時候重新運行成功解決!


QQ截圖20191213162054.png

然后我們打開http://localhost:8081/ahttp://localhost:8081/b頁面正常顯示。

QQ截圖20191213162633.png
QQ截圖20191213162649.png

但是存在一個問題!點擊二級路由的時候也可以正常顯示,但是我們直接打開或者刷新二級路由的時候(http://localhost:8081/a/test)時候發現頁面顯示成http://localhost:8081這個頁面了

因為這里我們設置的webpack-dev-serverhistoryApiFallback是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目錄如下:

QQ截圖20191213171322.png

通過webpack-bundle-analyzer分析webpack的拆包和bundle包之間的依賴關系

QQ截圖20191213171520.png

得出結論:
這種多頁面打包方式,在整個項目的基礎上打包,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測試打包結果,一切正常!

源碼:react+webpack4.x多頁面 release-tag-0.0.3

下一篇:react+webpack4.x多模塊打包配置

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容