Позвольте вам подробно разблокировать серию Webpack (оптимизация)

внешний интерфейс JavaScript
Позвольте вам подробно разблокировать серию Webpack (оптимизация)

Подробно разблокируйте серию Webpack (базовая)а такжеПодробно разблокируйте серию Webpack (дополнительно), в основном для объясненияWebpackКонфигурация, но поскольку проект становится больше и больше, скорость сборки может стать медленнее и медленнее, а объем построенного js также станет больше и больше. В это время надоWebpackконфигурация оптимизирована.

В этой статье перечислены более десяти методов оптимизации. Вы можете выбрать подходящий метод для оптимизации в соответствии с вашим собственным проектом. ЭтиWebpackЯ не видел большую часть исходного кода плагина, в основном из-за комбинацииWebpackОфициальные документы и практики проекта, а также потратил много времени на проверку и вывод этой статьи.Если в тексте есть ошибки, пожалуйста, исправьте их в поле для комментариев.

Ввиду быстрых изменений в передовых технологиях, эта статья основана наWebpackномер версии:

├── webpack@4.41.5 
└── webpack-cli@3.3.10 

Адрес проекта, соответствующий этой статье (использованный при написании этой статьи), предназначен для справки:GitHub.com/Иветт Л.А. У/И…

количественно

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

speed-measure-webpack-pluginПлагины могут измерять отдельные плагины иloaderЗатраченное время, после использования, при постройке у вас получится примерно так:

smp.jpeg
Сравните информацию до и после, чтобы определить эффект оптимизации.

speed-measure-webpack-pluginИспользование очень простое, вы можете напрямую использовать его для обертыванияWebpackКонфигурация:

//webpack.config.js
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();

const config = {
    //...webpack配置
}

module.exports = smp.wrap(config);

1.exclude/include

мы можем пройтиexclude,includeНастройте так, чтобы транспилировалось как можно меньше файлов. Как подсказывает название,excludeуказать файлы для исключения,includeУказывает файлы для включения.

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

//webpack.config.js
const path = require('path');
module.exports = {
    //...
    module: {
        rules: [
            {
                test: /\.js[x]?$/,
                use: ['babel-loader'],
                include: [path.resolve(__dirname, 'src')]
            }
        ]
    },
}

На картинке ниже то, что я не настроилincludeи настроенincludeСравнение результатов сборки:

include:exclude.jpeg

2. cache-loader

В некоторых накладных расходахloaderдобавлено доcache-loader, кэшировать результаты на диске. По умолчанию сохраняется вnode_modueles/.cache/cache-loaderПод содержанием.

Сначала установите зависимости:

npm install cache-loader -D

cache-loaderКонфигурация очень простая, поставь в др.loaderдо. ИсправлятьWebpackКонфигурация выглядит следующим образом:

module.exports = {
    //...
    
    module: {
        //我的项目中,babel-loader耗时比较长,所以我给它配置了`cache-loader`
        rules: [
            {
                test: /\.jsx?$/,
                use: ['cache-loader','babel-loader']
            }
        ]
    }
}

Если вы похожи на меня, только собираетесь датьbabel-loaderнастроитьcache, вы также можете не использоватьcache-loader,Датьbabel-loaderдобавить параметрыcacheDirectory.

cache-loader.jpeg

cacheDirectory: значение по умолчаниюfalse. Если установлено, указанный каталог будет использоваться для кэшированияloaderрезультат исполнения. ПослеWebpackСборка попытается прочитать кеш, чтобы избежать высокого потребления производительности, которое может происходить каждый раз при ее выполнении.BabelПроцесс перекомпиляции. установить нулевое значение илиtrueЕсли это так, используйте каталог кеша по умолчанию:node_modules/.cache/babel-loader. включиbabel-loaderкеш и конфигурацияcache-loaderЯ сравнил это, время сборки очень близко.

3.happypack

Из-за большого количества файлов, которые необходимо проанализировать и обработать, сборка представляет собой операцию чтения файлов и интенсивных вычислений, особенно когда количество файлов увеличивается.WebpackПроблема медленных сборок может быть серьезной. Чтение и запись файлов, а также вычислительные операции неизбежны.WebpackКак насчет одновременной обработки нескольких задач и использования мощности компьютеров с многоядерными процессорами для ускорения сборки?

HappyPackможет сделатьWebpackДля этого он разбивает задачу на несколько подпроцессов для одновременного выполнения, а подпроцессы обрабатывают результат, а затем отправляют результат в основной процесс.

Сначала нужно установитьhappypack:

npm install happypack -D

Измените файл конфигурации:

const Happypack = require('happypack');
module.exports = {
    //...
    module: {
        rules: [
            {
                test: /\.js[x]?$/,
                use: 'Happypack/loader?id=js',
                include: [path.resolve(__dirname, 'src')]
            },
            {
                test: /\.css$/,
                use: 'Happypack/loader?id=css',
                include: [
                    path.resolve(__dirname, 'src'),
                    path.resolve(__dirname, 'node_modules', 'bootstrap', 'dist')
                ]
            }
        ]
    },
    plugins: [
        new Happypack({
            id: 'js', //和rule中的id=js对应
            //将之前 rule 中的 loader 在此配置
            use: ['babel-loader'] //必须是数组
        }),
        new Happypack({
            id: 'css',//和rule中的id=css对应
            use: ['style-loader', 'css-loader','postcss-loader'],
        })
    ]
}

happypackВключено по умолчаниюCPU核数 - 1процесс, конечно, мы также можем пройтиthreadsДатьHappypack.

happypack.jpeg

Описание: когдаpostcss-loaderнастроен вHappypackВ проекте необходимо создать его в проекте.postcss.config.js.

//postcss.config.js
module.exports = {
    plugins: [
        require('autoprefixer')()
    ]
}

В противном случае будет выброшена ошибка:Error: No PostCSS Config found

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

4.thread-loader

Помимо использованияHappypackВ качестве альтернативы мы также можем использоватьthread-loader,Пучокthread-loaderразмещены в другихloaderраньше, затем поместите этоloaderПослеloaderбудет в отдельномworkerбег в бассейне.

Загрузчики, работающие в рабочем пуле, ограничены. Например:

  • ЭтиloaderНовый файл не может быть сгенерирован.
  • Этиloaderне могу использовать пользовательскийloaderAPI (то есть через плагины).
  • Этиloaderнедоступноwebpackнастройки опции.

Сначала установите зависимости:

npm install thread-loader -D

Изменить настройку:

module.exports = {
    module: {
        //我的项目中,babel-loader耗时比较长,所以我给它配置 thread-loader
        rules: [
            {
                test: /\.jsx?$/,
                use: ['thread-loader', 'cache-loader', 'babel-loader']
            }
        ]
    }
}

thread-loaderа такжеHappypackЯ сравнил, время сборки практически одинаковое. ноthread-loaderКонфигурация проста.

5. Включите многопроцессорное сжатие JS

Хотя многиеwebpackВ статье по оптимизации будет упомянута оптимизация многопроцессорного сжатия, будь тоwebpack-parallel-uglify-pluginилиuglifyjs-webpack-pluginнастроитьparallel. Но тут я хочу сказать, эти плагины отдельно устанавливать не нужно, они не сделают вашWebpackУлучшения скорости сборки.

токWebpackПо умолчанию используетсяTerserWebpackPlugin, многопроцессорность и кеширование включены по умолчанию, вы можете увидеть это в своем проекте при сборкеterserКэш-файлnode_modules/.cache/terser-webpack-plugin.

6.HardSourceWebpackPlugin

HardSourceWebpackPluginПредоставляет промежуточный кеш для модулей.Путь хранения по умолчанию для кеша:node_modules/.cache/hard-source.

настроитьhard-source-webpack-plugin, время сборки в первый раз не сильно изменилось, но во второй раз время сборки сократилось примерно на 80%.

Сначала установите зависимости:

npm install hard-source-webpack-plugin -D

ИсправлятьwebpackКонфигурация:

//webpack.config.js
var HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
module.exports = {
    //...
    plugins: [
        new HardSourceWebpackPlugin()
    ]
}

Протестировано с другим более крупным проектом, настроеннымHardSourceWebpackPlugin, время сборки сократилось с 8 с до 2 с.

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

7.noParse

Если некоторые сторонние модули не имеют версии спецификации AMD/CommonJS, вы можете использоватьnoParseидентифицировать модуль, чтобыWebpackЭти модули будут введены, но не преобразованы и не проанализированы, тем самым улучшаяWebpackпостроить производительность, например:jquery,lodash.

noParseЗначением атрибута является регулярное выражение илиfunction.

//webpack.config.js
module.exports = {
    //...
    module: {
        noParse: /jquery|lodash/
    }
}

мой текущийwebpack-optimizeпроект, не используетсяjqueryилиlodash.

Поэтому создайте новый тест проекта и вводите толькоjqueryа такжеloadsh, затем настройтеnoParseи не настроенnoParse, соответственно, для построения времени сравнения.

настроитьnoParseРаньше для сборки требовалось2392ms. настроенnoParseПосле этого для сборки требуется1613ms. Если вы используете сторонние зависимости, которые не нужно разрешать, то настройтеnoParseПонятно, что будет оптимизация.

8.resolve

resolveнастроитьwebpackКак найти файл, соответствующий модулю. Предположим, мы уверены, что модули все из корневого каталогаnode_modulesНайдите в, мы можем настроить:

//webpack.config.js
const path = require('path');
module.exports = {
    //...
    resolve: {
        modules: [path.resolve(__dirname, 'node_modules')],
    }
}

Важно помнить, что если вы настроите вышеуказанноеresolve.moudles, может быть проблема, например, есть ещеnode_modulesкаталог, то он появится, соответствующий файл, очевидно, есть, но выдается сообщение, что он не может быть найден. Поэтому я лично не рекомендую настраивать это. Если другие коллеги не знакомы с этой конфигурацией, они будут сбиты с толку, когда столкнутся с этой проблемой.

Кроме того,resolveизextensionsконфигурация, по умолчанию['.js', '.json'], если вы настраиваете его, не забудьте поставить наиболее часто встречающийся суффикс первым и контролировать длину списка, чтобы уменьшить количество попыток.

Этот проект небольшой, поэтому эффект оптимизации здесь не очевиден при тестировании.

9.IgnorePlugin

webpackВстроенный плагин, роль которого заключается в игнорировании каталога, указанного сторонним пакетом.

Например:moment(версия 2.24.0) упакует весь локализованный контент и основные функции вместе, мы можем использоватьIgnorePluginИгнорируйте локализованный контент при упаковке.

//webpack.config.js
module.exports = {
    //...
    plugins: [
        //忽略 moment 下的 ./locale 目录
        new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
    ]
}

При использовании, если нам нужно указать язык, нам нужно вручную импортировать языковой пакет, например, импортировать китайский языковой пакет:

import moment from 'moment';
import 'moment/locale/zh-cn';// 手动引入

index.jsтолько импортmoment, упакованныйbundle.jsразмер263KB, если настроеноIgnorePlugin, вводится отдельноmoment/locale/zh-cn, размер собранного пакета55KB.

10.externals

Мы можем хранить некоторые файлы JS вCDNвверх (уменьшениеWebpackупакованныйjsобъем), вindex.htmlпрошедший<script>Введение этикетки, например:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="root">root</div>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</body>
</html>

Надеемся, что при использовании еще возможно пройтиimportссылаться (например,import $ from 'jquery'), И надеюсьwebpackОн не будет упакован и может быть настроен на этом этапе.externals.

//webpack.config.js
module.exports = {
    //...
    externals: {
        //jquery通过script引入之后,全局中即有了 jQuery 变量
        'jquery': 'jQuery'
    }
}

11.DllPlugin

Иногда, если все файлы JS сгруппированы в один файл JS, окончательный сгенерированный файл JS будет очень большим.В настоящее время мы должны рассмотреть возможность разделенияbundles.

DllPluginа такжеDLLReferencePluginможно разделитьbundlesИ может значительно улучшить скорость строительства,DllPluginа такжеDLLReferencePluginобеwebpackвстроенный модуль.

Мы используемDllPluginКомпилировать редко обновляемые библиотеки, а когда версия этих зависимостей не меняется, перекомпилировать не нужно. мы создаем новыйwebpackconfig специально для компиляции библиотек динамической компоновки, например, с именем:webpack.config.dll.js, здесь мы будемreactа такжеreact-domУпакован отдельно в динамическую библиотеку.

//webpack.config.dll.js
const webpack = require('webpack');
const path = require('path');

module.exports = {
    entry: {
        react: ['react', 'react-dom']
    },
    mode: 'production',
    output: {
        filename: '[name].dll.[hash:6].js',
        path: path.resolve(__dirname, 'dist', 'dll'),
        library: '[name]_dll' //暴露给外部使用
        //libraryTarget 指定如何暴露内容,缺省时就是 var
    },
    plugins: [
        new webpack.DllPlugin({
            //name和library一致
            name: '[name]_dll', 
            path: path.resolve(__dirname, 'dist', 'dll', 'manifest.json') //manifest.json的生成路径
        })
    ]
}

существуетpackage.jsonизscriptsДобавить в:

{
    "scripts": {
        "dev": "NODE_ENV=development webpack-dev-server",
        "build": "NODE_ENV=production webpack",
        "build:dll": "webpack --config webpack.config.dll.js"
    },
}

воплощать в жизньnpm run build:all, можно увидетьdistКаталог выглядит следующим образом, причина, по которой динамическая библиотека размещена отдельноdllкаталог, в основном для использованияCleanWebpackPluginДинамические библиотеки удобнее отфильтровывать.

dist
└── dll
    ├── manifest.json
    └── react.dll.9dcd9d.js

manifest.jsonраньше позволялDLLReferencePluginСопоставьте со связанными зависимостями.

ИсправлятьwebpackОсновной файл конфигурации:webpack.config.jsКонфигурация:

//webpack.config.js
const webpack = require('webpack');
const path = require('path');
module.exports = {
    //...
    devServer: {
        contentBase: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new webpack.DllReferencePlugin({
            manifest: path.resolve(__dirname, 'dist', 'dll', 'manifest.json')
        }),
        new CleanWebpackPlugin({
            cleanOnceBeforeBuildPatterns: ['**/*', '!dll', '!dll/**'] //不删除dll目录
        }),
        //...
    ]
}

использоватьnpm run buildпостроить, вы можете видетьbundle.jsгромкость сильно уменьшается.

Исправлятьpublic/index.htmlфайл, в который вы импортируетеreact.dll.js

<script src="/dll/react.dll.9dcd9d.js"></script>

скорость наращивания

DllPlugin.jpeg

объем пакета

dll-size.jpeg

12. Извлеките общедоступный код

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

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

//webpack.config.js
module.exports = {
    optimization: {
        splitChunks: {//分割代码块
            cacheGroups: {
                vendor: {
                    //第三方依赖
                    priority: 1, //设置优先级,首先抽离第三方模块
                    name: 'vendor',
                    test: /node_modules/,
                    chunks: 'initial',
                    minSize: 0,
                    minChunks: 1 //最少引入了1次
                },
                //缓存组
                common: {
                    //公共模块
                    chunks: 'initial',
                    name: 'common',
                    minSize: 100, //大小超过100个字节
                    minChunks: 3 //最少引入了3次
                }
            }
        }
    }
}

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

splitChunks.jpeg

runtimeChunk

runtimeChunkЭффект должен включатьchunkСписок отображений изmain.jsизвлечено изsplitChunkне забудьте настроитьruntimeChunk.

module.exports = {
    //...
    optimization: {
        runtimeChunk: {
            name: 'manifest'
        }
    }
}

Окончательный построенный файл будет генерироватьmanifest.js.

Дальнейшая оптимизация с помощью webpack-bundle-analyzer

делаетwebpackПри построении оптимизацийvendorЯ играл более 1М,reactа такжеreact-domУпакован в DLL.

Следовательно, необходимо использоватьwebpack-bundle-analyzerПроверьте, какие сумки больше.

Сначала установите зависимости:

npm install webpack-bundle-analyzer -D

Он также очень прост в использовании, изменим нашу конфигурацию:

//webpack.config.prod.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.config.base');
module.exports = merge(baseWebpackConfig, {
    //....
    plugins: [
        //...
        new BundleAnalyzerPlugin(),
    ]
})

npm run buildBuild, он будет включен по умолчанию:http://127.0.0.1:8888/, вы можете увидеть объем каждого пакета:

W1.jpeg

дальше кvendorрасколоть,vendorразделить на 4 (используяsplitChunksможно разделить).

module.exports = {
    optimization: {
    concatenateModules: false,
    splitChunks: {//分割代码块
      maxInitialRequests:6, //默认是5
      cacheGroups: {
        vendor: {
          //第三方依赖
          priority: 1,
          name: 'vendor',
          test: /node_modules/,
          chunks: 'initial',
          minSize: 100,
          minChunks: 1 //重复引入了几次
        },
        'lottie-web': {
          name: "lottie-web", // 单独将 react-lottie 拆包
          priority: 5, // 权重需大于`vendor`
          test: /[\/]node_modules[\/]lottie-web[\/]/,
          chunks: 'initial',
          minSize: 100,
          minChunks: 1 //重复引入了几次
        },
        //...
      }
    },
  },
}

Перестройте, и результат будет выглядеть так:

W2.jpeg

13. Оптимизация самого вебпака

tree-shaking

Если вы используете ES6importсинтаксис, то в рабочей среде неиспользуемый код будет автоматически удален.

//math.js
const add = (a, b) => {
    console.log('aaaaaa')
    return a + b;
}

const minus = (a, b) => {
    console.log('bbbbbb')
    return a - b;
}

export {
    add,
    minus
}
//index.js
import {add, minus} from './math';
add(2,3);

В финальном коде сборкиminusФункции не упакованы.

продвижение масштаба хостинга

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

Также будьте осторожны при тестировании.speed-measure-webpack-pluginа такжеHotModuleReplacementPluginНельзя использовать одновременно, иначе будет сообщено об ошибке:

Оптимизация конфигурации Babel

если ты правbabelЕсли вы не знакомы с ним, вы можете прочитать эту статью:Знания Babel7, которые нельзя упустить.

без настройки@babel/plugin-transform-runtimeчас,babelбудет использовать небольшую вспомогательную функцию для достижения чего-то вроде_createClassи другие общедоступные методы. По умолчанию он будет введен (inject) в каждый файл, который в этом нуждается. Но в результате этого размер построенного JS становится больше.

Нам также не нужноjsвнедрить вспомогательную функцию в , чтобы мы могли использовать@babel/plugin-transform-runtime,@babel/plugin-transform-runtimeявляется многоразовымBabelДобавлены помощники для сохранения плагинов размера кода.

Поэтому мы можем.babelrcувеличить в@babel/plugin-transform-runtimeКонфигурация.

{
    "presets": [],
    "plugins": [
        [
            "@babel/plugin-transform-runtime"
        ]
    ]
}

Выше приведены некоторые из оптимизаций, которые я использовал до сих пор.Если у вас есть лучший метод оптимизации, оставьте сообщение в области комментариев, спасибо за чтение.

После прочтения этой статьи пришло время заняться строительными лесами:[Основы пользовательского интерфейса среднего и продвинутого уровня] Научат вас строить строительные леса касанием рук.

наконец

Если эта статья была вам полезна, пожалуйста, поставьте лайк этой статье.

Обратите внимание на публичный аккаунт

掘金使用

Справочная документация: