Оптимизация упаковки проекта Vue cli, это все, что я могу сделать

Vue.js
Оптимизация упаковки проекта Vue cli, это все, что я могу сделать

задний план

Поскольку передние проекты компании длинные вспомогательные, холодное начало составляет приблизительно 5 мин, а местная упаковка строительства составляет приблизительно 5 минут, а онлайн Tencent Cloud Docker строит полный процесс от 1 до 20 минут. Поэтому не только значительно повлиял на эффективность развития, но также отложил время, доставляемое к тесту.

Идеи оптимизации

  • Понять текущую конфигурацию веб-пакета проекта
  • Связанные оптимизации сборки
  • Оптимизация объема упаковки
  • Оптимизация, связанная с Docker

инструмент для анализа

speed-measure-webpack-plugin

представлять:

speed-measure-webpack-plugin npm

The first step to optimising your webpack build speed, is to know where to focus your attention. This plugin measures your webpack build speed, giving an output like this:

Благодаря анализу вывода smp мы можем четко понять время, затрачиваемое на работу загрузчика и плагина на каждом этапе процесса создания веб-пакета.

Как использовать:

# Yarn
yarn add -D speed-measure-webpack-plugin

const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
module.exports = {
  chainWebpack: config => {
    config
      .plugin('speed-measure-webpack-plugin')
      .use(SpeedMeasurePlugin)
      .end()
  }
}

webpack-bundle-analyzer

представлять:

webpack-bundle-analyzer npm

Как использовать:

# Yarn
yarn add -D webpack-bundle-analyzer

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}

vue-cli-service build --report
or
vue-cli-service build --report-json

Посмотреть текущую конфигурацию WebPack Vue-Cli

представлять

Леса VUE-CLI будет иметь много поведения по умолчанию WebPack, поэтому мы должны знать, на что настроен текущий веб-пакет для проектов на основе VUE-CLI, а затем мы можем сделать целевой анализ и оптимизацию.

vue-cli-serviceнезащищенныйinspectКоманда используется для просмотра проанализированной конфигурации веб-пакета. этот глобальныйvueИсполняемые файлы также предоставляютinspectкоманда, эта команда просто помещаетvue-cli-service inspectПрокси в ваш проект.

Как использовать:

#根据mode,分别生成开发环境、生产环境的配置
vue inspect --mode production > output.js
#输入命令后,在根目录会生产一个output.js文件

еслиvue command not foundВы можете установить и зарегистрировать команду vue глобальноnpm install -g vue-cli

попытка оптимизации

hard-source-webpack-plugin плюс кеш

представлять

hard-source-webpack-plugin npm

HardSourceWebpackPlugin is a plugin for webpack to provide an intermediate caching step for modules. In order to see results, you'll need to run webpack twice with this plugin: the first build will take the normal amount of time. The second build will be signficantly faster.

При запуске проекта для проекта будет создан кеш.Если в проекте нет пакета или других изменений, кеш будет повторно использоваться напрямую, не тратя время на пересборку в следующий раз.

Как использовать:

#yarn
yarn add -D hard-source-webpack-plugin

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

Уведомление:

Could not freeze : Cannot read property 'hash' of undefinedПосле удаления node_modules/.cache и перезапуска проекта причиной этой проблемы может быть ошибка, возникающая при компиляции при асинхронной загрузке модуля, или ее можно решить, добавив следующее:

new HardSourceWebpackPlugin.ExcludeModulePlugin([
 {
   // HardSource works with mini-css-extract-plugin but due to how
   // mini-css emits assets, assets are not emitted on repeated builds with
   // mini-css and hard-source together. Ignoring the mini-css loader
   // modules, but not the other css loader modules, excludes the modules
   // that mini-css needs rebuilt to output assets every time.
   test: /mini-css-extract-plugin[\\/]dist[\\/]loader/
 }
])

Сузить область анализа извлечения файлов

Во избежание бесполезного поиска и рекурсивного обхода можно использовать псевдоним для указания модуля во время обращения, noParse и не парсить сторонние зависимости, не зависящие от нативного кода.

// 定义getAliasPath方法,把相对路径转换成绝对路径
const getAliasPath = dir => join(__dirname, dir)
module.exports = {
	configureWebpack: config => {
    config.module.noParse = /^(vu|vue-router|vuex|vuex-router-sync|lodash|echarts|axios|element-ui)$/
  }
  chainWebpack: config => {
    // 添加别名
    config.resolve.alias
      .set('@', getAliasPath('src'))
      .set('assets', getAliasPath('src/assets'))
      .set('utils', getAliasPath('src/utils'))
      .set('views', getAliasPath('src/views'))
      .set('components', getAliasPath('src/components'))
	}
  // 生产环境禁用eslint
  lintOnSave: !process.env.NODE_ENV !== 'production',
}

Уменьшить размер упаковки

image-webpack-плагин сжатия изображений

Требования к пикселям изображения не очень велики, это сжатие все еще можно использовать, и степень сжатия не кажется сильно отличающейся невооруженным глазом. Обратите внимание, что я не сжимал svg, причина в том, что когда сжатый svg упаковывается в base64 во время сборки, сгенерированный base64 будет иметь проблемы и будет недоступен.

module.exports = {
  chainWebpack: config => {
   // 对图片进行压缩
    config.module
      .rule('images')
      .test(/\.(png|jpe?g|gif)(\?.*)?$/)
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({ bypassOnDebug: true })
      .end()
	}
}

UglifyJsPlugin удаляет комментарии консоли (не рекомендуется)

#yarn
yarn add -D uglifyjs-webpack-plugin

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
	configureWebpack: config => {
  	config.plugin.push(
    	new UglifyJsPlugin({
        uglifyOptions: {
          // 删除注释
          output: {
            comments: false
          },
          // 删除console debugger 删除警告
          compress: {
            warnings: false,
            drop_console: true, //console
            drop_debugger: false,
            pure_funcs: ['console.log'] //移除console
          }
        },
        sourceMap: false,
        parallel: true //使用多进程并行运行来提高构建速度。默认并发运行数:os.cpus().length - 1。
      })
    )
  }
}

Terser Удалить консоль

terser все еще поддерживается, а UglifyJs не поддерживается, terser намного мощнее, чем последний

chainWebpack: config => {
config.when(isProd, config => {
  config.optimization.runtimeChunk('single')
  // 配置删除 console.log
  config.optimization.minimizer('terser').tap(args => {
    // remove debugger
    args[0].terserOptions.compress.drop_debugger = true
    // 移除 console.log
    args[0].terserOptions.compress.pure_funcs = ['console.log']
    // 去掉注释 如果需要看chunk-vendors公共部分插件,可以注释掉就可以看到注释了
    args[0].terserOptions.output = {
      comments: false
    }
    return args
  })
})
}

Библиотека динамической компоновки DLL

Этот плагин создает пакет только для dll (dll-only-bundle) в дополнительной отдельной настройке веб-пакета. Этот подключаемый модуль создаст файл с именем manifest.json, который используется для сопоставления подключаемого модуля DLLReferencePlugin с соответствующими зависимостями.

Это можно просто понять как разделение некоторых зависимостей из пакета проекта и загрузка их запросами через отношения сопоставления. Я думаю, что он не будет анализироваться в проекте после его разделения, поэтому он полезен для построения и объема.

Настройку DllPlugin можно разделить на следующие шаги:

  1. Создайте новый файл webpack.dll.config.js (можно использовать другие имена) и настройте плагины, которые необходимо разделить;
  2. Создайте новую команду в файле package.json для упаковки,"build:dll":"webpack --config webpack.dll.config.js";выполнить команду;
  3. Настроить в файле vue.config.jsDllReferencePlugin, в основном относятся к dll к зависимостям, которые нужно прекомпилировать;
  4. Вручную ввести пакет разделенного пакета в index.html (лучше было бы выложить его в cdn)

Установить:

#yarn 
yarn add webpack-cli@^3.2.3 add-asset-html-webpack-plugin@^3.1.3 clean-webpack-plugin@^1.0.1 --dev
// webpack.dll.config.js
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path')
const webpack = require('webpack')
const CleanWebpackPlugin = require('clean-webpack-plugin')
// dll文件存放的目录
const dllPath = 'public/vendor'

module.exports = {
  entry: {
    // 需要提取的库文件
    vendor: ['vue', 'vue-router', 'vuex'],
    utils: ['axios', 'lodash']
  },
  output: {
    path: path.join(__dirname, dllPath),
    filename: '[name].dll.js',
    // vendor.dll.js中暴露出的全局变量名
    // 保持与 webpack.DllPlugin 中名称一致
    library: '[name]_[hash]'
  },
  plugins: [
    // 清除之前的dll文件
    new CleanWebpackPlugin(['*.*'], {
      root: path.join(__dirname, dllPath)
    }),
    // manifest.json 描述动态链接库包含了哪些内容
    new webpack.DllPlugin({
      path: path.join(__dirname, dllPath, '[name]-manifest.json'),
      // 保持与 output.library 中名称一致
      name: '[name]_[hash]',
      context: process.cwd()
    })
  ]
12

существуетvue.config.js pluginиспользуется в

config.plugin.push(
  new DllReferencePlugin({
    context: process.cwd(),
    manifest: require('./public/vendor/vendor-manifest.json')
  }),
    new DllReferencePlugin({
    context: process.cwd(),
    manifest: require('./public/vendor/utils-manifest.json')
  }),
    // 将 dll 注入到 生成的 html 模板中
    new AddAssetHtmlPlugin({
    // dll文件位置
    filepath: getPath('./public/vendor/*.js'),
    // dll 引用路径
    publicPath: './vendor',
    // dll最终输出的目录
    outputPath: './vendor'
  })
)

Код сегментации splitChunks

split-chunks-plugin webpack

  • minChunks: представляет количество цитирований, по умолчанию 1.
  • maxAsyncRequests: максимальное количество параллельных запросов при загрузке по запросу, по умолчанию 5
  • maxInitialRequests: максимальное количество параллельных запросов для записи, по умолчанию 3
  • AutomaticNameDelimiter: Именованный разделитель
  • Имя: разделить имя блока, по умолчанию автоматически генерируется имя блока и значение HASH
  • cacheGroups: группы кэширования. Помимо всех вышеперечисленных атрибутов, к атрибутам группы кеша относятся test, priority, reuseExistingChunk
    • test: используется для контроля того, какие модули соответствуют этой группе кеша
    • приоритет: приоритет упаковки группы кеша
    • reuseExistingChunk: если модуль, содержащийся в текущем блоке кода, уже существует, новый блок кода больше не создается.
config.optimization = {
  runtimeChunk: 'single',
  splitChunks: {
    chunks: 'all', // 表示哪些代码需要优化,有三个可选值:initial(初始块)、async(按需加载块)、all(全部块),默认为async
    maxInitialRequests: Infinity, // 按需加载时候最大的并行请求数,默认为5
    minSize: 30000, // 依赖包超过300000bit将被单独打包
    // 缓存组
    // priority: 缓存组打包的先后优先级
    // minChunks: 表示被引用次数,默认为1
    cacheGroups: {
      //公共模块
      commons: {
        name: 'chunk-commons',
        test: resolve('src'), // can customize your rules
        minSize: 100, //大小超过100个字节
        minChunks: 3, //  minimum common number
        priority: 5,
        reuseExistingChunk: true
      },
      // 第三方库
      libs: {
        name: 'chunk-libs',
        test: /[\\/]node_modules[\\/]/,
        priority: 10,
        chunks: 'initial', // only package third parties that are initially dependent
        reuseExistingChunk: true,
        enforce: true
      },
      echarts: {
        name: 'chunk-echarts',
        test: /[\\/]node_modules[\\/]echarts[\\/]/,
        chunks: 'all',
        priority: 12,
        reuseExistingChunk: true,
        enforce: true
      }
    }
  }
}

gzip-пакет сжатия-webpack-plugin

Применение:Уууу, эта лошадь plus.com/package/com…

const CompressionWebpackPlugin = require("compression-webpack-plugin");

const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV);
const productionGzipExtensions = /.(js|css|json|txt|html|ico|svg)(?.*)?$/i;

module.exports = {
  configureWebpack: config => {
    const plugins = [];
    if (IS_PROD) {
      plugins.push(
        new CompressionWebpackPlugin({
          filename: "[path].gz[query]",
          algorithm: "gzip",
          test: productionGzipExtensions,
          threshold: 10240,
          minRatio: 0.8
        })
      );
    }
    config.plugins = [...config.plugins, ...plugins];
  }
};

terser-webpack-plugin многопоточное сжатие js

Применение:woohoo. Эта лошадь Plus.com/package/fetus...

externals & cdn

Параметр конфигурации externals обеспечивает способ «исключен из выходного пакета, зависящего от». Недопущение импорта некоторых пакетов (пакетов) запакованных в бандл, но (время выполнения) опять же зависит от получения этих расширений (внешних зависимостей) работающих извне.

Это свойство простое для понимания и очень удобное в использовании, очень приятно!Самый простой способ это настроить имя,конечно можно и написать какие-то сложные конфигурацииофициальная документация

//vue.config.js
...
configureWebpack:{
	externals: {
      "vue": "Vue",
      "element-ui": "ELEMENT"
    },
}
// 然后在 index.html 手动cdn引入(或者用插件自动添加)

Суммировать

Выше приведены некоторые небольшие оптимизации, которые я сделал в проекте компании, которые могут быть неприменимы к другим проектам и даже не оптимальны в использовании, но они все же в значительной степени полезны для этого проекта. Чтобы узнать больше об оптимизации веб-пакетов, позвольте мне прикрепить карту связей. (Только для обучения, записывайте 📝)

разное

Vue CLI

настроить параметры Webpack в vue-cli

Как развернуть фронтенд-проекты с докером