¿Cómo optimizar su proyecto vue-cli?

prefacio

En el desarrollo diario lo que más se nota es el tiempo de compilación y empaquetado del proyecto, sobre todo cuando se empaqueta y despliega con más frecuencia, este tiempo es muy largo.

Por ejemplo: el proceso de compilación tomó alrededor de 86 segundos para "arrancar en frío" un proyecto que se tomó antes:

¿Se puede tolerar esto? Obviamente no, por lo que planeo optimizarlo, de lo contrario, afectará demasiado la experiencia de desarrollo. El siguiente es un procesamiento realizado en el proceso de optimización.

Analice los módulos que consumen mucho tiempo

Ver la configuración integrada de vue-cli

El usuario vue-cli-service inspectpuede ver fácilmente el contenido de configuración incorporado de vue-cli , ingrese: en la terminal , y el archivo vue inspect --mode production webpack.config.production.jsproyecto y src .webpack.config.production.js

imagen.png

herramienta de análisis

Para obtener módulos que consumen mucho tiempo, debe utilizar algunas herramientas de análisis, como:

velocidad-medida-webpack-plugin

Este complemento puede medir la velocidad de compilación de los paquetes web y generar el tiempo de compilación de cada módulo, lo que puede ayudarnos a encontrar mejor los módulos que consumen mucho tiempo.

Configurar en vue.config.js

const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
module.exports = {
  ...,
  configureWebpack: (config) => {
      ...
      config.plugins.push(
        new SpeedMeasurePlugin(),
      );
    },
};
复制代码

Reiniciar

A partir de los resultados de salida en la figura a continuación, es fácil ver los módulos correspondientes que consumen mucho tiempo.

analizador de paquete webpack

Este complemento visualiza el tamaño de los archivos de salida del paquete web y proporciona un mapa de árbol interactivo con zoom.

Configurar en vue.config.js

  const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
+ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
  ...,
  configureWebpack: (config) => {
      ...
      config.plugins.push(
        new SpeedMeasurePlugin(),
      + new BundleAnalyzerPlugin()
      );
    },
};
复制代码

Reiniciar

A partir de los resultados de salida en la figura a continuación, puede observar fácilmente el tamaño empaquetado de cada módulo.

mejoramiento

cargador de subprocesos: habilita la optimización de subprocesos múltiples

De acuerdo con el tiempo de compilación generado por speed-measure-webpack-plugin , el tiempo de compilación de los siguientes cargadores es relativamente grande:

  • vue-loader
  • cargador ts
  • cargador de babel
  • image-webpack-cargador
  • postcss-loader

关于这一部分就可以通过 thread-loader 进行优化,因为它能将这些非常耗时的内容单独放到另一个线程中执行,但并不是针对所有的 loader 都做这个处理,因为这个处理本身也是有较大的开支。

注意:仅在耗时的操作中使用 thread-loader,否则使用 thread-loader 会后可能会导致项目构建时间变得更长,因为每个 worker 都是一个独立的 node.js 进程,其开销大约为 600ms 左右,同时还会限制跨进程的数据交换等。

在回顾下前面,没有使用 thread-loader 前项目的 "冷启动" 时间约 86 秒:

只针对 babel-loader 使用 thread-loader 后项目的 "冷启动" 时间约 78 秒:

在对其他 laoder 使用 thread-loader 发现时间更长,因此取尝试后的最优结果

hard-source-webpack-plugin —— 使用缓存优化

webpack 中几种缓存方式:

以上这些缓存方式都有首次启动时的开销,即它们会让 "冷启动" 时间会更长,但是二次启动能够节省很多时间.

vue-cli 已经内置了 cache-loaderbabel-loader 的 cacheDirectory 标志,其中对应的配置如下:

默认情况下的 "二次启动" 时间约为 38 秒,原因是 "冷启动" 时已经将 babel-laoder、ts-loader、vue-loader 进行了缓存:

imagen.png

先配置 hard-source-webpack-plugin 插件:

  const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
+ const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
module.exports = {
  ...,
  configureWebpack: (config) => {
      ...
      config.plugins.push(
        // 为模块提供中间缓存,缓存路径是:node_modules/.cache/hard-source
        // 解决未检测到的配置更改
       + new HardSourceWebpackPlugin({
          root: process.cwd(),
          directories: [],
          environmentHash: {
            root: process.cwd(),
            directories: [],
            // 配置了files 的主要原因是解决配置更新,cache 不生效了的问题
            // 配置后有包的变化,plugin 会重新构建一部分 cache
            files: ['package.json', 'yarn.lock']
          }
        }),
        new SpeedMeasurePlugin(),
        new BundleAnalyzerPlugin(),
      );
    },
};
复制代码

使用 hard-source-webpack-plugin 后的 "冷启动" 和 "二次启动" 时间如下:

imagen.png

减少打包体积

减少 js 代码体积

在打包之后的 dist 目录下,查找对应的 console.log,结果如下:

imagen.png

可以发现 vue-cli 中默认的配置并没有将 js 文件中的一些 console.log 语句进行删除,因此这也是一些可以继续优化的内容.

这里可以通过 uglifyjs-webpack-plugin terser-webpack-plugin 插件来删除注释和压缩 js 代码,具体配置可以直接点链接进行查阅.

对图片压缩

针对一些对图片像素没有很高要求的图片资源进行压缩,这里可以通过 image-webpack-pluginimage-minimizer-webpack-plugin 进行图片资源的压缩,具体配置可以直接点链接进行查阅.

外部扩展 —— externals & cdn

externals 选项就是用于 防止 将某些 import 的包(package) 打包到 bundle 中,而是在运行时(runtime) 再去从外部获取这些 扩展依赖(external dependencies),具体可见

简单来说就是原本应该要被打包到 bundle 中的 js,现在通过配置 externals 选项,将它做外 bundle 之外的资源,即 cdn 资源,在代码运行时再去请求这个资源。

例如,下面就是在项目关于 externals 的配置:

 chainWebpack: (config) => {
    ...
    // 通过 CDN 方式引入资源
      config.externals({
        echarts: 'echarts',
        nprogress: 'NProgress',
      });
 }
复制代码

DllPlugin 插件 —— 优化打包时间

DllPlugin 插件负责将那些比较稳定(例如 vue/react 全家桶)的库进行打包拆分 bundles,下次在打包时就不需要重复进行打包,因此大幅度提升了构建的速度。

webpack4 版本中,已经集成 DllPlugin 插件,我们只需要进行配置即可,具体可见

  • 创建 dll.js 文件,进行简单配置
const path = require('path');
const webpack = require('webpack');

module.exports = {
  entry: {
    vendor: ['echarts', 'element-ui', 'vue/dist/vue.esm.js', 'vue-router', 'vuex'],
  },
  output: {
    path: path.join(__dirname, 'target'),
    filename: '[name].js',
    library: '[name]_[hash]',
  },
  plugins: [
    new webpack.DllPlugin({
      // DllPlugin的name属性需要和libary保持一致
      name: '[name]_[hash]',
      //指定当前目录
      path: path.join(__dirname, '.', '[name]-manifest.json'),
      // context需要和webpack.config.js保持一致
      context: __dirname,
    }),
  ],
};
复制代码
  • package.json 文件中配置 script 脚本:"dll": "webpack --config ./dll.js"
  • 安装 webpack-cli,因为原本依赖中并没有安装过这个依赖,而运行这个脚本命令需要 webpack-cli
  • 执行脚本命令 npm run dll,生成 vendor-manifest.json 文件,这个文件是用于让 DllReferencePlugin 能够映射到相应的依赖上
  • vue.config.js 中配置 DllReferencePlugin 插件,链接到已被打包的依赖上
const { pathResolve } = require('./build/utils.js'); // eslint-disable-line
const devConfig = require('./build/webpack.dev.conf.js'); // eslint-disable-line
const buildConfig = require('./build/webpack.prod.conf.js');
// 分析工具
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

// 资源缓存
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');

// 抽离稳定的第三方库,避免重复打包
const DllReferencePlugin = require('webpack').DllReferencePlugin;

// 公共函数
const { versionSet } = require('./build/utils'); // eslint-disable-line

// 是否为开发环境
const isDevelopment = process.env.NODE_ENV == 'development';

const vueWebpackConfig = () => {
  let envConfig = {};

  if (isDevelopment) {
    // 开发
    envConfig = devConfig;
  } else {
    // 构建
    versionSet();
    envConfig = buildConfig;
  }

  const vueConfig = {
    // 环境配置
    ...envConfig,
    productionSourceMap: isDevelopment, // 是否在构建生产包时生成sourcdeMap

    // 拓展webpack配置
    chainWebpack: (config) => {
      //  ============ 配置别名 ============
      config.resolve.alias
        .set('@build', pathResolve('../build')) // 构建目录
        .set('@', pathResolve('../src'))
        .set('@api', pathResolve('../src/api'))
        .set('@utils', pathResolve('../src/utils'))
        .set('@views', pathResolve('../src/views'));

      // ============ svg处理 ============
      const svgRule = config.module.rule('svg');
      // 清除已有的所有 loader。
      // 如果你不这样做,接下来的 loader 会附加在该规则现有的 loader 之后。
      svgRule.uses.clear();

      // 添加要替换的 loader
      svgRule.use('svg-sprite-loader').loader('svg-sprite-loader').options({
        symbolId: 'icon-[name]',
      });

      // ============ 压缩图片 ============
      config.module
        .rule('images')
        .use('image-webpack-loader')
        .loader('image-webpack-loader')
        .options({ bypassOnDebug: true })
        .end();

      // ============ 打包分析工具 ============
      if (!isDevelopment) {
        if (process.env.npm_config_report) {
          config.plugin('webpack-bundle-analyzer').use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin).end();
          config.plugins.delete('prefetch');
        }
      }

      // ============ CDN资源引入 ============
      config.externals({
        // echarts: 'echarts',
        nprogress: 'NProgress',
      });
    },

    configureWebpack: (config) => {
      // 尽量保证项目中文件后缀的精确
      config.resolve.extensions = ['.ts', '.js', '.vue', '.json'];

      // 处理 babel-loader
      config.module.rules[12].use.unshift({
        loader: 'thread-loader',
      });

      config.plugins.push(
        // 为模块提供中间缓存,缓存路径是:node_modules/.cache/hard-source
        new HardSourceWebpackPlugin({
          root: process.cwd(),
          directories: [],
          environmentHash: {
            root: process.cwd(),
            directories: [],
            // 配置了files 的主要原因是解决配置更新,cache 不生效了的问题,配置后有包的变化,plugin 会重新构建一部分cache
            files: ['package.json', 'yarn.lock'],
          },
        }),

        // DllReferencePlugin 插件
          new DllReferencePlugin({
            context: __dirname,
            // manifest就是我们第 2 步中打包出来的 json 文件
            manifest: require('./vendor-manifest.json'),
          }),

        // 分析工具
        new SpeedMeasurePlugin(),
        new BundleAnalyzerPlugin(),
      );
    },
  };

  return vueConfig;
};

module.exports = vueWebpackConfig();
复制代码

其他优化

resolve.alias & resolve.extensions

resolve.alias 是用于创建 import 或 require 的别名,来确保模块引入变得更简单

例如,下面是项目中定义的一些别名:

   chainWebpack: (config) => {
      // 配置别名
      config.resolve.alias
        .set('@build', pathResolve('../build')) // 构建目录
        .set('@', pathResolve('../src'))
        .set('@api', pathResolve('../src/api'))
        .set('@utils', pathResolve('../src/utils'))
        .set('@views', pathResolve('../src/views'));
  }
复制代码

resolve.extensions 指定为对应的文件后缀,保证在查找模块时的无用查找和递归等,即保证这个配置里的文件后缀要尽可能少。

image.png

减少不必要的解析 —— module.noParse

module.noParse 是防止 webpack 解析那些任何与给定正则表达式相匹配的文件,忽略的文件中 不应该含有 importrequiredefine 的调用,或任何其他导入机制,通过忽略大型的 library 可以提高构建性能。

例如,vue-cli 中关于 module.noParse 的配置如下:

代码层面优化

webpack-bundle-analyzer 反应的内容,可以发现某些 jscss 模块体积相对来说比较大,这个时候可以找到对应的文件梳理逻辑并进行代码优化,如封装 js 逻辑、抽离 css 样式等,即最基本的优化就是少书写重复的样式和逻辑,这样也能避免一些无效的重复编译.

最后

Las anteriores son algunas optimizaciones basadas en vue-cli (basadas en webapck), si no ha realizado la optimización correspondiente antes, puede probarla.

Finalmente, echemos un vistazo a los cambios en el tiempo de construcción del empaque de producción antes y después de la optimización:

image.png

image.png

Supongo que te gusta

Origin juejin.im/post/7078491632605069348
Recomendado
Clasificación