Одна статья, чтобы получить стратегию оптимизации сборки веб-пакета

Webpack
Одна статья, чтобы получить стратегию оптимизации сборки веб-пакета

В предыдущих статьях мы узнали об основном использовании и принципах веб-пакета:

Когда проект становится все более сложным, он столкнется с проблемой, что объем файла будет медленным и построенным. Оптимизация сборки Webapck — это вещь, которую необходимо учитывать для крупных проектов.Здесь мы обсуждаем построение политик оптимизации по скорости и объему.

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

Перед оптимизацией нам нужно понять некоторые инструменты количественного анализа и использовать их, чтобы помочь нам проанализировать точки, которые необходимо оптимизировать.

webpackbar

webpackbar может отображать процесс упаковки в режиме реального времени во время упаковки. Конфигурация также очень проста, просто добавьте ее в массив плагинов:

const WebpackBar = require('webpackbar')
module.exports = {
  plugins: [
    ...
    new WebpackBar()
  ]
}

image.png

speed-measure-webpack-plugin

использоватьspeed-measure-webpack-pluginВы можете увидеть трудоемкую ситуацию каждого загрузчика и плагина.

Он немного отличается от использования обычных плагинов, он должен обернуть весь элемент конфигурации webpack своим методом wrap.

const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()
module.exports = smp.wrap({
  entry: './src/main.js',
  ...
})

После упаковки вывод информации в командную строку следующий, мы можем посмотреть какие загрузчики и плагины долго работают, а потом оптимизировать их.

image,.png

webpack-bundle-analyzer

webpack-bundle-analyzerНаглядным способом мы можем наглядно увидеть, какой контент модуля входит в упакованный пакет, и размер каждого модуля. На основе этой информации мы можем анализировать структуру проекта, корректировать конфигурацию упаковки и оптимизировать.

Добавьте плагин на массив плагинов. После завершения сборки по умолчанию будет вhttp://127.0.0.1:8888/Отобразите результаты анализа.

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

image.png webpack-bundle-analyzerРазмер файла модуля рассчитывается в трех случаях:

  • stat: исходный размер файла без преобразования
  • parsed: выходной размер файла после преобразования (например, преобразование babel-loader ES6-> ES5, сжатие UglifyJsPlugin и т. д.)
  • gzip: размер анализируемого файла после сжатия Gzip.

использоватьspeed-measure-webpack-pluginа такжеwebpack-bundle-analyzerсамо по себе также увеличит время упаковки (webpack-bundle-analyzerЭто занимает особенно много времени), поэтому рекомендуется использовать эти два плагина в анализе разработки и удалить их в производственной среде.

image.png

Оптимизировать скорость сборки

многопроцессная сборка

Webpack, работающий на Node.js, является однопоточным. Даже если несколько задач существуют одновременно, они могут быть поставлены в очередь на выполнение только одна за другой. Когда проект более сложный, сборка будет медленнее. Сегодня большинство ЦП являются многоядерными, и мы можем использовать некоторые инструменты, чтобы полностью реализовать преимущества ЦП в многоядерном параллелизме.

Более распространеныhappypack,thread-loader.

happypack

happypack может разбить задачу сборки на несколько подпроцессов для одновременного выполнения, а затем отправить результат в основной процесс после того, как подпроцессы его обработают. Конфигурация выглядит следующим образом: исходная конфигурация загрузчика передается в happyPack для обработки.

const Happypack = require('happypack')
module.exports = {
  module:{
    rules:[
      {
        test: /\.js$/,
        use: 'happypack/loader?id=babel'   //问号后面的查询参数指定了处理这类文件的HappyPack实例的名字
      },
    ]
  },
  plugins:[
    new Happypack({
      id: 'babel',     //HappyPack实例名,对应上面rules中的“id=babel”
      use: ['babel-loader']     //原本要使用的loader
    })
  ]
}

thread-loader

Автор happypack больше не поддерживает этот проект.После webpack4 вы можете использоватьthread-loader.

thread-loaderЕго очень просто использовать, просто поместите его перед другими загрузчиками, как показано ниже. размещены в этомthread-loaderПоследующие загрузчики будут работать в отдельном пуле рабочих процессов.

module.exports = {
  module:{
    rules:[
      {
          test: /\.js$/,
          use: ['thread-loader','babel-loader']
      }
    ]
  },
}

image.pngЕсли это небольшой проект, не рекомендуется запускать многопроцессную сборку, поскольку для запуска процесса требуется время, и скорость сборки будет ниже.

Использовать кеш

Использование кеша может повысить скорость вторичного построения (следующие сравнительные диаграммы посвящены скорости вторичного построения). После использования кеша в node_modules будет.cacheКаталог для хранения кэшированного контента.

cache-loader

Добавьте это перед некоторыми загрузчиками с высокой производительностью.cache-loader, чтобы кэшировать результаты на диск.

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: ['cache-loader','babel-loader']
      }
    ]
  }
}

Видно, что с помощьюcache-loaderПосле этого скорость сборки была значительно улучшена.

image.pngправильноbabel-loaderИспользовать кеш или нетcache-loaderпрямо вbabel-loaderдобавить после?cacheDirectory=true

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: ['babel-loader?cacheDirectory=true']
      }
    ]
  }
}

hard-source-webpack-plugin

hard-source-webpack-pluginИспользуется для включения кэширования модуля.

const HardSourceWebpackPlugin = require("hard-source-webpack-plugin")
module.exports = {
  plugins:[
    new HardSourceWebpackPlugin()
  ]
}

использоватьhard-source-webpack-pluginПосле второго здания, чтобы повысить скорость около 90%.

image.png

include/exclude

Обычно загрузчик обрабатывает все файлы, соответствующие правилам сопоставления. Например, babel-loader будет просматривать все js-файлы, используемые в проекте, компилировать и преобразовывать код каждого файла. Файлы js в node_modules в основном переведены и не нуждаются в повторной обработке, поэтому мы используем include/exclude, чтобы избежать этого ненужного перевода.

module.exports = {
  module:{
    rules:[
      {
          test: /\.js$/,
          use: ['babel-loader'],
          exclude: /node_modules/
          //或者  include: [path.resolve(__dirname, 'src')]
      }
    ]
  },
}

Включить напрямую указывает папку поиска, что более эффективно, чем исключение, и может повысить скорость сборки.image.png

динамически подключаемая библиотека

Вышеупомянутый загрузчик babel может избежать обработки сторонних библиотек в node_modules через include/exclude.

Однако, если код сторонней библиотеки и бизнес-код упакованы в пакетный файл, то подключаемые модули, обрабатывающие этот пакетный файл, такие как uglifyjs-webpack-plugin, terser-webpack-plugin и т. д., не имеют возможности не обрабатывать содержимое сторонней библиотеки внутри.

На самом деле, сторонний библиотечный код в основном созревает, какой процесс нет. Следовательно, мы можем проецировать сторонний библиотечный код разделен.

Общее лечение тремя способами:

  1. Externals
  2. SplitChunks
  3. DllPlugin

Внешние файлы могут не иметь дело со сторонними библиотеками, но каждая сторонняя библиотека должна быть введена путем добавления тега скрипта в html-документ. использовать сторонние библиотеки Неразумно вводить небольшое количество функций полностью тегами скрипта.

SplitChunks перестраивает стороннюю библиотеку каждый раз при ее сборке, что не может эффективно повысить скорость сборки.

Рекомендуется использовать DllPlugin и DLLReferencePlugin (используются вместе), которые являются встроенными плагинами webpack. DllPlugin отдельно упакует редко обновляемые сторонние библиотеки, когда версии этих сторонних библиотек не менялись, пересборка не требуется.

Инструкции:

  1. Используйте DllPlugin для упаковки сторонних библиотек
  2. Используйте DLLReferencePlugin для ссылки на manifest.json, чтобы связать пакет, который был введен на шаге 1.
  • Сначала создайте файл конфигурации webpackwebpack.dll.jsДля упаковки сторонних библиотек (шаг 1)
const path = require('path')
const webpack = require('webpack')

module.exports = {
  mode: 'production',
  entry: {
    three: ['three', 'dat.gui']   // 第三方库数组
  },
  output: {
    filename: '[name].dll.js',    //[name]就是在entry
    path: path.resolve(__dirname, 'dist/lib'),
    library: '[name]'
  },
  plugins: [
    new webpack.DllPlugin({
      name: '[name]',
      path: path.resolve(__dirname, 'dist/lib/[name].json') //manifest.json的存放位置
    })
  ]
}

После упаковки вы можете увидеть, что вdistПапка lib добавляется в каталог.image.png

  • Тогда мыwebpack.base.jsВнесите некоторые изменения, чтобы связать пакет, который был введен на шаге 1 (шаг 2).
module.exports = {
  plugins:[
    //修改CleanWebpackPlugin配置
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: [  
        '!lib/**'              //在每次清楚dist目录时,不清理lib文件夹的内容
      ]
    }),
    // dll相关配置
    new webpack.DllReferencePlugin({    
      // 将manifest字段配置成我们第1步中打包出来的json文件
      manifest: require('./dist/lib/three.json')  
    })
  ]
}

После пакет снова видно, по сравнению с началом объем всего проекта 9.11мб, объем уменьшен на 90%, т.к. это многостраничный пакет(Конфигурация многостраничного пакета), каждая страница ссылается на громоздкийthree.js核心文件, берем наибольший объемthree.js核心文件После извлечения из пакета каждой страницы размер пакета значительно уменьшается.

image.png

Посмотрим на время сборки: по сравнению с тем, что было до использования DllPlugin, время сократилось на 30%.image.png

DllPlugin может отделять не только сторонние библиотеки, но и базовые библиотеки в бизнес-коде.

Оптимизированный объем сборки

разделение кода

Разделите базовую библиотеку на стороннюю библиотеку и бизнес-код, избегая единого размера bundle.js, время загрузки слишком велико. И в многостраничной конструкции это также может уменьшить упаковку.

Обычная операция осуществляется черезSplitChunks(В предыдущей статье подробно написано:SplitChunks)а такжединамически подключаемая библиотека(Как показано выше) и не будет повторяться здесь.

Динамический импорт

Роль динамического импорта в основном заключается в уменьшении объема ресурсов выше сгиба, а ресурсы, которые не находятся выше сгиба, будут запрашиваться при их использовании, тем самым повышая скорость загрузки выше сгиба. Типичным примером является управление маршрутизацией для одностраничных приложений (таких какvue-router)

{
  path: '/list',
  name: 'List',
  component: () => import('../views/List.vue')  
},

не импортировать компоненты напрямую (import List from '../views/List.vue'), который упакует компоненты в один и тот же пакет. Вместо этого импортируйте компоненты динамически,любой пропускimport()Ссылочные модули упакованы в отдельные пакетыКогда вы используете его, вы загрузите его. Для функциональных комплексов не рекомендуется использовать динамический ИМПОРТ.

<span @click="loadModal">show弹窗</span>
/***
methods: {
  loadModal(){
     import('../modal/index.js')
  }
}
***/

treeShaking

Используйте синтаксис импорта/экспорта ES6 и импортируйте и экспортируйте свой код, используя следующие методы вместо экспорта по умолчанию.

// util.js 导出
export const a = 1
export const b = 2
export function afunc(){}
或
export { a, b, afunc }

// index.js 导入
import { a, b } from './util.js'
console.log(a,b)

затем вmode:productionпроизводственной среде, он автоматически откроетсяtree-shaking, удалите неиспользуемый код, в приведенном выше примереafunc函数не будет упакован в комплект.

сжатие кода

Обычно используемые плагины сжатия кода js:uglifyjs-webpack-pluginа такжеterser-webpack-plugin.

В WebPack4 сжатие кода включается по умолчанию в производственных условиях. Мы также можем настроить на себя, чтобы переопределить конфигурацию по умолчанию для завершения более индивидуальных требований.

До версии 4.26.0 встроенный плагин сжатия веб-пакета был uglifyjs-webpack-plugin, начиная с версии 4.26.0 он был заменен на uglifyjs-webpack-plugin.terser-webpack-plugin. Здесь мы такжеterser-webpack-pluginНапример, в отличие от использования обычных плагинов, вoptimization.minimizerНастройте плагин сжатия в

const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
  optimization: {
    minimizer: [  
      new TerserPlugin({
        parallel: true,  //开启并行压缩,可以加快构建速度
        sourceMap: true, //如果生产环境使用source-maps,则必须设置为true
      })
    ]
  }
}

image.png

Спрайт

Карта спрайтов объединяет несколько маленьких значков в большое изображение.В среде HTTP1.x карта спрайтов может уменьшить HTTP-запросы и увеличить скорость отображения веб-страниц.

Иконка, используемая для синтеза изображения спрайта, должна быть небольшого размера, и не рекомендуется вставлять большее изображение в изображение спрайта, в то же время, если это статическая иконка сайта, она не является иконкой динамически. получен через ajax-запрос. Поэтому он обычно используется в качестве логотипа веб-сайта, значка и других изображений.

Во время разработки пользовательский интерфейс может предоставить изображение спрайта, но каждый раз, когда добавляется иконка, ее нужно создавать заново и пересчитывать смещение, что хлопотно. Синтезируя изображение спрайта через плагин webpack, вы можете напрямую использовать одну маленькую иконку во время разработки.При упаковке изображение спрайта автоматически синтезируется, а css в css автоматически модифицируется.background-positionценность .

Ниже мы используемpostcss-spritesавтоматически синтезировать изображения спрайтов.

Первый вwebpack.base.jsСредняя конфигурацияpostcss-loader:

//webpack.base.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['vue-style-loader','css-loader', 'postcss-loader']  //配置postcss-loader
      },
      {
        test: /\.less$/,
        use: [
          'vue-style-loader','css-loader', 'postcss-loader', 'less-loader']  //配置postcss-loader
      }
    ]
  }
};

Затем создайте новый в корневом каталоге проекта..postcssrc.js, настроитьpostcss-sprites.

module.exports = {
  "plugins": [
    require('postcss-sprites')({
      // 默认会合并css中用到的所有静态图片
      // 使用filterBy指定需要合并的图片,比如这里这里只合并images/icon文件夹下的图片
      filterBy: function (image) {
        if (image.url.indexOf('/images/icon/') > -1) {
            return Promise.resolve();
        }
        return Promise.reject();
      }
    })
  ]
}

По умолчанию изображения будут объединены вsprite.pngиллюстрация спрайта.

Непосредственно укажите маленькую иконку в качестве фона в css:

.star{
  display: inline-block;
  height: 100px;
  width: 100px;
  &.l1{
    background: url('../icon/star.png') no-repeat;
  }
  &.l2{
    background: url('../icon/star2.png') no-repeat;
  }
  &.l3{
    background: url('../icon/star3.png') no-repeat;
  }
}

После завершения упаковки вы можете увидеть, что она автоматически модифицируется.background-imageа такжеbackground-position.

image.png image.png image.png

gzip

Принцип gzip, ссылкаИзучение секретов сжатия gzip в HTTP-транспорте

Включите сжатие gzip, чтобы уменьшить размер файла. Когда браузер поддерживает gzip, он может ускорить загрузку ресурсов. И сервер, и клиент могут выполнять сжатие gzip, сервер сжимает при ответе на запрос, а клиент сжимает при сборке приложения. Однако процесс сжатия файлов требует времени и ресурсов процессора, а большое количество требований к сжатию увеличивает нагрузку на сервер.

Таким образом, вы можете генерировать сжатые файлы gzip при сборке и упаковке, размещать их на сервере как статические ресурсы и возвращать сжатые файлы сразу после получения запроса.

Использование webpack для создания файлов gzip требует помощиcompression-webpack-plugin, используя конфигурацию следующим образом:

const CompressionWebpackPlugin = require("compression-webpack-plugin")
module.exports = {
  plugins: [
     new CompressionWebpackPlugin({
       test: /\.(js|css)$/,         //匹配要压缩的文件
       algorithm: "gzip"
     })
  ]
}

image.png

адрес проекта: GitHub.com/Alxa О Лала/Я…