Vue项目性能优化

项目初始体积与速度

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

结果预览

性能优化

externals 提取项目依赖

打包分析页面中可以看到,其中最大的几个文件都是一些公共依赖包,那么只要把这些依赖提取出来,就可以解决 chunk-vendors.js 过大的问题

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

// vue.config.js 中配置
module.exports = {
  configureWebpack: {
    externals: {
      vue: 'Vue',
      'vue-router': 'VueRouter',
      axios: 'axios',
      echarts: 'echarts'
    }
}
<-- 在 index.html 中使用 CDN 引入依赖 !-->
<body>
    <script src="http://lib.baomitu.com/vue/2.6.14/vue.min.js"></script>
    <script src="http://lib.baomitu.com/vue-router/3.5.1/vue-router.min.js"></script>
    <script src="http://lib.baomitu.com/axios/1.2.1/axios.min.js"></script>
    <script src="http://lib.baomitu.com/echarts/5.3.2/echarts.min.js"></script>
  </body>

提前公共依赖为独立小文件

// vue.config.js 中配置
module.exports = {
  chainWebpack(config) {
    config
      .optimization.splitChunks({
        chunks: 'all',
        cacheGroups: {
          pdfh5: {
            name: 'chunk-pdfh5',
            priority: 20,
            test: /[\\/]node_modules[\\/]_?pdfh5(.*)/
          },
          jsoneditor: {
            name: 'chunk-jsoneditor',
            priority: 20,
            test: /[\\/]node_modules[\\/]_?jsoneditor(.*)/
          },
          commons: {
            name: 'chunk-commons',
            test: resolve('src/components'), // can customize your rules
            minChunks: 3, //  minimum common number
            priority: 5,
            reuseExistingChunk: true
          }
        }
      })
  }
}

减小三方依赖的体积

继续分析打包文件,项目中使用了 momentjs,发现打包后有很多没有用到的语言包

moment包大小

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

# 安装
npm install moment-locales-webpack-plugin -D
// vue.config.js 中引入
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');

module.exports = {
  configureWebpack: {
     plugins: [
       new MomentLocalesPlugin({localesToKeep: ['zh-cn']})
     ]
  }
}

HappyPack 多线程打包

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

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

# 安装
npm install HappyPack -D
// 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
      })
     ]
  }
}

Gzip压缩

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

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

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

# 安装
npm install compression-webpack-plugin -D
// vue.config.js 中引入

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

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

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/
                }
            }
        }
    },
    output: {
        filename: "[name].dll.js", // 输出路径和文件名称
        library: "[name]", // 全局变量名称:其他模块会从此变量上获取里面模块
        path: AbsPath("dist/static") // 输出目录路径
    },
    plugins: [
        new DllPlugin({
            name: "[name]", // 全局变量名称:减小搜索范围,与output.library结合使用
            path: AbsPath("dist/static/[name]-manifest.json") // 输出目录路径
        })
    ]
};

2)package.json 配置脚本

"build:dll": "webpack --config ./dll.config.js",

3)使用 DllReferencePlugin 将打包生成的dll文件,引用到需要的预编译的依赖上来,并通过 html-webpack-tags-plugin 在打包时自动插入dll文件

vue.config.js 配置如下

import { DllReferencePlugin } from "webpack";
import HtmlTagsPlugin from "html-webpack-tags-plugin";

export default {
  configureWebpack: {
    plugins: [
      new DllReferencePlugin({
        manifest: AbsPath("dist/static/vendor-manifest.json") // manifest文件路径
      }),
      new HtmlTagsPlugin({
        append: false, // 在生成资源后插入
        publicPath: "/", // 使用公共路径
        tags: ["static/vendor.dll.js"] // 资源路径
      })
    ]
  }
};

先运行 npm run build:dll 打包生成依赖文件,以后只用运行 npm run build 构建业务代码即可