Автор 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 загружает приложение за считанные секунды по требованию»(самый важный шаг)