最近优化以前的项目,从单页面拆分红了多页面,这里记录下整个踩坑过程。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…