Автор DBCdouble
Исходный код проекта Demo:кликните сюда
Введение
Прошло много времени с тех пор, как 15 февраля 2018 года вышел webpack 4.0.0.webpackПолагаться на "Нулевая конфигурация","До 98 % быстрее«Я успешно привлек бесчисленное количество поклонников. Для меня, который страдал от слишком долгого времени упаковки проекта, я, несомненно, увидел свет, поэтому я решил начать тестирование воды.
2. Структура проекта и окружающая среда
Перед обновлением:- Node: v8.11.4
- webpack: ^1.12.9
- связанные с Вавилоном: ^6.x
- react: ^0.14.8 (Когда я впервые увидел версию для реакции, я был немного сбит с толку, но это правда, ха-ха, я не могу не восхищаться тем, что человек, который первоначально создал этот проект, должен быть боссом реакции, и я обновлю статью, чтобы перейти на react16.x)
- react-router: ^2.6.1 (позже статья будет обновлена до react-router4.x)
- Связанные загрузчики
- Компоненты роутинга (страниц): 130 (в проекте используются SPA-приложения, и на данный момент существует 130 страниц роутинга, поэтому, если скорость построения можно успешно улучшить или уменьшить размер файла на достаточно большом приложении, то обновление версии webpack Появится 4.0. Значительно)
После обновления:
- Node: v8.11.4
- webpack: ^4.29.5
- связанные с Вавилоном: ^7.x
- react: ^0.14.8
- react-router: ^2.6.1
- Связанные загрузчики (обновленные загрузчики будут подробно описаны позже)
- Компоненты маршрутизации (страница) количество
3. Фон
С непрерывной итерацией проекта количество файлов стилей и js файлов увеличивается, что приводит к тому, что все больше и больше времени тратится на упаковку веб-пакета.В среде разработки часто необходимо часто отлаживать определенный фрагмент кода ctrl+s будет появляются долго Явление ожидания (ждать надоедает) со временем накапливается и тратит слишком много времени на ожидание упаковки. Не говоря уже о производственной среде, средняя продолжительность составляет около 100–120 с.В нормальных условиях после входа в npm run deploy to package я выбираю выйти и выкурить сигарету. И если ситуация заключается в устранении онлайн-ошибок, каждая секунда на счету, поэтому необходимо оптимизировать время упаковки.
4. Анализ
процесс сборки вебпака
- Инициализировать: начать сборку, прочитать и объединить параметры конфигурации, загрузитьPlugin, экземплярCompiler.
- Компилировать: изEntryвыдается за каждыйModuleпоследовательный вызов, соответствующийLoaderПерейдите к переводу содержимого файла, а затем найдитеModuleзависимыйModule, который компилируется рекурсивно.
- Вывод: объедините скомпилированный модуль вChunk,Пучок ChunkПреобразуйте его в файл и выведите локально.
Анализ упаковки
Время, проведенное в производственной среде webpack2.x: 104,145 с.
Время, проведенное в среде разработки webpack2.x: 68099 мс
Хотя мы можем интуитивно увидеть время, затраченное на упаковку webpack2, мы не знаем, какие этапы прошла упаковка webpack и на что потрачено много времени. можно использовать здесьspeed-measure-webpack-pluginУпаковка Webpack обнаружена во время каждой части потраченного времени, при вводе команды в терминал монтируется.
npm install speed-measure-webpack-plugin -D
После завершения установки настраиваем его в конфигурационном файле вебпака
webpack.config.js
Обратитесь к использованию плагина speed-measure-webpack-plugin,проверить здесь
После настройки запустите проект (здесь анализируется только среда разработки), как показано ниже
Как видно из рисунка выше, большую часть времени в процессе упаковки вебпака тратится на загрузчик, который является вторым звеном в процессе построения вебпака, этап компиляции. Обратите внимание, что также видно, что ProgressPlugin занял 28,87 с, поэтому после того, как нам не нужно анализировать время, затраченное на процесс упаковки веб-пакета, мы можем прокомментировать его в webpack.config.js.
Пять, установка и настройка
1. веб-пакет
Сначала удалите предыдущий веб-пакет, webpack-cli, webpack-dev-server
npm uninstall webpack webpack-dev-server webpack-cli && npm uninstall webpacl-cli -g
Установите последнюю версию веб-пакета, webpack-cli (webpack4 извлекает шаблоны webpack-cli из webpack, поэтому необходимо установить webpack-cli), webpack-dev-server
npm install webpack webpack-dev-server webpack-cli -D
Кстати, я обновлю соответствующие плагины вебпака до последней версии, т.к. в вебпак внесено много изменений по сравнению с вебпакс2, на случай, если плагины старой версии не совместимы с вебпак4, поэтому буду ставить все модули плагинов, связанных с веб-пакетом в проекте, здесь. Сначала удалите его, чтобы вы могли анализировать ошибки при обновлении
npm uninstall extract-text-webpack-plugin html-webpack-plugin webpack-dev-middleware webpack-hot-middleware
2. Обновите бабел7
Удалите предыдущие модули, связанные с Babel.
npm uninstall babel-core babel-loader babel-cli babel-eslint babel-plugin-react-transform babel-plugin-transform-runtime babel-preset-es2015 babel-preset-react babel-preset-stage-0 babel-runtime
установить бабел7
npm install @babel/cli @babel/core babel-loader @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/plugin-proposal-export-default-from @babel/plugin-transform-runtime @babel/preset-env @babel/preset-react
- @babel/cli: Инструменты лесов Бабела
- @babel/core: babel-core существует как ядро babel.Основной API-интерфейс babel находится в этом модуле, например: transform, который используется для транскодирования строк для получения AST
- babel-loader: используется для компиляции кода JavaScript.
- @babel/preset-env: Официальное объяснение «Для подготовки следующего поколения JavaScript Compiler», скомпилированном в JavaScript Standard Browser знать
- @babel/preset-react
@babel/plugin-proposal-class-properties: Разобрать свойства класса class
@babel/plugin-proposal-decorators: Разобрать синтаксис шаблона декоратора, например @connect, с помощью react-redux.
@babel/plugin-proposal-export-default-from: Разобрать экспорт xxx из синтаксиса 'xxx'
.babelrcФайл представляет собой файл конфигурации babel (я настроил его непосредственно в параметрах babel-loader в webpack.config.js. Обратите внимание, что файл .babelrc необходимо преобразовать в формат json, а имя атрибута должно быть двойным) цитируется)
3. Установите ESlint
В корневой каталог проекта установитеeslint
а такжеeslint-loader
npm install eslint eslint-loader -D
.eslintrc
Это файл конфигурации eslint, нам нужно увеличить в корне проекта.eslintrc
документ.
{
"parser": "babel-eslint",
"env": {
"browser": true,
"es6": true,
"node": true
},
"globals" : {
"Action" : false,
"__DEV__" : false,
"__PROD__" : false,
"__DEBUG__" : false,
"__DEBUG_NEW_WINDOW__" : false,
"__BASENAME__" : false
},
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"extends": "airbnb",
"rules": {
"semi": [0],
"react/jsx-filename-extension": [0]
}}
существуетwebpack.config.js
, добавьте файлы, которые необходимо обнаружитьeslint-loader
Загрузчик. Как правило, мы проверяем перед компиляцией кода.
webpack.config.js
Обратите внимание, что здесь isEslint использует параметр eslint, передаваемый сценариями npm, чтобы определить, нужно ли текущей среде выполнять проверку формата кода, чтобы у разработчиков было больше возможностей для выбора, а eslint-loader должен быть настроен перед babel-loader, поэтому здесь используйте unshift для добавления эслинт-погрузчик
packack.json
Добавьте следующую команду в файл package.json
{
"scripts": {
"eslint": "eslint --ext .js --ext .jsx src/"
}
}
На этом этапе вы можете определить формат кода в файле src, выполнив npm run eslint.
4. Для установки и упаковки необходимы плагины
npm install webpack-merge yargs-parser clean-webpack-plugin progress-bar-webpack-plugin webpack-build-notifier html-webpack-plugin mini-css-extract-plugin add-asset-html-webpack-plugin uglifyjs-webpack-plugin optimize-css-assets-webpack-plugin friendly-errors-webpack-plugin happypack
- webpack-merge: Общая конфигурация и конфигурация среды для слияния webpack (слияние webpack.config.js и webpack.development.js или webpack.production.js)
- yargs-parser: используется для преобразования параметров командной строки в наших npm-скриптах в пары ключ-значение, например --mode development будет разобрано в пары ключ-значение mode: "development", что удобно для получения параметров в файле конфигурации
- clean-webpack-plugin: Используется для очистки локальных файлов.При упаковке в производственной среде, если папка dist не очищена, то каждая упаковка будет генерировать разные файлы js или css и накапливаться в папке, потому что каждая упаковка будет генерировать разные хэши Значение вызывает имя файла, сгенерированное каждым пакетом, должно отличаться от имени последнего пакета и не будет перезаписывать файлы, оставшиеся от последнего пакета.
- progress-bar-webpack-plugin: При упаковке и компиляции отслеживайте процесс упаковки в виде индикатора выполнения.
- webpack-build-notifier: когда вы переключаетесь на другую страницу после упаковки, в локальной системе появляется всплывающее окно, информирующее вас о результате упаковки (успешно, неудачно или с предупреждением) по завершении.
- html-webpack-plugin: автоматически генерировать html и импортировать упакованные js и css в html-файл по умолчанию.
mini-css-extract-plugin: по умолчанию в файле стиля упаковки webpack код файла стиля будет упакован в bundle.js,mini-css-extract-pluginЭтот плагин может извлекать файл стиля из bundle.js в файл и поддерживает фрагмент css.
add-asset-html-webpack-plugin: Как видно из названия, его роль заключается во введении статических ресурсов css или js вhtml-webpack-pluginв сгенерированном html файле
uglifyjs-webpack-plugin: сокращение кода, используемое для сжатия js (вы можете вызвать поток системы для выполненияМногопоточное сжатие, Оптимизация веб-пакатаСкорость сжатия)
optimize-css-assets-webpack-plugin: сжатие css, в основном используетсяcssnanoКомпрессор (среда выполнения webpack4 имеет встроенный cssnano, поэтому его не нужно устанавливать)
- friendly-errors-webpack-plugin: можно лучше видеть предупреждения и ошибки webapck, работающего в терминале.
happypack: Многопоточная компиляция, ускорить компиляцию (ускорить компиляцию загрузчика), обратите внимание, что thread-loader нельзя использовать в сочетании с mini-css-extract-plugin.
- splitChunks: потомок CommonChunkPlugin, используемый для фрагментирования bundle.js (встроенный плагин webpack).
- DllPlugin: Модули предварительно скомпилированы, он будет компилироваться в первый раз, нужно будет предварительно сконфигурировать модули, скомпилированные в кеше, второй раз скомпилировать, разрешить эти модули напрямую с помощью кеша, а не компилировать эти модули (встроенные модули webpack )
- DllReferencePlugin: Свяжите предварительно скомпилированные модули с текущей компиляцией.Когда веб-пакет анализирует эти модули, предварительно скомпилированные модули (встроенные плагины веб-пакета) будут использоваться напрямую.
- HotModuleReplacementPlugin: реализовать локальную горячую загрузку (обновление), которая отличается от глобального обновления в webpack-dev-server (встроенный плагин webpack).
5. Конфигурация файла, связанная с Webpack
Следующие файлы могут быть использованы непосредственно в вашей копии проекта
webpack.config.js
const path = require('path')
const webpack = require('webpack')
const os = require('os')
const merge = require('webpack-merge')
const argv = require('yargs-parser')(process.argv.slice(2))
const mode = argv.mode || 'development'
const interface = argv.interface || 'development'
const isEslint = !!argv.eslint
const isDev = mode === 'development'
const mergeConfig = require(`./config/webpack.${mode}.js`)
const CleanWebpackPlugin = require('clean-webpack-plugin')
const ProgressBarPlugin = require('progress-bar-webpack-plugin')
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin")
const WebpackBuildNotifierPlugin = require('webpack-build-notifier')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
const FirendlyErrorePlugin = require('friendly-errors-webpack-plugin')
const HappyPack = require('happypack')
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })
const smp = new SpeedMeasurePlugin()
const loading = { html:"加载中..."}
const apiConfig = {
development: 'http://xxxxx/a',
production: 'http://xxx/b'
}
let commonConfig = {
module: {
rules: [{
test: /\.js$/,
loaders: ['happypack/loader?id=babel'],
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
},{
test: /\.css$/,
loaders: [
MiniCssExtractPlugin.loader,
'css-loader'
]
},{
test: /\.less$/,
loaders: [
isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
{
loader:'less-loader?sourceMap=true',
options:{
javascriptEnabled: true
},
}
// include: path.resolve(__dirname, 'src')
]
},{
test: /\.(png|svg|jpg|gif)$/,
use: [
'url-loader'
]
},{
test: /\.(woff|woff2|eot|ttf|otf|ico)$/,
use: [
'file-loader'
]
},{
test: /\.(csv|tsv)$/,
use: [
'csv-loader'
]
},{
test: /\.xml$/,
use: [
'xml-loader'
]
},{
test: /\.md$/,
use: [
"html-loader",
"markdown-loader"
]
}]
},
//解析 resolve: {
extensions: ['.js', '.jsx'], // 自动解析确定的扩展
},
plugins: [
new HappyPack({
id: 'babel',
loaders: [{
loader: 'babel-loader',
options: {
cacheDirectory: true,
presets: ['@babel/preset-env', '@babel/preset-react'],
plugins: [
['@babel/plugin-proposal-decorators', { "legacy": true }],
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-export-default-from',
'@babel/plugin-transform-runtime',
// 'react-hot-loader/babel',
// 'dynamic-import-webpack',
['import',{
libraryName:'antd',
libraryDirectory: 'es',
style:true
}]
]
}
}],
//共享进程池
threadPool: happyThreadPool,
//允许 HappyPack 输出日志
verbose: true,
}),
new CleanWebpackPlugin(['dist']),
new ProgressBarPlugin(),
new WebpackBuildNotifierPlugin({
title: "xxx后台管理系统🍎",
logo: path.resolve(__dirname, "src/static/favicon.ico"),
suppressSuccess: true
}),
new webpack.DefinePlugin({
'process.env' : {
'NODE_ENV' : JSON.stringify(mode)
},
'NODE_ENV' : JSON.stringify(mode),
'baseUrl': JSON.stringify(apiConfig[interface]),
'__DEV__' : mode === 'development',
'__PROD__' : mode === 'production',
'__TEST__' : mode === 'test',
'__DEBUG__' : mode === 'development' && !argv.no_debug,
'__DEBUG_NEW_WINDOW__' : !!argv.nw,
'__BASENAME__' : JSON.stringify(process.env.BASENAME || '')
}),
new FirendlyErrorePlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/index.html'),
favicon: path.resolve(__dirname, 'public/favicon.ico'),
filename: 'index.html',
loading
}),
new MiniCssExtractPlugin({
filename: isDev ? 'styles/[name].[hash:4].css' : 'styles/[name].[hash:8].css',
chunkFilename:isDev ? 'styles/[name].[hash:4].css' : 'styles/[name].[hash:8].css'
}),
// 告诉 Webpack 使用了哪些动态链接库
new webpack.DllReferencePlugin({
// 描述 vendor 动态链接库的文件内容
manifest: require('./public/vendor/vendor.manifest.json')
}),
// 该插件将把给定的 JS 或 CSS 文件添加到 webpack 配置的文件中,并将其放入资源列表 html webpack插件注入到生成的 html 中。
new AddAssetHtmlPlugin([
{
// 要添加到编译中的文件的绝对路径,以及生成的HTML文件。支持 globby 字符串
filepath: require.resolve(path.resolve(__dirname, 'public/vendor/vendor.dll.js')),
// 文件输出目录
outputPath: 'vendor',
// 脚本或链接标记的公共路径
publicPath: 'vendor'
}
]),
new webpack.HotModuleReplacementPlugin()
],
devServer: {
host: 'localhost',
port: 8080,
historyApiFallback: true,
overlay: {//当出现编译器错误或警告时,就在网页上显示一层黑色的背景层和错误信息
errors: true
},
inline: true,
open: true,
hot: true
},
performance: {
// false | "error" | "warning" // 不显示性能提示 | 以错误形式提示 | 以警告...
hints: false, // 开发环境设置较大防止警告
// 根据入口起点的最大体积,控制webpack何时生成性能提示,整数类型,以字节为单位
maxEntrypointSize: 50000000,
// 最大单个资源体积,默认250000 (bytes)
maxAssetSize: 30000000
}
}
if (isEslint) {
commonConfig.module.rules.unshift[{
//前置(在执行编译之前去执行eslint-loader检查代码规范,有报错就不执行编译)
enforce: 'pre',
test: /.(js|jsx)$/,
loaders: ['eslint-loader'],
exclude: /node_modules/
}]
}
module.exports = merge(commonConfig, mergeConfig)
Примечание: Плагин speed-measure-webpack-plugin не используется при окончательном экспорте конфигурации, потому что будет сообщено об ошибке Я не знаю, может ли это быть из-за несовместимости с happypack. interface используется для определения адреса текущего упакованного сетевого запроса js, isEslint определяет, нужно ли выполнять обнаружение кода, isDev используется для определения того, является ли текущая среда выполнения разработкой или производством, а конкретная проблема зависит от кода
webpack.config.dll.js
const path = require('path');
const webpack = require('webpack');
const CleanWebpaclPlugin = require('clean-webpack-plugin');
const FirendlyErrorePlugin = require('friendly-errors-webpack-plugin');
module.exports = {
mode: 'production',
entry: {
// 将 lodash 模块作为入口编译成动态链接库
vendor: ['react', 'react-dom', 'react-router', 'react-redux', 'react-router-redux']
},
output: {
// 指定生成文件所在目录
// 由于每次打包生产环境时会清空 dist 文件夹,因此这里我将它们存放在了 public 文件夹下
path: path.resolve(__dirname, 'public/vendor'),
// 指定文件名
filename: '[name].dll.js',
// 存放动态链接库的全局变量名称,例如对应 vendor 来说就是 vendor_dll_lib // 这个名称需要与 DllPlugin 插件中的 name 属性值对应起来
// 之所以在前面 _dll_lib 是为了防止全局变量冲突
library: '[name]_dll_lib'
},
plugins: [
new CleanWebpaclPlugin(['vendor'], {
root: path.resolve(__dirname, 'public')
}),
new FirendlyErrorePlugin(), // 接入 DllPlugin
new webpack.DllPlugin({
// 描述动态链接库的 manifest.json 文件输出时的文件名称
// 由于每次打包生产环境时会清空 dist 文件夹,因此这里我将它们存放在了 public 文件夹下
path: path.join(__dirname, 'public', 'vendor', '[name].manifest.json'),
// 动态链接库的全局变量名称,需要和 output.library 中保持一致
// 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
// 例如 vendor.manifest.json 中就有 "name": "vendor_dll_lib" name: '[name]_dll_lib'
})
],
performance: {
// false | "error" | "warning" // 不显示性能提示 | 以错误形式提示 | 以警告...
hints: "warning", // 开发环境设置较大防止警告
// 根据入口起点的最大体积,控制webpack何时生成性能提示,整数类型,以字节为单位
maxEntrypointSize: 5000000, // 最大单个资源体积,默认250000 (bytes)
maxAssetSize: 3000000
}}
бегатьnpm run dll
После инструкции видно, что в публичном каталоге проекта есть дополнительная папка vendor, и видно, что в ней два файла:
-
vendor.dll.js
содержитreact react-dom react-router react-redux react-router-redux
Базовая операционная среда , поместите эти основные модули в пакет, пока версия пакета этих пакетов не будет обновлена, вам не нужно перекомпилировать эти модули каждый раз, когда вы упаковываете, повышая скорость упаковки. -
vendor.manifest.json
Он также генерируется DllPlugin для описания того, какие модули включены в файл библиотеки динамической компоновки.
config/webpack.development.js
module.exports = {
mode: 'development',
//devtool: 'cheap-module-source-map',
devtool: 'eval',
output: {
filename: 'scripts/[name].bundle.[hash:4].js'
}
}
В среде разработки мы не делаем сжатие js и сжатие css для повышения скорости отладки и сохранения упаковки страниц в среде разработки
config/webpack.production.js
const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); //开启多核压缩
const OptmizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const os = require('os');
module.exports = {
mode: 'production',
devtool: 'hidden-source-map',
output: {
filename: 'scripts/[name].bundle.[hash:8].js'
},
optimization: {
splitChunks: {
chunks: 'all', // initial、async和all
minSize: 30000, // 形成一个新代码块最小的体积
maxAsyncRequests: 5, // 按需加载时候最大的并行请求数
maxInitialRequests: 3, // 最大初始化请求数
automaticNameDelimiter: '~', // 打包分割符
name: true,
cacheGroups: {
vendors: { // 项目基本框架等
chunks: 'all',
test: /antd/,
priority: 100,
name: 'vendors',
}
}
},
minimizer: [
new UglifyJsPlugin({
parallel: os.cpus().length,
cache:true,
sourceMap:true,
uglifyOptions: {
compress: {
// 在UglifyJs删除没有用到的代码时不输出警告
warnings: false,
// 删除所有的 `console` 语句,可以兼容ie浏览器
drop_console: true,
// 内嵌定义了但是只用到一次的变量
collapse_vars: true,
// 提取出出现多次但是没有定义成变量去引用的静态值
reduce_vars: true,
},
output: {
// 最紧凑的输出
beautify: false,
// 删除所有的注释
comments: false,
}
}
}),
new OptmizeCssAssetsWebpackPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano'),
cssProcessorOptions: {
safe: true,
discardComments: {
removeAll: true
}
}
})
],
}}
В конфигурации производственной среды выполняется сжатие js и сжатие css, а antd отделяется от упакованного файла записи с помощью splitChunks для уменьшения размера bundle.js.
public/index.html
<!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>
</body>
</html>
package.json
6. Пакетные приложения
1. Выполните npm run dll, чтобы сгенерировать общедоступный/поставщик (вам не нужно выполнять эту команду после упаковки, если версия пакета в поставщике не изменилась)
2. Выполните npm run start:dev для автоматического запуска локального сервера webpack-dev.
3. Выполните npm run deploy, чтобы упаковать производственную среду.
4. Сравнение и анализ времени упаковки
Используйте метод разделенного кода для асинхронной загрузки компонентов для оптимизации объема, см.«Webpack загружает приложение за считанные секунды по требованию»(самый важный шаг)