vue项目优化

127 阅读4分钟

vue 项目可以通过添加--report命令: "build": "vue-cli-service build --report",打包后 dist 目录会生成 report.html 文件,用来分析各文件的大小

或者通过安装 webpack-bundle-analyzer 插件来分析,步骤如下:

1)安装

npm install webpack-bundle-analyzer -D 2)vue.config.js中 引入

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; module.exports = { configureWebpack: { plugins: [ new BundleAnalyzerPlugin() ] } } 3)npm run serve运行后,在浏览器打开http://127.0.0.1:8888/ 可以看到分析页面

初始打包速度 25386ms totalTime.png 开始优化 ✈︎ 1、externals 提取项目依赖 从上面的打包分析页面中可以看到,chunk-vendors.js 体积为 2.21M,其中最大的几个文件都是一些公共依赖包,那么只要把这些依赖提取出来,就可以解决 chunk-vendors.js 过大的问题

可以使用 externals 来提取这些依赖包,告诉 webpack 这些依赖是外部环境提供的,在打包时可以忽略它们,就不会再打到 chunk-vendors.js 中

1)vue.config.js 中配置:

module.exports = { configureWebpack: { externals: { vue: 'Vue', 'vue-router': 'VueRouter', axios: 'axios', echarts: 'echarts' } } 2)在 index.html 中使用 CDN 引入依赖

验证 externals 的有效性:

重新打包,最新数据如下:

打包体积:1.12M

externalsSize.png 打包速度:18879ms

externalsTime.png 使用 externals 后,包体积压缩50%、打包速度提升26%

2、组件库的按需引入 为什么没有使用 externals 的方式处理组件库呢?

externals缺点:直接在html内引入的,失去了按需引入的功能,只能引入组件库完整的js和css

组件库按需引入的原理:最终只引入指定组件和对应的样式

elementUI 需要借助 babel-plugin-component 插件实现,插件的作用如下:

如按需引入 Button 组件:

import { Button } from 'element-ui'

Vue.component(Button.name, Button) 编译后的文件(自动引入 button.css):

import _Button from "element-ui/lib/button"; import _Button2 from "element-ui/lib/theme-chalk/button.css"; // base.css是公共的样式 import "element-ui/lib/theme-chalk/base.css";

Vue.component(_Button.name, _Button); 通过该插件,最终只引入指定组件和样式,来实现减少组件库体积大小

1)安装 babel-plugin-component

npm install babel-plugin-component -D 2)babel.config.js中引入

module.exports = { presets: ['@vue/app'], plugins: [ [ 'component', { libraryName: 'element-ui', styleLibraryName: 'theme-chalk' } ] ] }; 验证组件库按需引入的有效性:

重新打包,最新数据如下:

打包体积:648KB

babel-plugin-componentSize.png 打包速度:15135ms

babel-plugin-componentTime.png 组件库按需引入后,包体积压缩72%、打包速度提升40%

同时 chunk-vendors.css 的体积也有了明显的减少,从206KB降到了82KB

原始体积:

originalCss.png 按需引入后:

babelCss.png 3、减小三方依赖的体积 继续分析打包文件,项目中使用了 momentjs,发现打包后有很多没有用到的语言包

momentJs.png 使用 moment-locales-webpack-plugin 插件,剔除掉无用的语言包

1)安装

npm install moment-locales-webpack-plugin -D 2)vue.config.js 中引入

const MomentLocalesPlugin = require('moment-locales-webpack-plugin');

module.exports = { configureWebpack: { plugins: [ new MomentLocalesPlugin({localesToKeep: ['zh-cn']}) ] } } 验证插件的有效性:

重新打包,最新数据如下:

打包体积:407KB

momentJsSize.png 打包速度:10505ms

momentJsTime.png 减小三方依赖体积后,包体积压缩82%、打包速度提升59%

4、HappyPack 多线程打包 由于运行在 Node.js 之上的 webpack 是单线程模型的,我们需要 webpack 能同一时间处理多个任务,发挥多核 CPU 电脑的威力

HappyPack 就能实现多线程打包,它把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程,来提升打包速度

1)安装

npm install HappyPack -D 2)vue.config.js 中引入

const HappyPack = require('happypack'); const os = require('os'); // 开辟一个线程池,拿到系统CPU的核数,happypack 将编译工作利用所有线程 const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });

module.exports = { configureWebpack: { plugins: [ new HappyPack({ id: 'happybabel', loaders: ['babel-loader'], threadPool: happyThreadPool }) ] } } 验证 HappyPack 的有效性:

重新打包,最新数据如下:

打包速度:8949ms

happyPackTime.png 使用HappyPack后,打包速度进一步提升了65%

由于测试项目较小,打包时间缩短的不算太多。实测发现越是复杂的项目,HappyPack 对打包速度的提升越明显

5、Gzip压缩 线上的项目,一般都会结合构建工具 webpack 插件或服务端配置 nginx,来实现 http 传输的 gzip 压缩,目的就是把服务端响应文件的体积尽量减小,优化返回速度

html、js、css资源,使用 gzip 后通常可以将体积压缩70%以上

这里介绍下使用 webpack 进行 gzip 压缩的方式,使用 compression-webpack-plugin 插件

1)安装

npm install compression-webpack-plugin -D 2)vue.config.js 中引入

const CompressionPlugin = require('compression-webpack-plugin');

module.exports = { configureWebpack: { plugins: [ new CompressionPlugin({ test: /.(js|css)(?.*)?$/i, //需要压缩的文件正则 threshold: 1024, //文件大小大于这个值时启用压缩 deleteOriginalAssets: false //压缩后保留原文件 }) ] } } 验证插件的有效性:

重新打包,原来 407KB 的体积压缩为 108KB

gZip.png 6、DllPlugin 动态链接库 DllPlugin 与 externals 的作用相似,都是将依赖抽离出去,节约打包时间。区别是 DllPlugin 是将依赖单独打包,这样以后每次只构建业务代码,而 externals 是将依赖转化为 CDN 的方式引入

当公司没有很好的 CDN 资源或不支持 CDN 时,就可以考虑使用 DllPlugin ,替换掉 externals

DllPlugin 配置流程大致分为三步:

1)创建 dll.config.js 配置文件

import { DllPlugin } from "webpack";

export default { // 需要抽离的依赖 entry: { vendor: ["vue", "vue-router", "axios", "echarts"] }, mode: "production", optimization: { splitChunks: { cacheGroups: { vendor: { chunks: "all", name: "vendor", test: /node_modules/ } } } }, outp