最近優化以前的項目,從單頁面拆分紅了多頁面,這裏記錄下整個踩坑過程。javascript
1.將原項目的單頁面替換爲多頁面
css
index.html
和
user.html
兩個頁面。
2.配置vue.config.jshtml
// vue.config.js
const titles = require('./title.js')
const glob = require('glob')
const pages = {}
glob.sync('./src/pages/**/main.js').forEach(path => {
const chunk = path.split('./src/pages/')[1].split('/main.js')[0]
pages[chunk] = {
entry: path,
template: 'public/index.html',
title: titles[chunk],
chunks: ['chunk-vendors', 'chunk-common', chunk]
}
})
module.exports = {
pages,
chainWebpack: config => config.plugins.delete('named-chunks'),
devServer: {
proxy: {
'/api': {
target: 'http://127.0.0.1:8080',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
}
複製代碼
能夠執行vue inspect
查看完整配置信息:
vue
vue-router
官方給出的示例以下,這裏webpackChunkName
若是不寫打包時會自動生成序號代替。java
//router/index.js
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
},
複製代碼
爲了方便追蹤打包狀況最好寫上,就能夠看到about.[hash].js
的大小了。webpack
若是想要多個路由打包進一個js裏,寫同一個webpackChunkName
便可git
{
path: '/another',
name: 'another',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/Another.vue')
}
複製代碼
打包後about.js
文件變大了0.33Kb,多了一個頁面。
github
npm i webpack-bundle-analyzer
後修改vue.config.js配置web
//vue.config.js
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
const report = process.env.npm_config_report;
module.exports = {
...
configureWebpack: config => {
if (report) {
config.plugins.push(new BundleAnalyzerPlugin());
}
},
...
}
複製代碼
再在package.json
中添加vue-router
"scripts": {
...
"analyze": "npm_config_report=true npm run build"
},
複製代碼
這裏經過控制npm_config_report
來顯示分析頁面,執行npm run analyze
能夠看到打包狀況
上圖能夠看到vue
、vue-router
和vuex
佔據了大部分空間,它們和咱們的實際開發無關,且不會常常變化,咱們能夠把它們單獨提取出來,這樣不用每次都從新打包,瀏覽器訪問時由於並行加載和緩存會極大地提升訪問效率。
常見的優化方法有兩種:一是經過cdn
搭配externals
來實現,二是經過單獨打包搭配DllReferencePlugin
。
簡單說下兩種優劣: cdn
+externals
:配置簡單,全環境下均可訪問到cdn,若是不慎調用了打包文件內部的方法,可能會致使重複打包; DllReferencePlugin
:配置較複雜,須要手動生成dll,全部訪問指向同一份dll,不會形成重複打包。
1.簡單的配置方法
最簡單的作法就是將cdn手動添加到index.html
中
<body>
<div id="app"></div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
<script src="https://cdn.bootcss.com/vuex/3.1.1/vuex.min.js"></script>
</body>
複製代碼
而後在vue.config.js
中說明externals
項
configureWebpack: (config) => {
config.externals = {
vue: 'Vue',
vuex: 'Vuex',
'vue-router': 'VueRouter',
// 'alias-name': 'ObjName'
// 寫法: 中劃線: 上駝峯
}
},
複製代碼
這時再打包就沒有這些依賴項了。
2.區分開發環境和正式環境
以上方法配置後,由於引入的是vue.min.js
,會致使Vue DevTool
沒法使用,怎麼辦呢?
const isProduction = process.env.NODE_ENV === "production";
...
configureWebpack: (config) => {
if(isProduction){
config.externals = {
vue: 'Vue',
vuex: 'Vuex',
'vue-router': 'VueRouter',
// 'alias-name': 'ObjName'
// 寫法: 中劃線: 上駝峯
}
}
},
複製代碼
正式環境才使用externals
能夠嗎?能夠,可是報錯:
Uncaught TypeError: Cannot redefine property: $router
複製代碼
由於在index.html
中已經引入過一次vue-router
了,若是不把它放入externals
,就會重複定義。
所以咱們須要僅在正式環境中引入cdn,調整以前的代碼:
// vue.config.js
const cdn = {
css: [],
js: [
'https://cdn.bootcss.com/vue/2.6.10/vue.min.js',
'https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js',
'https://cdn.bootcss.com/vuex/3.1.1/vuex.min.js',
],
}
module.exports={
...
chainWebpack: (config) => {
if (isProduction) {
// 生產環境注入cdn + 多頁面
glob.sync("./src/pages/**/main.js").forEach(path => {
const chunk = path.split("./src/pages/")[1].split("/main.js")[0];
config.plugin("html-" + chunk).tap(args => {
args[0].cdn = cdn;
return args;
});
});
}
},
}
複製代碼
這一步在多頁面模式下有個坑,官方有提到可是一句帶過了——就是會存在多個 html-webpack-plugin
實例,而config.plugin()
必須接收準確的plugin
名稱,若是硬着頭皮按照網上教程走,確定會卡在這裏。其實很簡單,只要vue inspect --plugins
就能夠看到了
接下來,index.html
處理下cdn參數就完成啦😝
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<!-- 使用CDN的CSS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
<% } %>
<!-- 使用CDN的JS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.js[i] %>" rel="preload" as="script" />
<% } %>
</head>
<body>
<noscript>
<strong>We're sorry but vue-multiple-pages-demo doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
<!-- built files will be auto injected -->
</body>
</html>
複製代碼
前面提到也能夠用dllPlugin
來優化,不過若是你使用chrome
的Vue DevTool
,vue
就不能放進dllPlugin
了。
1.建立webpack.dll.conf.js
const path = require('path')
const webpack = require('webpack')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
// dll文件存放的目錄
const dllPath = 'public/vendor'
module.exports = {
entry: {
core: ['vue-router','vuex'],
// other: [],
},
output: {
path: path.join(__dirname, dllPath),
filename: '[name].dll.js',
// vendor.dll.js中暴露出的全局變量名
// 保持與 webpack.DllPlugin 中名稱一致
library: '[name]_[hash]',
},
plugins: [
// 清除以前的dll文件
// "clean-webpack-plugin": "^1.0.0" 注意版本不一樣的寫法不一樣
// new CleanWebpackPlugin(['*.*'], {
// root: path.join(__dirname, dllPath),
// }),
// "clean-webpack-plugin": "^3.0.0"
new CleanWebpackPlugin(),
// 設置環境變量
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: 'production',
},
}),
// manifest.json 描述動態連接庫包含了哪些內容
new webpack.DllPlugin({
path: path.join(__dirname, dllPath, '[name]-manifest.json'),
// 保持與 output.library 中名稱一致
name: '[name]_[hash]',
context: process.cwd(),
}),
],
}
複製代碼
2.預編譯dll
在package.json
中添加
script:{
...
"dll": "webpack -p --progress --config ./webpack.dll.conf.js"
}
複製代碼
運行npm run dll
就能夠在public/vendor
下生成dll了。
3.在webpack
中聲明預編譯部分
聲明後webpack
打包時就會跳過這些dll。
// vue.config.js
configureWebpack: config => {
if (isProduction) {
config.externals = {
vue: "Vue"
// vuex: "Vuex", 這些都改爲dllPlugin編譯
// "vue-router": "VueRouter"
// 'alias-name': 'ObjName'
// 寫法: 中劃線: 上駝峯
};
}
config.plugins.push(
// 名稱要和以前的一致,能夠繼續擴展多個
...["core"].map(name => {
return new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require(`./public/vendor/${name}-manifest.json`)
});
})
);
},
複製代碼
4.引用dll
最後就是引入到index.html
中,你能夠簡單地直接寫入:
<script src=./vendor/core.dll.js></script>
複製代碼
若是想更智能些,就用用到add-asset-html-webpack-plugin
,它能將生成在public/vendor
下的dll自動注入到index.html
中。
const AddAssetHtmlPlugin = require("add-asset-html-webpack-plugin");
config.plugins.push(
...
// 將 dll 注入到 生成的 html 模板中
new AddAssetHtmlPlugin({
// dll文件位置
filepath: path.resolve(__dirname, "./public/vendor/*.js"),
// dll 引用路徑
publicPath: "./vendor",
// dll最終輸出的目錄
outputPath: "./vendor"
})
);
},
複製代碼
大功告成!
每個commit
對應一個配置步驟,vue
利用external
引入,區分了開發和配置環境,其餘的vue-router
等都是用dllPlugin
引入的,有須要的小夥伴能夠去看看 :)
github.com/Plortinus/v…
juejin.im/post/5cb041…
blog.csdn.net/neoveee/art…