本文具体介绍 vue.config.js 配置,以优化打包后 chunk-vendors.js 的大小,不具体介绍代码优化及各类api封装等。
1. 老生常谈的代码层面的优化
- 按需引入各类组件库
详情请见element 官网
1)element,echarts等
// element
import { Button, Input } from 'element-ui';
Vue.use(Button).use(Input);
// echarts
//引入基本模板
let echarts = require('echarts/lib/echarts')
// 引入折线图等组件
require('echarts/lib/chart/gauge')
require('echarts/lib/chart/radar')
Vue.prototype.$echarts = echarts
.babel.config.js
module.exports = {
presets: [["@vue/app", { useBuiltIns: "entry" }]],
plugins: [
// element官方教程
[
"component",
{
libraryName: "element-ui",
styleLibraryName: "theme-chalk"
}
]
]
}
v-if 和 v-show选择调用
1)少使用v-if
2)绑定key细分vuejs组件
1)组件细分,比如一个组件,可以把整个组件细分成轮播组件、列表组件、分页组件等。减少watch的数据
1)数据大时,系统会出现卡顿,所以减少watch的数据。路由懒加载
1)分割路由,当路由被访问的时候才加载对应的组件。
const router = new VueRouter({
routes: [
{ path: '/foo', component: () => import('./Foo.vue')}
]
})
- 内容类系统的图片资源按需加载
1)图片加载比较多,使用v-lazy之类的懒加载。
npm install vue-lazyload --save-dev
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload)
手动清除各类事件监听器,定时器及闭包等
SSR(服务端渲染)
如果项目比较大,首屏无论怎么做优化,都出现闪屏或者一阵黑屏的情况。可以考虑使用SSR(服务端渲染)vue functional 优化
Object.freeze 优化
减少http请求
减少缓存及dom操作
2. webpack 打包的优化
- 查看引入的各类包占比
- 在package.json 的scripts里加入 "report": "vue-cli-service build --report"
- 运行npm run report
- 在 dist 文件里找到report.html, 在浏览器打开即可
- 开启gzip压缩
// 下载
npm install compression-webpack-plugin --save-dev
const isProduction = ["production", "prod"].includes(process.env.NODE_ENV);
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i;
configureWebpack: config => {
const plugins = [];
if (isProduction) {
plugins.push(
new CompressionWebpackPlugin({
filename: "[path].gz[query]",
algorithm: "gzip",
test: productionGzipExtensions,
threshold: 10240,
minRatio: 0.8
})
);
}
config.plugins = [...config.plugins, ...plugins];
}
- splitChunks 单独打包第三方模块
// webpack配置
chainWebpack: (config) => {
if (isProduction) {
config.optimization.delete("splitChunks");
}
return config;
},
configureWebpack: config => {
if (isProduction) {
// 利用 splitChunks 单独打包第三方模块
config.optimization = {
splitChunks: {
cacheGroups: {
common: {
name: "chunk-common",
chunks: "initial",
minChunks: 2,
maxInitialRequests: 5,
minSize: 0,
priority: 1,
reuseExistingChunk: true,
enforce: true
},
vendors: {
name: "chunk-vendors",
test: /[\\/]node_modules[\\/]/,
chunks: "initial",
priority: 2,
reuseExistingChunk: true,
enforce: true
},
elementUI: {
name: "chunk-elementui",
test: /[\\/]node_modules[\\/]element-ui[\\/]/,
chunks: "all",
priority: 3,
reuseExistingChunk: true,
enforce: true
},
echarts: {
name: "chunk-echarts",
test: /[\\/]node_modules[\\/](vue-)?echarts[\\/]/,
chunks: "all",
priority: 4,
reuseExistingChunk: true,
enforce: true
}
}
}
};
}
config.plugins = [...config.plugins, ...plugins];
}
- 去除consollog
configureWebpack: config => {
const plugins = [];
if (isProduction) {
// 去除consollog
plugins.push(
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false, // 打包报错可注掉
drop_console: true,
drop_debugger: false,
pure_funcs: ["console.log"] //移除console
},
},
sourceMap: false,
parallel: true,
})
);
}
config.plugins = [...config.plugins, ...plugins];
}
- 压缩图片
config.module
.rule('images')
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
bypassOnDebug: true
})
.end()
设置完后,打包后可以看到 .gz 结尾的文件,在http请求的Request Headers 中能看到 Accept-Encoding:gzip。
要使服务器返回.gz文件,还需要对服务器进行配置,根据Request Headers的Accept-Encoding标签进行鉴别,如果支持gzip就返回.gz文件。
完整vue.config.js
const path = require("path");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const CompressionWebpackPlugin = require("compression-webpack-plugin");
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i;
const isProduction = ["production", "prod", 'test'].includes(process.env.NODE_ENV);
const resolve = dir => path.join(__dirname, dir);
module.exports = {
publicPath: "/", // 公共路径
runtimeCompiler: true, // 是否使用包含运行时编译器的 Vue 构建版本
outputDir: process.env.outputDir, // 不同的环境打不同包名
pluginOptions: {
"style-resources-loader": {
preProcessor: "less",
patterns: [
// 这个是加上自己的路径,
// 注意:试过不能使用别名路径
path.resolve(__dirname, "./src/assets/css/variable.less")
]
}
},
lintOnSave: false, // 关闭eslint
productionSourceMap: false, // 生产环境下css 分离文件
// css相关配置
devServer: {
// 配置服务器
port: 8112,
open: true,
https: false,
// disableHostCheck: true,
// overlay: {
// warnings: false,
// errors: true
// },
proxy: {
"/api/*": {
target: process.env.VUE_APP_URL, // 目标 API 地址
ws: true, // 是否代理 websockets
changOrigin: true, // 跨域配置
pathRewrite: {
"^/api": "/"
}
},
// "/common/*": {
// target: process.env.VUE_APP_MIPA_URL, // 目标 API 地址
// ws: true, // 是否代理 websockets
// changOrigin: true, // 跨域配置
// pathRewrite: {
// "^/common": "/"
// }
// }
}
},
// 兼容ie10
// webpack配置
chainWebpack: config => {
// 移除 prefetch 插件
config.plugins.delete('prefetch')
// 移除 preload 插件
config.plugins.delete('preload');
// ie 兼容,与main.js引入,存在一个就可
// config.entry("main").add("babel-polyfill");
// 别名
config.resolve.alias
.set("@", resolve("src"))
.set("@i", resolve("src/assets/image"));
if (isProduction) {
config.optimization.delete("splitChunks");
}
// 压缩图片
// config.module
// .rule('images')
// .use('image-webpack-loader')
// .loader('image-webpack-loader')
// .options({
// bypassOnDebug: true
// })
// .end()
return config;
},
// 配置webpack
configureWebpack: config => {
const plugins = [];
if (isProduction) {
// 开启gzip压缩
plugins.push(
new CompressionWebpackPlugin({
filename: "[path].gz[query]",
algorithm: "gzip",
test: productionGzipExtensions,
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: false // 删除原文件
})
);
// 利用 splitChunks 单独打包第三方模块
config.optimization = {
splitChunks: {
cacheGroups: {
common: {
name: "chunk-common",
chunks: "all",
minChunks: 2,
maxInitialRequests: 5,
minSize: 0,
priority: 1,
reuseExistingChunk: true,
enforce: true
},
vendors: {
name: "chunk-vendors",
test: /[\\/]node_modules[\\/]/,
chunks: "all",
priority: 2,
reuseExistingChunk: true,
enforce: true
},
styles: {
name: 'chunk-styles',
test: /\.(sa|sc|le|c)ss$/,
chunks: 'all',
enforce: true,
},
elementUI: {
name: "chunk-element-ui",
test: /[\\/]node_modules[\\/]element-ui[\\/]/,
chunks: "all",
priority: 3,
reuseExistingChunk: true,
enforce: true
},
echarts: {
name: "chunk-echarts",
test: /[\\/]node_modules[\\/]echarts[\\/]/,
chunks: "all",
priority: 4,
reuseExistingChunk: true,
enforce: true
},
vue: {
name: "chunk-vue",
test: /[\\/]node_modules[\\/]vue[\\/]/,
chunks: "all",
priority: 5,
reuseExistingChunk: true,
enforce: true
},
xlsx: {
name: "chunk-xlsx",
test: /[\\/]node_modules[\\/]xlsx[\\/]/,
chunks: 'all',
priority: 6,
reuseExistingChunk: true,
enforce: true
},
runtimeChunk: {
name: 'manifest'
}
}
}
};
// 去除consollog
plugins.push(
new UglifyJsPlugin({
uglifyOptions: {
// 删除注释
output: {
comments: false
},
compress: {
// warnings: false,
drop_console: true,
drop_debugger: false,
pure_funcs: ["console.log"] //移除console
}
},
sourceMap: false,
parallel: true
})
);
}
config.plugins = [...config.plugins, ...plugins];
}
};