实用的Vue项目打包优化方案

610 阅读2分钟

以下几种打包优化方案,都是实际项目中非常实用的,使用以下方案进行优化,通常都能使项目体积减小30%左右。

优化的基本思路:文件尽可能的小;多个小文件,代替一个大文件

Source Map

生产环境的代码通常都有被压缩混淆过,运行时若抛出异常,往往很难定位到具体位置,Source Map的出现就是为了解决这个问题。

Source Map 本质上是一个信息文件,里面储存着代码转换前后的对应位置信息,通过这些信息就能准确定位到异常。

生成Source Map会极大的降低打包速度。

生产环境可以考虑取消Source Map生成。如果要生成,应将服务器配置为不允许普通用户访问 Source Map 文件!

// vue.config.js
module.exports = {
   productionSourceMap: false // 关闭生产环境source map生成
}

组件懒加载

组件的代码默认会被分别打包至app.js和app.css文件中,组件越多,文件就会越大。

app.js与app.css文件会在首屏完成加载,过大的文件会降低加载速度,从而导致白屏时间过长。设置懒加载可以将组件的代码抽离出去,生成独立的文件,当使用到该组件时,才会加载对应的文件。

实际项目中使用最多的是路由懒加载。

懒加载需要抽离代码,生成文件,因此会降低打包速度。

webpackChunkName可以指定生成文件的名称。

const routes = [
    // ...route
    {
        path: '/about',
        name: 'About',
        component: () => import(/* webpackChunkName: 'about' */'./views/about.vue');
    }
];

按需加载

实际项目中通常会依赖许多的第三方包,全局引入的方式会将包的所有代码都打包到项目中,而实际上绝大多数代码都是不需要的,按需加载可以在构建过程中将不需要的代码剔除掉。

按需加载能大幅减小文件体积,可以作为主要的优化手段

按需加载ElementUI

首先,安装babel-plugin-component:

npm install babel-plugin-component -D

然后,将 babel.config.js 修改为:

module.exports = {
  presets: ["@vue/cli-plugin-babel/preset"],
  plugins: [
     // ...plugins
    [
      "component",
      {
        libraryName: "element-ui",
        styleLibraryName: "theme-chalk",
      },
    ],
  ],
};

接下来,创建一个element-ui-plugin.js文件,在这个文件中注册你需要使用的组件:

import { Button, Message } from 'element-ui';

export default {
    install: (Vue) => {
        Vue.use(Button);
        Vue.prototype.$message = Message;
    }
}

最后,在main.js中引入element-ui-plugin.js

import Vue from 'vue';
import ElementUiPlugin from './element-ui-plugin';

Vue.use(ElementUiPlugin);

CDN加速

项目中的第三方包,默认都会被打包到chunk-vendors.js中,因此该文件的体量往往会非常的大。

chunk-vendors.js会在首屏幕完成加载,过大的文件会降低加载速度,导致白屏时间过长。通过配置externals(外部扩展)可以将第三包的代码从chunk-vendors.js中剔除出去,运行时再从用户环境中获取这些扩展依赖。

外部扩展通常会指定为一个CDN链接,使用html-webpack-plugin可以很方便的将CDN链接注入到html文件中。

使用CDN时,一个稳定可靠的CDN服务是非常非常重要的。

// vue.config.js

// 定义外部扩展
const externals = {
    vue: "Vue",
};

// 定义CDN地址
const cdn = {
    css: [
        'https://unpkg.com/element-ui/lib/theme-chalk/index.css'
    ],
    js: [
        'https://unpkg.com/vue@2.6.14/dist/vue.runtime.min.js'
    ]
}

module.exports = {
    chainWebpack: (config) => {
        if (process.env.NODE_ENV === 'production') {
            config.externals(externals);
            // 使用html-webpack-plugin,输入CDN连接
            config
                .plugin('html').tap((args) => {
                    args[0].cdn = cdn;
                    return args;
                })
        }
    }
};

配置index.html文件:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <!-- 注入CSS -->
    <% for(let css of htmlWebpackPlugin.options.cdn.css) { %>
      <link rel="stylesheet" href="<%=css%>">
    <% } %>

    <!-- 注入JS -->
    <% for(let js of htmlWebpackPlugin.options.cdn.js) { %>
      <script src="<%=js%>"></script>
    <% } %>
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>

  <body>
    <div id="app"></div>
  </body>
</html>

拆分第三方模块

如果找不到稳定可靠的CDN服务,或者不想使用CDN,同时又希望将第三方包从chunk-vendors.js中抽离出来,可以使用splitChunks进行拆分,将依赖生成独立的文件。

注意:当文件过大时再考虑拆分,实际优化时需要在请求大小和请求数量之间做权衡。

// vue.config.js
module.exports = {
    // ...options

    chainWebpack: (config) => {
        // 生产环境配置
        if (process.env.NODE_ENV === 'production') {
            config.optimization
                .runtimeChunk("single")
                .splitChunks({
                    chunks: "all", // 对同步、异步引入的模块都进行分析
                    maxInitialRequests: Infinity, // 指定入口最大并行请求数,尽可能多的生成文件
                    minSize: 20000, // 指定生成文件的最小体积
                    cacheGroups: {
                        vendor: {
                            test: /[\\/]node_modules[\\/]/,
                            name: (module) => {
                                const packageName = module.context.match(
                                    /[\\/]node_modules[\\/](.*?)([\\/]|$)/
                                )[1];
                                // 指定第三方包输出文件名
                                return `${packageName.replace("@", "")}`;
                            }
                        }
                    }
                });
        }
    }
};

Gzip压缩

Gzip压缩,能极大的减轻文件的体积,加快传输效率,可以作为主要的优化手段。使用Gzip压缩需要对服务器进行配置,在不支持Gzip时,要将普通文件传输给客户端。

打包生产Gzip文件

首先,安装compression-webpack-plugin:

注意:如果使用最新版的vue-cli,推荐安装compression-webpack-plugin 5.0.0版本,否则在打包过程中会出现异常。

npm install compression-webpack-plugin@5.0.0 --save-dev

接下来,修改vue.config.js:

// vue.config.js
const CompressionWebpackPlugin = require("compression-webpack-plugin");

module.exports = {
    // ...options
    chainWebpack: (config) => {
        if (process.env.NODE_ENV === 'production') {
            // 注册Plugin
            config
                .plugin('Compression')
                .use(CompressionWebpackPlugin, [
                  {
                    include: 'src', // 指定src目录
                    test: /\.js$|\.html$|\.css$/, // 匹配src目录下所有js、html、css文件
                    minRatio: 1, // 只对压缩比率大于1的文件进行压缩,压缩比率 = 压缩后大小 / 原大小
                    threshold: 10240, // 压缩大于1kb的文件
                  },
                ])
        }
    }
};