Vue项目卡顿慢加载?这些优化技巧告诉你!(一):https://developer.aliyun.com/article/1415073
pdf插件优化
项目中有pdf预览的功能,安装了pdfjs-dist
和vue-pdf
两个插件,后来跟后端协商,走文件流的方式了, 就没用到这两个, 就卸载了, 如果谁有相关方案,欢迎来提
moment.js的优化
如果时间充裕,建议换成day.js 大小只有6kb, 但是我们项目里面用moment地方太多了,且比较混乱, 就暂时先不更换, 采用剔除其他语言包的方案
方案一
const MomentLocalesPlugin = require('moment-locales-webpack-plugin'); module.exports = { configureWebpack: { plugins: [ new MomentLocalesPlugin({ // 保留一个 localesToKeep: ['zh-cn'], }), ] } } //使用 import moment from 'moment'; // 按需加载需要引入对应的语言包 import 'moment/locale/zh-cn'; moment.locale('zh-cn');
方案二
const webpack = require('webpack'); module.exports = { //... plugins: [ // 忽略 moment.js的所有本地文件 new webpack.IgnorePlugin(/^./locale$/, /moment$/), ], }; // 使用 import moment from 'moment'; // 按需加载需要引入对应的语言包 import 'moment/locale/zh-cn'; moment.locale('zh-cn');
针对lodash库优化
项目中有时会安装lodash,但一般只使用几个方法, 在打包的时候,会发现会将lodash全部打进去, 所以使用下面这两个库
babel-plugin-lodash
用来精简Lodash模块的,只保留用到的方法。lodash-webpack-plugin
这个插件经过用noop, identity, 或其余更简单的替代品来替换一些模块的特性,使得打包后的体积更小(翻译)。
注意:这个插件默认会关闭一些lodash不经常使用的特性,能够给插件传递options来开启某些特性
modules.exports = { // 其余配置省略... plugins: ['lodash'] } // vue.config.js const LodashModuleReplacementPlugin = require('lodash-webpack-plugin'); module.exports = { // ... configureWebpack: { plugins: [ new LodashModuleReplacementPlugin() ] } } // 或者chainWebpack 但是我觉得这种写法,有点一言难尽 module.exports = { chainWebpack: config => { config.plugin("loadshReplace") .use(new LodashModuleReplacementPlugin()); } }
注意: 这个插件可能带来一些意想不到的坑, 可能会导致使用第三方插件报错出现一些其他问题,具体可以参考下这篇文章 zhuanlan.zhihu.com/p/349260482
开启 Tree Shaking 功能
在前面的依赖视图分析中可以看到sailfish 这个依赖,如下图,它是我们公司以前封装的UI库,它是以源码的方式供项目使用, 没有打包. 老项目里面用了几个组件,打包的时候发现还是全部打进来了
优化方式有以下几个点
- 按需加载:在需要使用这个 UI 库的组件时再进行动态加载,不在打包时将整个库全部打入。
- 开启 Tree Shaking:如果你使用的是 ES6 模块化的方式引入这个 UI 库,并且使用了相关的工具(如 webpack),则可以开启 Tree Shaking 功能,只将你用到的组件打入打包文件中,未使用的组件将会被去除。
- 使用 babel-plugin-import 插件:有些 UI 库将所有的组件都导出为一个模块,对于这种情况,你可以使用 babel-plugin-import 插件,通过按需加载的方式只加载你使用的组件,而不是全部导入。
- 自己打包:如果这个 UI 库的源码是开放的,而且你只想使用部分组件,那么你也可以自己将需要的组件单独打包,而不是全部打包。
因为后期规划要替换掉它,所以不想在它上面浪费过多精力
其他的优化
externals
externals
配置项用来告诉Webpack
要构建的代码中使用了哪些不用被打包的模块,也就是说这些模版是外部环境提供的,Webpack
在打包时可以忽略它们
一般是将体积较大的第三方包抽离为externals,一般处理element-ui
,vue
或者其他的,看自己项目
// vue.config.js module.exports = { configureWebpack: { externals: { // 安装的包名 --- 暴露的全局变量的值 "element-ui": "ELEMENT", echarts: "echarts", vue: "Vue", }, }, }
然后在index.html引入对应的cnd链接, 如果害怕cdn不稳定,可以去下载下来,copy到自己的项目中,手动引入, 也可使用webpack配置注入进去
// vue.config.js module.exports = { chainWebpack: config => { config.plugin('html').tap(args => { args[0].cdn = { js: [ 'https://xx.com/CDN/js/index-element-ui@2.13.0.js', ], css: [ 'https://xx.com/CDN/css/element-ui2.13.0/index.css', ], }; return args; }); } } // 使用 <!DOCTYPE html> <html lang="zh"> <head> <% 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 文件,配置在 vue.config.js 下 --> <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %> <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>" type="text/javascript"></script> <% } %> </head> <body> <div id="app"></div> </body> </html> </html>
注意
- externals:声明文件被外部引用不用打包,不参与打包流程,由于直接写死在html里,可以使用cdn等加速
- externals缺点:直接html内引入的,所以不会有treeshaking,按需引入等,具体看自己取舍
推荐常用的两个cdn服务 BootCDN 与 cdnjs 。
移除 preload(预载) 与 prefetch (预取)
vue 脚手架默认开启了 preload 与 prefetch,当我们项目很大时,会造成首屏加载速度慢的元凶
preload 与 prefetch 都是一种资源预加载机制; preload 是预先加载资源,但并不执行,只有需要时才执行它; prefetch 是意图预获取一些资源,以备下一个导航/页面使用; preload 的优先级高于 prefetch。
// vue.config.js chainWebpack: config => { // 移除 preload(预载) 插件 config.plugins.delete('preload') // 移除 prefetch(预取) 插件 config.plugins.delete('prefetch') }
主要关注首屏速度, 可自行去体验下关闭前后的效果
清除log等调试信息
使用 terser-webpack-plugin 清除 console.log
先安装依赖 npm install terser-webpack-plugin --save-dev 然后配置 const TerserPlugin = require('terser-webpack-plugin'); module.exports = { configureWebpack: config => { // 生产环境下清除 console.log if (process.env.NODE_ENV === 'production') { return { optimization: { minimizer: [ new TerserPlugin({ sourceMap: false, terserOptions: { compress: { drop_console: true } } }) ] } } } } }
分包拆包
一般拆包原则如下
- 将变动的与不易变动的资源进行分离 —> 有效利用缓存;* 将 node_modules 中的资源拆分出来,如果 node_modules 中的资源不变,就可以有效利用缓存,避免受到业务代码频繁改动的影响;
- 将大的拆分成若干个小的 chunk —> 缩短单个资源下载时间;
- 将公共模块抽离出来 —> 避免资源被重复打包,这样也可以在一定程度上减小打包产物总体积;
- 将被多个 chunk 引用的包拆分成单独的模块;
要根据项目情况灵活配置,我就贴一个我在项目里使用的
// vue.config.js module.exports = { chainWebpack(config) { config.when(process.env.NODE_ENV !== 'development',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 }, elementUI: { name: 'chunk-elementUI', // split elementUI 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[\/]_?element-ui(.*)/ // 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 } } }) // https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk config.optimization.runtimeChunk('single') } ) } }
压缩图片
项目里面后期肯定会放不少图片进去, 这些图片如果不压缩以下,占的资源就会非常大,社区里面有不少插件可以进行压缩,但我个人还是很喜欢熊猫压缩
推荐大家使用. 可以整个文件夹丢进去
结束语
一般常用的就这些,当然还有很多其他的优化方案, 这里没有绝对的最佳配置,只有最佳实践,大家根据自己的项目实际情况,逐步分析优化.