本文大約二千字,看完本文大概須要二十分鐘,動手嘗試須要一小時css
前段時間作項目,技術棧是vue+webpack,主要就是官網首頁加後臺管理系統 根據當時狀況,分析出三種方案html
上一張多頁面的結構圖 前端
npm install vue-cli -g
vue init webpack multiple-vue-amazing
複製代碼
npm install glob --save-dev
複製代碼
修改src文件夾下面的目錄結構vue
/* 這裏是添加的部分 ---------------------------- 開始 */
// 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過大引發加載速度慢,那就分離這個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
這個問題其實就是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多頁面配置和優化打包的思想其實也是同樣的,放心大膽的使用吧,有坑就解決它。
本文源碼喜歡點個贊