- Подготовка знаний
1. Упаковка в производственном режиме поставляется с оптимизацией
-
tree shaking
Встряхивание дерева — это термин, обычно используемый для удаления неиспользуемого кода (мертвый код) в js при упаковке, и он основан на ***функции статической структуры импорта и экспорта в модульной системе ES6***
Когда модуль вводится во время разработки, если упоминается только одна из функций, только те функции, которые используются, будут упакованы в пакет во время онлайн-упаковки, а другие неиспользуемые функции не будут упакованы, что может обеспечить простейшую базовую оптимизацию. .
- Создайте math.js, добавьте два метода
export const add = (a, b) => a + b export const minus = (a, b) => a- b
- использовать в main.js
// tree shaking 分析 // 若是此时使用 require 引入,不管 math 中的方法是否使用,都会被打包 const math = require('./utils/math') // 若是使用 import 引入, 只会打包使用了 math 的方法 import { add } from './utils/math' console.log('index 页面',math.add(1,2)); console.log('index 页面',add(1,2));
- Упакуйте в соответствии с различными методами импорта и просмотрите упакованные файлы
-
scope hoisting
Функция хостинга области действия: позволяет определить взаимосвязь между модулями, что может уменьшить размер файла кода, упакованного файлом веб-пакета, и ускорить его работу.
Принцип реализации хостинга области действия: анализируйте зависимости между модулями и объединяйте разрозненные модули в функцию, насколько это возможно, но предпосылка заключается в том, что избыточность кода не может быть вызвана, поэтому могут быть объединены только те модули, на которые ссылаются один раз.
Поскольку хостинг области должен анализировать зависимости между модулями, исходный код должен использовать модульные операторы ES6, иначе он не будет работать по той же причине, что и встряска дерева.
-
Определите несколько переменных в main.js и выведите
const a = 1 const b = 2 const c = 3 // webpack 在这里会进行 预执行,将结果推断后打包放在这里 console.log(a + b + c) console.log(a, b, c)
-
После упаковки код становится
console.log(6),console.log(1,2,3)
Поскольку три переменные определены и используются только в этом месте и не используются в других местах, webpack упаковывает их напрямую с определенными значениями, сохраняя определение трех переменных.
-
-
сжатие кода
Весь код сжат и запутан с помощью UglifyJsPlugin.
2. Оптимизация CSS
2.1 Извлечь CSS в отдельные файлы
Mini-css-extract-plugin — это плагин для извлечения CSS в отдельные файлы, CSS-файл создается для каждого js-файла, содержащего CSS, поддерживает загрузку CSS и sourceMap по требованию.
-
Можно использовать только в webpack4, преимущество
- Асинхронная загрузка
- Нет повторной компиляции, лучшая производительность
- проще в использовании
- только для css
-
использовать
-
Установить
npm i -D mini-css-extract-plugin
-
Цитировать
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
-
Создайте объект плагина, настройте имя извлеченного файла css, поддержите синтаксис заполнителя
new MiniCssExtractPlugin({ filename:'[name].css' // [name] 就是 placeholder 语法 })
-
Замените все ранее настроенные загрузчики стилей на MiniCssExtractPlugin.loader.
{ test:/\.css$/, use:[MiniCssExtractPlugin.loader, 'css-loader'] }, { test:/\.less$/, use:[MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] }, { test:/\.scss$/, use:[MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },
-
2.2 Автоматически добавлять префикс CSS
Чтобы использовать postcss, вам нужно использовать postcss-loader и autoprefixer.
-
Установить
npm i -D postcss-loader autoprefixer
-
Измените файл конфигурации, чтобы разместить postcss-loader справа от css-loader.
{ test:/\.css$/, use:[MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader',] }, { test:/\.less$/, use:[MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader'] }, { test:/\.scss$/, use:[MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'] },
-
Добавьте файл конфигурации postcss в корневой каталог проекта: postcss.config.js
module.exports = { plugins: [ require('autoprefixer')({ browsers: [ // 加这个后可以出现额外的兼容性前缀 "> 0.01%" ] }) ] }
2.3 Включить сжатие CSS
Необходимо использовать плагин optimise-css-assets-webpack-plugin для завершения сжатия css.
Однако, поскольку настройки оптимизации веб-пакета по умолчанию будут перезаписаны при настройке сжатия css, код JS не может быть сжат, поэтому необходимо импортировать плагин сжатия кода JS в terser-webpack-plugin.
-
Установить
npm i -D terser-webpack-plugin optimize-css-assets-webpack-plugin
-
Цитировать
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin');
-
настроить
optimization:{ minimizer: [ new TerserPlugin({}), new OptimizeCssAssetsPlugin({}) ] }
Плагин сжатия JS по умолчанию, используемый webpack4, — это uglifyjs-webpack-plugin, который также рекомендовался в предыдущей версии mini-css-extract-plugin, но в новой версии рекомендуется использовать terser-webpack-plugin.
3. JS-оптимизация
Разделение кода — одна из важных функций оптимизации, используемых при упаковке веб-пакетов.Эта функция может разделять код на разные пакеты, а затем загружать эти файлы по запросу или параллельно.Разделение кода можно использовать для получения пакетов меньшего размера и контроля загрузки ресурсов. приоритет, если его можно использовать разумно, это может сильно повлиять на время загрузки
- Три распространенных метода разделения кода
- Начальная точка входа: использовать конфигурацию входа, ручное разделение кода
- Размещение дубликатов: используйте плагин SplitChunksPlugin для дедупликации и разделения фрагментов.
- Динамический импорт: разделение кода путем встраивания вызовов функций модулей.
3.1 Ручная настройка мультивхода
- Есть некоторые проблемы с ручной настройкой нескольких записей
- Если между входными фрагментами есть дубликаты модулей, какие дубликаты модулей будут добавлены в каждый упакованный js-файл.
- Метод недостаточно гибкий, и основная логика приложения не может быть динамически разбита на код.
-
Настройте несколько записей в файле конфигурации веб-пакета
entry:{ main: './src/main.js', other: './src/other.js' }, output:{ path: path.join(__dirname, '..', './dist'), filename: '[name].js', publickPath: '/' }
-
Добавьте модуль как в main.js, так и в other.js и используйте его функции.
-
Main.js
import $ from 'jquery' $(() => { $('<div></div>').html('main').appendTo('body') })
-
other.js
import $ from 'jquery' $(() => { $('<div></div>').html('other').appendTo('body') })
-
-
Упакуйте файл, вы можете видеть, что jquery загружается как в основной, так и в другие упакованные файлы.
3.2 Извлечение общедоступного кода
Плагин, используемый выше webpack4, называется SplitChunksPlugin. Плагин CommonChunk, который использовался до того, как был удален webpack 4. В последней версии веб-пакета вам нужно только добавить атрибут splitChunks под узлом оптимизации в файле конфигурации, чтобы выполнить соответствующую настройку.
-
Изменить файл конфигурации
optimization splitChunks:{ chunks: "all" } }
-
Файл представления пакета
После упаковки соответствующие входные файлы будут упакованы, и будет создан дополнительный файл js.Этот файл является общедоступной частью, на которую ссылается каждый фрагмент.
-
Параметр конфигурации splitChunksPlugin
Конфигурацию SplitChunksPlugin нужно изменить только в splitChunks под узлом оптимизации.Если изменений нет, будут использоваться настройки по умолчанию.
Конфигурация SplitChunksPlugin по умолчанию работает для подавляющего большинства пользователей.
-
webpack будет автоматически разделять код на основе следующих принципов по умолчанию.
- Общие блоки кода или компоненты модулей из папки node_modules
- Упакованные блоки кода имеют размер более 30 КБ, минимизируйте размер перед сжатием
- При загрузке блоков кода по запросу максимальное количество одновременных запросов не должно превышать 5
- При инициализации страницы максимальное количество одновременных запросов не должно превышать 3
-
Конфигурация SplitChunksPlugin по умолчанию
module.exports = { optimization: { splitChunks: { chunks: 'async', // 只对异步加载的模块进行拆分,import('jquery').then()就是典型的异步加载,可选项还有 all | initial minSize: 30000, // 模块最少大于 30kb 才会拆分 maxSize: 0, // 为0时模块大小无上限,只要大于 30kb 都会拆分。若是非0,超过了maxSize的值,会进一步拆分 minChunks: 1, // 模块最少引用一次才会拆分 maxAsyncRequests: 5, // 异步加载时同时发送的请求数量最大不能超过5,超过5的部分不拆分 maxInitialRequests: 3, // 页面初始化时,同时发送的请求数量最大不能超过3,超过3的不跟不拆分 automaticNameDelimiter: '~', // 默认的连接符 name: true, // 拆分的chunk名,设置为true表示根据模块名和CacheGroup的key来自动生成,使用上面的连接符连接 cacheGroups: { // 缓存组配置,上面配置读取完成后进行拆分,如果需要把多个模块拆分到一个文件,就需要缓存,所以命名为缓存组 vendors: { // 自定义缓存组名 test: /[\\/]node_modules[\\/]/, // 检查 node_modules 目录,只要模块在该目录下就使用上面配置拆分到这个组 priority: -10, // 权重为-10,决定了那个组优先匹配,假如node_modules下面有个模块要拆分,同时满足vendors和default组,此时就会分到 priority 值比较大的组,因为 -10 > -20 所以分到 vendors 组 filename:'vendoes.js' }, default: { // 默认缓存组名 minChunks: 2, // 最少引用两次才会被拆分 priority: -20, // 权重 -20 reuseExistingChunk: true // 如果主入口中引入了两个模块,其中一个正好也引用了后一个,就会直接复用,无需引用两次 } } } } };
-
3.3 Динамический импорт (ленивая загрузка)
По умолчанию webpack4 позволяет динамически импортировать синтаксис импорта, но для этого требуется поддержка подключаемого модуля Babel. Последняя версия пакета подключаемого модуля Babel: @babel/plugin-syntax-dynamic-import. Следует отметить, что самый большой Преимущество динамического импорта заключается в том, что он реализует ленивую загрузку. Модуль будет загружен только после этого модуля, что может повысить скорость загрузки первого экрана приложения SPA.Принцип маршрутизации ленивой загрузки для трех фреймворков одинаков.
-
Установить
npm i -D @babel/plugin-syntax-dynamic-import
-
Измените .babelrc, чтобы добавить плагин @babel/plugin-syntax-dynamic-import.
{ "presets": ["@babel/env"], "plugins": [ "@babel/plugin-proposal-class-properties", "@babel/plugin-syntax-dynamic-import" ] }
-
Импортируйте модуль jq динамически
function getDivDom(){ // import('jquery') 返回的是一个 promise,若是低版本需要注意 return import('jquery').then(({default: $}) => { return $('<div></div>').html('动态导入') }) }
-
Добавьте событие нажатия на кнопку, вызовите функцию getDivDom, чтобы создать элемент и добавить его на страницу после нажатия
window.onload = () => { document.getElementById('btn').addEventListener('click',() => { getDivDom().then(item => { item.appendTo('body') }) }) }
4.noParse
При введении некоторых сторонних модулей, таких как jq и т. д., мы знаем, что они не будут зависеть от других модулей внутри, потому что мы используем только один файл js или css, поэтому, если в это время webpack анализирует их внутренние зависимости, в на самом деле, это очень трудоемко, вам нужно, чтобы webpack не тратил энергию на разбор этих библиотек, которые, как вы знаете, не имеют зависимостей, вы можете добавить noParse под узлом модуля файла конфигурации веб-пакета, и настроить регулярный для определения модулей которым не нужно разрешать зависимости
module:{
noParse: /jquery|bootstrap/ // jquery|bootstrap 之间不能加空格变成 jquery | bootstrap, 会无效
}
5.IgnorePlugin
При внедрении некоторых сторонних модулей, таких как momentJS и dayJS, они будут выполнять внутреннюю обработку i18n, поэтому будут содержать много языковых пакетов, а языковые пакеты будут занимать больше места при упаковке.Если проекту нужны только чтобы использовать китайский или несколько языков, его можно игнорировать.Удалите все языковые пакеты, а затем вставьте языковые пакеты по мере необходимости, чтобы эффективность сборки была выше, а файлы, создаваемые упаковкой, были меньше
-
Возьмите момент в качестве примера
import moment from 'moment' moment.locale('zh-CN') // 设置为中文 console.log(moment().subtract(6, 'days').calendar())
-
Прежде всего, от какого языкового пакета зависит этот момент, и проанализируйте его, просмотрев исходный код момента.
function loadLocale(name) { var oldLocale = null; // TODO: Find a better way to register and load all the locales in Node if (!locales[name] && (typeof module !== 'undefined') && module && module.exports) { try { oldLocale = globalLocale._abbr; var aliasedRequire = require; aliasedRequire('./locale/' + name); getSetGlobalLocale(oldLocale); } catch (e) {} } return locales[name]; }
Через aliasedRequire('./locale/' + name) вы можете узнать, что многоязычный каталог momentJS является локальным, и все языковые JS-файлы находятся в этом каталоге.
-
Игнорируйте его зависимости с помощью плагина IgnorePlugin.
Игнорировать многоязычную локаль каталога momentJS
new webpack.IgnorePlugin(/\.\/locale/, /moment/)
-
Импортируйте вручную, когда вам нужно использовать некоторые зависимости
После игнорирования его зависимостей moment.locale('zh-CN') завершится ошибкой, потому что все языковые пакеты, от которых он зависит, игнорируются и их необходимо импортировать вручную.
import moment from 'moment' import 'moment/locale/zh-cn' // 需要手动引入方可生效 moment.locale('zh-CN') console.log(moment().subtract(6, 'days').calendar())
6.DLLPlugin
При внедрении некоторых сторонних модулей, таких как Vue, React и т.д., файлы этих фреймворков вообще не модифицируются, и их нужно парсить каждый раз при упаковке, что также будет влиять на скорость упаковки, это только улучшает скорость доступа пользователя после выхода в интернет, но не улучшает скорость сборки, поэтому, если вам нужно улучшить скорость сборки, вы должны использовать метод библиотеки динамической компоновки, аналогичный dll файлу windows
С помощью подключаемого модуля DLLPlugin эти фреймворки используются в качестве динамически подключаемых библиотек, которые собираются только один раз, и каждая последующая сборка будет генерировать только свой собственный бизнес-код, что может значительно повысить эффективность построения.
Идея свиньи состоит в том, чтобы говорить о некоторых немодифицированных зависимых файлах и заранее их упаковывать, чтобы нам не нужно было упаковывать эти коды, когда мы разрабатываем код и выпускаем его, тем самым экономя время упаковки, в основном используя два плагины: DLLPlugin и DLLReferencePlugin
Следует отметить, что если используется DLLPlugin, плагин CleanWebpackPlugin будет конфликтовать, и плагин CleanWebpackPlugin необходимо удалить.
-
DLLPlugin
Создайте файл dll с одной конфигурацией веб-пакета, а также создайте файл manifest.json, который используется подключаемым модулем DLLReferencePlugin для сопоставления зависимостей, который сообщает веб-пакету, какие файлы были извлечены и упакованы.
- Параметры конфигурации
- контекст (необязательно): контекст запроса в файле манифеста, по умолчанию используется контекст файла веб-пакета.
- name: имя открытой функции dll, которое может совпадать с output.library.
- path:manifest.json Сгенерированная папка и имя
- Параметры конфигурации
-
DLLReferencePlugin
Этот плагин в основном используется для основной конфигурации веб-пакета, dll, на который он ссылается, должен быть предварительно собран в зависимости от системы.
- Параметры конфигурации
- контекст: контекст запроса в файле манифеста
- manifest: manifest.json, сгенерированный плагином DLLPlugin.
- контент (необязательно): запрошенный идентификатор модуля сопоставления (по умолчанию — manifest.content)
- имя (необязательно): имя, предоставляемое dll
- область действия (необязательно): префикс, используемый для доступа к файлам dll.
- sourceType (необязательно): как выставляется dll (libraryTarget)
- Параметры конфигурации
Извлеките библиотеку из проекта Vue в DLL
- Подготовьте файл конфигурации веб-пакета, который упаковывает Vue в DLL.
- Создайте новый файл webpack.vue.js в каталоге сборки, который специально используется для упаковки DLL vue.
- Запись конфигурации: поместите несколько библиотек в DLL
- Экспорт конфигурации: обязательно установите свойство библиотеки, чтобы глобально отображать упакованные результаты.
- Настройка плагина: установите имя файла DLL и местоположение файла манифеста после упаковки.
// 此配置文件 是打包VUE全家桶的
const path = require('path')
const webpack = require('webpack')
module.exports = {
mode: 'production',
entry:{
vue: [
'vue/dist/vue',
'vue-router'
]
},
output:{
path: path.resolve(__dirname, '../dist'),
filename: '[name]_dll.js',
library: '[name]_dll' // 最终会在全局暴露出一个[name]_dll的对象
},
plugins:[
new webpack.DllPlugin({
name: '[name]_dll',
path: path.resolve(__dirname, '../dist/manifest.json'),
})
]
}
webpack.vue.js используется только для упаковки и генерации файла [имя]_dd.js и файла manifest.json.Он не должен участвовать в упаковке бизнес-кода, потому что он будет сгенерирован только тогда, когда потребуется файл dll генерироваться каждый раз при изменении.Выполнить один раз, иначе не нужно участвовать в упаковке
- существует
webpack.base.js
настроить плагин в
Используйте DllReferencePlugin, чтобы указать расположение файла манифеста.
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, '../dist/manifest.json'),
})
- Поскольку файл [имя]_dll не импортируется динамически после его создания, необходим подключаемый модуль для динамического импорта сгенерированного файла dll.
Установите плагин add-asset-html-webpack
npm i -D add-asset-html-webpack-plugin
Настройте плагин для автоматического добавления тегов скрипта в HTML.Следует отметить, что его нужно вводить после HtmlWebpackPlugin, т.к. HtmlWebpackPlugin создает html файл, а AddAssetHtmlWebpackPlugin вставляет скрипт в существующий html, иначе он будет перезаписан
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '../dist/vue_dll.js')
})
7. Анализ и оптимизация
При упаковке используйте плагины с умомspeed-measure-webpack-plugin
иwebpack-bundle-analyzer
, который можно использовать для каждого процесса сборки проекта.loader
,plugin
и другие трудоемкие статистические данные, а также оптимизация для трудоемких операций, результаты упаковки можно контролировать и оптимизировать для размера тома.
Установить
npm i speed-measure-webpack-plugin -D
npm i webpack-bundle-analyzer -D
настроить
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const smp = new SpeedMeasurePlugin()
// vue-cli3中的配置
configureWebpack: smp.wrap({
plugins: [
new BundleAnalyzerPlugin()
]
}),
section>