由于cli3配置简化,cli2中原来存在的很多配置出现了一些改变,现在就以下遇到的问题进行记录
一、 实现vue-router懒加载功能
使用初衷是为了减少首页http请求过多,默认预加载了全部的js文件
官网文档提出 Preload
与Prefetch
,vue-cli3默认使用俩大功能
vue.config.js
增加如下配置,取消prefetch
和preload
,这样就是实现加载当前所需
chainWebpack(config) {
config.plugins.delete('preload')
config.plugins.delete('prefetch')
}
1、Preload
用来指定页面加载后很快会被用到的资源,所以在页面加载的过程中,我们希望在浏览器开始主体渲染之前尽早 preload
。
2、Prefetch
用来告诉浏览器在页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容。
// webpackPrefetch 开启home页的预加载 ,未设置的默认执行路由懒加载
// webpackChunkName 指定about页面在当前home加载完提前加载
component: () => import(/* webpackChunkName: "about",webpackPrefetch: true */ "../views/Home.vue"),
二、webpack 外包扩展 (externals
)
使用externals减小打包的后js体积大小,cdn加速来提升访问速度
初衷为了减小打包后的文件大小,下图为浏览器使用externals
前后 请求文件总数量,时间,文件大小的对比图
1.在vue.config.js
文件中增加如下配置
// 对应的版本可以看package.json
const cdnMap = {
css: [
'https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/theme-chalk/index.css'
],
js: [
'https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js',
'https://cdn.bootcdn.net/ajax/libs/vuex/3.1.3/vuex.min.js',
'https://cdn.bootcdn.net/ajax/libs/vue-router/3.1.6/vue-router.min.js',
'https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.min.js',
'https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/index.js',
'https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/locale/zh-CN.min.js',
]
}
// 需要扩展的资源
const externals = {
vue: "Vue",
vuex: "Vuex",
"vue-router": "VueRouter",
"element-ui":"element-ui",
"axios":"axios",
}
const regDev = RegExp(/development/);
module.exports = {
chainWebpack(config) {
// 外部扩展
config.externals((regDev.test(process.env.NODE_ENV))? {} : externals)
config.plugin('html').tap(args => {
args[0].cdn = (regDev.test(process.env.NODE_ENV))? {} : cdnMap
return args
})
}
}
2.通过 bootcdn
官网可以查询到vue,vue-router,vuex,axios,element-ui
的cdn外网地址,查询示例如下
为了区分开发环境和生产环境进行如下配置
在html
文件中引入cdn
地址 如下
<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">
<title>cdn外部扩展</title>
<% if (process.env.NODE_ENV !== 'development') { %>
<% for(var css of htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%=css%>" rel="preload" as="style" />
<link rel="stylesheet" href="<%=css%>" as="style" />
<% } %>
<% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
<link href="<%=js%>" rel="preload" as="script" />
<script type="text/javascript" src="<%=js%>"></script>
<% } %>
<% } %>
</head>
<body>
<div id="app"></div>
</body>
你以为当前就结束了?太天真了
当前配置完启动会报错 element is not defined
明明我已经在html里边引用了,开发模式怎么还报错呢?
经过层层排除问题,找到所有有关联关系的文件,最终锁定main.js
入口文件
// 判断在开发模式下才是用引入
const regDev = RegExp(/development/); // 为多个开发环境做的预留
if(regDev.test(process.env.NODE_ENV)){ //开发模式
const ElementUI = require("element-ui"); // 不能使用import形式引入
require("element-ui/lib/theme-chalk/index.css");
const locale = require("element-ui/lib/locale/lang/zh-CN");
Vue.use(ElementUI, {
locale,
});
}
现在有个问题,为什么vuex,vue-router,axios
不需要注释掉也不报错呢?vue.use(Vuex)
与externals
之间有什么关联呢?
实际上使用了externals
之后依赖,里边所有的use
都可以去掉了,因为外部差距都会先判断有没有vue实例,直接挂载上去,element-ui
出现问题是因为本身与vue有强依赖关系,所以element-ui
使用externals
的基础上同时去掉main.js
里的use
关于element
的单模块按需引用也可以有效的减少js体积,比如我只用到了 按钮,分页,表格等等
import {Button,ButtonGroup,Input,Pagination,Table,TableColumn} from 'element-ui';
Vue.use(Button);
Vue.use(ButtonGroup);
Vue.use(Input);
Vue.use(Pagination);
Vue.use(Table);
Vue.use(TableColumn);
这类情况不需要使用cdn
外扩加载,可以直接打包进js文件
三、压缩代码配置 提升效率,保证安全 uglifyjs-webpack-plugin
// cli3 默认不包含此依赖,需要下载引入
npm i uglifyjs-webpack-plugin --save
vue.config.js
添加以下代码
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
........此次省略其他配置
configureWebpack(config){
//压缩代码
config.plugins.push(
new UglifyJsPlugin({ // 更多配置请参考 uglifyJs官方文档
uglifyOptions: {
compress: {
drop_debugger: true, //生产环境自动删除debugger
drop_console: true, //生产环境自动删除console
},
warnings: false,
},
sourceMap: false,//是否生成map文件
parallel: true,//使用多进程并行运行来提高构建速度。默认并发运行数:os.cpus().length - 1。
})
)
}
}
四、代码分割配置优化 splitChunks
1.node_module
s全部打包成chunk-libs
并设置优先级为10
2.将vantUI
单独拆包,设置优先级为20
3.将自己的comnponents
打包成common
设置优先级为5
vue.config.js
添加以下代码
module.exports = {
........此次省略其他配置
chainWebpack(config) {
config.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // only package third parties that are initially dependent
},
// 如果想爸vantUI单独打包出来 可以通过当前方式设置 其他后引入的依赖也可以通过这种方式单独打包成一个js文件
vantUI: {
name: 'chunk-vantUI', // split vantUI into a single package
priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
test: /[\\/]node_modules[\\/]_?vant(.*)/ // in order to adapt to cnpm
},
commons: {
name: 'chunk-commons',
test: resolve('src/components'), // can customize your rules
minChunks: 3, // minimum common number
priority: 5,
reuseExistingChunk: true
}
}
})
}
}
启动报错:resolve is not defind
莫慌这是因为引用失败,vue.config.js
文件上方加入引用
const path = require('path');
function resolve(dir){
return path.join(__dirname, dir)
}
ok了,当前启动成功
五、合并css文件(经过测试没发现明显优化)
主要是借助ExtractTextPlugin
插件
优化思路是减少css文件的请求数量
css: {
// 是否将组件中的 CSS 提取至一个独立的 CSS 文件中,当作为一个库构建时,你也可以将其设置为 false 免得用户自己导入 CSS
// 默认生产环境下是 true,开发环境下是 false
extract: !(regDev.test(process.env.NODE_ENV))? true : false,
// 当为true时,css文件名可省略 module 默认为 false
modules: false,
// 是否为 CSS 开启 source map。设置为 true 之后可能会影响构建的性能
sourceMap: false,
//向 CSS 相关的 loader 传递选项(支持 css-loader postcss-loader sass-loader less-loader stylus-loader)
loaderOptions: { css: {}, less: {} }
}
为什么要CSS分离,开启CSS分离之后每个组件的css会单独打包,造成页面上有大量请求,所以在正式环境中将CSS分离关闭
启动项目发现提示warning警告
WARN "css.modules" option in vue.config.js is deprecated now, please use "css.requireModuleExtension" instead.
css-loader升级v3后使用css.requireModuleExtension代替css.modules
css: {
........
// 当为true时,css文件名可省略 requireModuleExtension默认为 false
requireModuleExtension: false,
.......
}
六、使用gzip的形式进行访问优化(正在测试中,稍后进行更新)
利用 compression-webpack-plugin
开启gzip
// 安装依赖
npm i compression-webpack-plugin --save-dev
vue.config.js
文件添加如下配置
const CompressionWebpackPlugin = require('compression-webpack-plugin')//开启gzip
module.exports = {
......此处省略一万行
chainWebpack(config) {
config.plugin('compression').use(CompressionWebpackPlugin).tap(() => [
{
test: /\.js$|\.html$|\.css/, // 匹配文件名
threshold: 1024, // 超过1k进行压缩
deleteOriginalAssets: false // 是否删除源文件
}
])
}
}
gzip
压缩率还是很高的,下方是对比图
经过一系列配置,发现vue.config.js
文件变量太多了,看上去不美观,现进行文件抽离
注意:不能使用es6
的import
引入,配置需要使用commonjs
的require
形式引入,es6
与commonjs
的关系自行百度,这里就不在复述了
抽离文件config.base.js
文件内容如下,放在项目src>config>config.base.js
'use strict';
const path = require('path');
//cdn外链地址 对应的版本可以看package.json
exports.cdnMap = {
css: [
'https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/theme-chalk/index.css'
],
js: [
'https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js',
'https://cdn.bootcdn.net/ajax/libs/vuex/3.1.3/vuex.min.js',
'https://cdn.bootcdn.net/ajax/libs/vue-router/3.1.6/vue-router.min.js',
'https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.min.js',
'https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/index.js',
'https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/locale/zh-CN.min.js',
]
}
// 需要扩展的资源
exports.externals = {
vue: "Vue",
vuex: "Vuex",
"vue-router": "VueRouter",
"element-ui":"element-ui",
"axios":"axios",
}
//正则检测当前是否为开发环境
exports.regDev = RegExp(/development/);
//路径指定函数
function resolve(dir){
return path.join(__dirname, dir)
}
//压缩代码配置
exports.UglifyJsPluginOptions={
uglifyOptions: {
compress: {
drop_debugger: true, //生产环境自动删除debugger
drop_console: true, //生产环境自动删除console
},
warnings: false,
},
sourceMap: false,//是否生成map文件
parallel: true,//使用多进程并行运行来提高构建速度。默认并发运行数:os.cpus().length - 1。
}
// 代码分割配置
exports.splitChunksOptions={
chunks: 'all',
cacheGroups: {
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // only package third parties that are initially dependent
},
commons: {
name: 'chunk-commons',
test: resolve('src/components'), // can customize your rules
minChunks: 3, // minimum common number
priority: 5,
reuseExistingChunk: true
}
}
}
//gzip配置
exports.CompressionWebpackPluginOptions ={
test: /\.js$|\.html$|\.css/, // 匹配文件名
threshold: 10240, // 超过10k进行压缩
deleteOriginalAssets: false // 是否删除源文件
}
vue.config.js
文件修改成如下形式
为区分开发环境,生产环境使用regDev
进行如下区分,开发环境不需要使用访问类优化,只有生产环境才需要
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const {regDev,cdnMap,externals,UglifyJsPluginOptions,splitChunksOptions,CompressionWebpackPluginOptions} = require("./src/config/config.base")
module.exports = {
outputDir: 'dist',
assetsDir: "static",
publicPath: (regDev.test(process.env.NODE_ENV))? "/" : "./",
productionSourceMap: false,
devServer: {
port: 8093,
host: '0.0.0.0',
open: true,
proxy: {
'/API': {
target: 'http://0.0.0.0:8088',
changeOrigin: true,
pathRewrite: {
'^/API': ''
},
ws: false
}
}
},
// 在exports中添加,这里很关键,不配置不行
transpileDependencies: ['element-ui'],
chainWebpack(config) {
// 在chainWebpack中添加下面的代码
config.entry('main').add('babel-polyfill') // main是入口js文件
// 移除 prefetch 插件
config.plugins.delete('prefetch')
// 开发环境不执行
if(!regDev.test(process.env.NODE_ENV)){
// 外部扩展
config.externals(externals)
config.plugin('html').tap(args => {
args[0].cdn = cdnMap
return args
})
// 代码分割
config.optimization.splitChunks(splitChunksOptions)
//压缩代码
config.optimization.minimizer = [
new UglifyJsPlugin(UglifyJsPluginOptions)
]
// 开启gzip 但是Nginx上也要做配置
config.plugin('compression').use(CompressionWebpackPlugin).tap(() => [
CompressionWebpackPluginOptions
])
}
},
css: {
// 是否将组件中的 CSS 提取至一个独立的 CSS 文件中,当作为一个库构建时,你也可以将其设置为 false 免得用户自己导入 CSS
// 默认生产环境下是 true,开发环境下是 false
extract: !(regDev.test(process.env.NODE_ENV))? true : false,
// 当为true时,css文件名可省略 module 默认为 false
modules: false,
// 是否为 CSS 开启 source map。设置为 true 之后可能会影响构建的性能
sourceMap: false,
//向 CSS 相关的 loader 传递选项(支持 css-loader postcss-loader sass-loader less-loader stylus-loader)
loaderOptions: { css: {}, less: {} }
}
}
深层优化方案借助分析工具 webpack-bundle-analyzer
(正在研究学习中,后续进行补充)
以上所有为个人实践总结和参考前辈经验,欢迎各位大佬指出问题,小弟感激不尽(性能优化持续进行中,后续继续总结)