本文大約二千字,看完本文大概須要二十分鐘,動手嘗試須要一小時css
前段時間作項目,技術棧是vue+webpack,主要就是官網首頁加後臺管理系統 根據當時狀況,分析出三種方案html
上一張多頁面的結構圖 前端
npm install vue-cli -g
vue init webpack multiple-vue-amazing
複製代碼
npm install glob --save-dev
複製代碼
修改src文件夾下面的目錄結構vue
在util.js裏面加入/* 這裏是添加的部分 ---------------------------- 開始 */ // glob是webpack安裝時依賴的一個第三方模塊,還模塊容許你使用 *等符號, 例如lib/*.js就是獲取lib文件夾下的全部js後綴名的文件 var glob = require('glob') // 頁面模板 var HtmlWebpackPlugin = require('html-webpack-plugin') // 取得相應的頁面路徑,由於以前的配置,因此是src文件夾下的pages文件夾 var PAGE_PATH = path.resolve(__dirname, '../src/pages') // 用於作相應的merge處理 var merge = require('webpack-merge') //多入口配置 // 經過glob模塊讀取pages文件夾下的全部對應文件夾下的js後綴文件,若是該文件存在 // 那麼就做爲入口處理 exports.entries = function () { var entryFiles = glob.sync(PAGE_PATH + '/*/*.js') var map = {} entryFiles.forEach((filePath) => { var filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.')) map[filename] = filePath }) return map } //多頁面輸出配置 // 與上面的多頁面入口配置相同,讀取pages文件夾下的對應的html後綴文件,而後放入數組中 exports.htmlPlugin = function () { let entryHtml = glob.sync(PAGE_PATH + '/*/*.html') let arr = [] entryHtml.forEach((filePath) => { let filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.')) let conf = { // 模板來源 template: filePath, // 文件名稱 filename: filename + '.html', // 頁面模板須要加對應的js腳本,若是不加這行則每一個頁面都會引入全部的js腳本 chunks: ['manifest', 'vendor', filename], inject: true } if (process.env.NODE_ENV === 'production') { conf = merge(conf, { minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true }, chunksSortMode: 'dependency' }) } arr.push(new HtmlWebpackPlugin(conf)) }) return arr } /* 這裏是添加的部分 ---------------------------- 結束 */ 複製代碼
webpack.base.conf.js 文件node
/* 修改部分 ---------------- 開始 */
entry: utils.entries(),
/* 修改部分 ---------------- 結束 */
複製代碼
webpack.dev.conf.js 文件jquery
/* 註釋這個區域的文件 ------------- 開始 */ // new HtmlWebpackPlugin({ // filename: 'index.html', // template: 'index.html', // inject: true // }), /* 註釋這個區域的文件 ------------- 結束 */ new FriendlyErrorsPlugin() /* 添加 .concat(utils.htmlPlugin()) ------------------ */ ].concat(utils.htmlPlugin()) 複製代碼
webpack.prod.conf.js 文件webpack
/* 註釋這個區域的內容 ---------------------- 開始 */ // new HtmlWebpackPlugin({ // filename: config.build.index, // template: 'index.html', // inject: true, // minify: { // removeComments: true, // collapseWhitespace: true, // removeAttributeQuotes: true // // more options: // // https://github.com/kangax/html-minifier#options-quick-reference // }, // // necessary to consistently work with multiple chunks via CommonsChunkPlugin // chunksSortMode: 'dependency' // }), /* 註釋這個區域的內容 ---------------------- 結束 */ // copy custom static assets new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../static'), to: config.build.assetsSubDirectory, ignore: ['.*'] } ]) /* 該位置添加 .concat(utils.htmlPlugin()) ------------------- */ ].concat(utils.htmlPlugin()) 複製代碼
npm install element-ui bootstrap-vue --save
複製代碼
分別在不一樣的頁面引入不一樣的ui index.jsnginx
import BootstrapVue from 'bootstrap-vue' Vue.use(BootstrapVue) 複製代碼
admin.jsgit
import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' Vue.use(ElementUI) 複製代碼
上面多頁面的配置是參考網上的,並且網上的思路大都很類似,核心就是改多個entry,配置完成了以後,開發的時候也是發現不了問題的,而後大概就開發了一個月,開發完以後對官網進行性能分析時發現,webpack打包的vendor.js網絡加載時間特別長,致使首屏的白屏時間很是長,最終經過-webpack-bundle-analyzer分析獲得告終論github
npm run build --report
複製代碼
你會發現vendor.js包含了index.html和admin.html的共同部分,因此這個vendor包註定會很大很冗餘
既然是vendor過大引發加載速度慢,那就分離這個vendor就行了。我是這樣想的,把各個頁面中都使用到的第三方代碼提取至vendor.js中,而後各個頁面中用到的第三方代碼再打包成各自的vendor-x.js,例如現有頁面index.html、admin.html,則最終會打包出vendor.js、vendor-index.js、vendor-admin.js。
webpack.prod.conf.js 文件
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor-admin', chunks: ['vendor'], minChunks: function (module, count) { return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf(path.join(__dirname, '../node_modules')) === 0 && module.resource.indexOf('element-ui') != -1 ) } }), new webpack.optimize.CommonsChunkPlugin({ name: 'vendor-index', chunks: ['vendor'], minChunks: function (module, count) { return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf(path.join(__dirname, '../node_modules')) === 0 && module.resource.indexOf('bootstrap-vue') != -1 ) } }), 複製代碼
再次分析,一切都很ok,vendor.js被分離成了vendor.js、vendor-index、vendor-admin.js
原本覺得解決了CommonsChunkPlugin的分離vendor.js的問題,就能夠了,而後打包出來發現index.html和admin.html都少了一個引入(各自對應的那個vendor-xx.js)這個問題其實就是HtmlWebpackPlugin的問題 把原來的 chunksSortMode: 'dependency'改爲自定義函數的配置,以下
util.js文件
chunksSortMode: function (chunk1, chunk2) { var order1 = chunks.indexOf(chunk1.names[0]) var order2 = chunks.indexOf(chunk2.names[0]) return order1 - order2 }, 複製代碼
其實多頁面之間抽離公共的文件的場景,中大型項目會用的比較多,最初是看到下面評論說須要抽離common-api的時候會多打包出來common-api.css,而且有同窗私信我遇到一些關於commonChunk的問題,我作一個更新,並提供思路
需求:項目中一些公共的js甚至是css,可複用到每個page裏邊,例如admin.js引用common-api.js,index.js也引用了common-api.js。如今抽離多個頁面之間公共的模塊common-api
新建一個common/index.js 直接引用一個本地文件js(我這裏用jquery來代替公共js)
在admin.js index.js裏引用
import $ from '../../common' console.log($('body')) 複製代碼
打包明顯能發現jquery被打包了兩次,資源浪費了。
坑就是必須指定chunks 否則會報錯webpack ERROR in CommonsChunkPlugin: While running in normal mode it's not allowed to use a non-entry chunk
一開始有位同窗問我這個錯誤怎麼解決,一開始我也不知道,後來查閱了一些資料,發現只要指定了chunks就能夠解決這個錯誤,貼一段github的issues,感興趣的能夠深刻了解
從新指定util.js 裏面的htmlPlugin的順序
let chunks = filename === 'admin' ? ['manifest', 'vendor', 'vendor-admin', 'common-api', filename] : ['manifest', 'vendor', 'vendor-index', 'common-api', filename] 複製代碼
最後看看結果,抽離出來了common-api,jq只被加載了一次, 而且並無多打包出來common-api.css,html裏面script的順序也是對的
最終實現大功告成了,雖然配置看起來很簡單,不過我當時開發的時候,思考了好久,因此假如你CommonsChunkPlugin和HtmlWebpackPlugin不熟悉或者只會用別人第三方的配置表,估計會踩大坑,好比說,CommonsChunkPlugin不指定chunks,默認是什麼?minChunks大多數人只會寫一個數值,然而自定義一個函數的寫法其實才是最強大的,根據我我的的經驗chunks結合minChunks自定義函數的寫法,能解決幾乎全部CommonsChunkPlugin靈異的事件。
雖然本篇文章是基於webpack3來說的,可是webpack4多頁面配置和優化打包的思想其實也是同樣的,放心大膽的使用吧,有坑就解決它。
本文源碼喜歡點個贊