этот учебникгитхаб-адресЕсли это поможет вам, пожалуйста, как это.
Несмотря на нынешнюю эру больших интерфейсов, большинство веб-приложений были созданы单页应用(spa)
, но в некоторых сценариях наш проект все же можно сделать多页应用(mpa)
. Прежде чем продолжить чтение, вам необходимо иметь определенное представление об основных концепциях webpack, nodejs и npm.Если вы не знакомы с ним, перейдите на соответствующие официальные сайты.webpack,nodejs,npm.
В этой статье шаг за шагом объясняются следующие аспекты:
- структура каталогов проекта mpa
- Самая простая конфигурация многостраничного портала
- Сценарий автоматически генерирует конфигурацию многостраничной записи.
- Общая конфигурация загрузчика
- Общее разделение кода и оптимизация сборки пакетов.
- Используйте contentHash для постоянного кэширования
Структура каталогов проекта
Создайте новую папку с произвольным именем, предполагая, что это имя webpack-mpa. Затем перейдите в этот каталог и выполните командуnpm init
, Просто нажмите Enter до упора и создайте папки в приложении в соответствии со следующей структурой каталогов. Исходный код разработки размещается в подкаталоге приложения. Ресурсы в каталоге приложения — это ресурсы, необходимые для проекта.Каталог страниц является ядром этого приложения: несколько записей, Файлы конфигурации веб-пакета хранятся в каталоге configs, dist — это выходной каталог упаковки, а scripts — это каталог хранения для сценариев запуска и упаковки.
├── README.md
├── app // 开发源代码目录
│ ├── assets // 资源目录
│ │ ├── images
│ │ └── styles
│ │ ├── common.less
│ └── pages // 多页面入口目录
│ ├── entry1
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── index.less
│ └── entry2
│ ├── index.html
│ ├── index.js
│ └── index.less
├── configs // webpack配置文件目录
│ ├── env.js
│ ├── module.js
│ ├── optimization.js
│ ├── plugins.js
│ ├── webpack.config.babel.js
│ └── webpackDevServer.config.babel.js
├── dist // 打包输出目录
├── package.json
├── scripts // 启动和打包脚本
│ ├── build.js
│ ├── devServer.js
│ └── start.js
├── yarn-error.log
├── yarn.lock
Простейшая конфигурация многостраничной записи
Подготовка к запуску проекта
Начиная с webpack4 файл конфигурации заканчивается на xxx.babel.js, вы можете использовать синтаксис модуля es6 напрямую, но вам нужно установить @babel/register @babel/core @babel/reset-env. Кроме того, добавьте плагин cross-env для установки переменных среды на разных платформах.
yarn add webpack webpack-cli cross-env @babel/register @babel/core @babel/preset-env --dev
После добавления основных зависимостей создайте файл .babelrc в корневом каталоге.
.babelrc
{
"presets": [
[
"@babel/preset-env"
]
]
}
Создайте новый файл webpack.config.babel.js в каталоге configs.
webpack.config.babel.js
import path from "path";
const { NODE_ENV } = process.env;
export default {
target: "web", // 构建的项目运行平台。有web electron node等可选
mode: NODE_ENV // webpack运行模式,默认为production
}
entry
webpack entryЕсть три способа записи: объект, строка, массив. В этом примере используется обозначение объекта. Добавьте конфигурацию записи в webpack.config.babel.js:
entry: {
entry1: path.resolve(__dirname, "../app/pages/entry1/index.js"), // 入口文件1
entry2: path.resolve(__dirname, "../app/pages/entry2/index.js") // 入口文件2
}
output
В многостраничных приложениях невозможно упаковать все входные файлы в один выходной файл, поэтому нам необходимо упаковать их в соответствующие выходные файлы отдельно по соответствующим входным файлам.
Добавьте конфигурацию вывода упаковки в webpack.config.babel.js:
output: {
path: path.resolve(__dirname, "../dist"),
filename: "[name].js"
}
[имя] в имени файла — это подстановочный знак фрагмента, фрагмент может быть просто понят как имя файла записи, а файл входа — это фрагмент. В этом примере есть два фрагмента: entry1 и entry2. Поэтому выходные файлы после упаковки: entry1.js, entry2.js.
Теперь наш webpack.config.babel.js становится таким
import path from "path";
const { NODE_ENV } = process.env;
export default {
target: "web",
mode: NODE_ENV,
entry: {
entry1: path.resolve(__dirname, "../app/pages/entry1/index.js"),
entry2: path.resolve(__dirname, "../app/pages/entry2/index.js")
},
output: {
path: path.resolve(__dirname, "../dist"),
filename: "[name].js"
}
}
Для удобства добавьте команду сценария scripts в package.json
"build": "cross-env NODE_ENV=production webpack --config configs/webpack.config.babel.js"
Теперь вы можете запуститьnpm run build
Упакуйте команду и получите вывод, подобный следующему:
Hash: 29c9e7c031516faa1d3e
Version: webpack 4.40.2
Time: 63ms
Built at: 2019-09-14 20:38:15
Asset Size Chunks Chunk Names
entry1.js 972 bytes 0 [emitted] entry1
entry2.js 973 bytes 1 [emitted] entry2
Entrypoint entry1 = entry1.js
Entrypoint entry2 = entry2.js
[0] ./app/pages/entry1/index.js 57 bytes {0} [built]
[1] ./app/pages/entry2/index.js 56 bytes {1} [built]
Вы можете видеть два фрагмента входа, а выходное имя также является именем фрагмента входа после упаковки. Выходной каталог находится в корневом каталоге.
Добавить файл шаблона html
В приведенном выше примере просто упаковывается файл записи js, затем добавляется шаблон html и вставляется файл записи в соответствующий файл шаблона html, что требует использованияhtml-webpack-pluginплагин.
Сначала установите плагин
yarn add html-webpack-plugin clean-webpack-plugin progress-bar-webpack-plugin --dev
Написание всей конфигурации в файле webpack.config.babel.js будет необычайно громоздким, и им будет легче управлять, если отделить оптимизацию подключаемого модуля веб-пакета. Создайте новый файл plugin.js в каталоге configs, используйте clean-webpack-plugin для очистки каталога dist при каждой упаковке и progress-bar-webpack-plugin для отображения хода сборки.
plugin.js
import path from "path";
import HtmlWebpackPlugin from "html-webpack-plugin";
import { CleanWebpackPlugin } from "clean-webpack-plugin";
import ProgressBarPlugin from "progress-bar-webpack-plugin";
const plugins = [
new CleanWebpackPlugin(),
new ProgressBarPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../app/pages/entry1/index.html"),
filename: `entry1.html`,
chunks: ["entry1"]
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../app/pages/entry2/index.html"),
filename: `entry2.html`,
chunks: ["entry2"]
})
]
export default [...plugins];
хорошо, простейший пример сделан. Основной принцип многостраничного приложения состоит в том, чтобы добавить несколько записей, а также упаковать и вывести несколько выходов. В этом примере мы вручную добавляем файл записи и шаблон записи, если страниц будет больше, код конфигурации вебпака будет сильно меняться, а менять конфигурацию каждый раз при добавлении новой страницы очень неудобно. Следующий подраздел, Сценарии для автоматического создания конфигураций многостраничных записей, решит эту проблему.
Скрипт для автоматической генерации конфигурации многостраничной записи
Ключом к автоматическому созданию конфигурации с несколькими входами являетсяДинамическое сканирование всех файлов записей для создания конфигураций записей в соответствии с указанной структурой каталогов и одновременное создание конфигураций подключаемых модулей HTML-шаблонов.. Поскольку каталог приложения читается много раз, в каталоге configs создается новый файл env.js для хранения переменных, которые используются много раз, и для предварительного определения некоторых переменных.
env.js
import path from "path";
export const { NODE_ENV, BUNDLE_ANNALYZE } = process.env;
export const isDevelopment = NODE_ENV === "development";
export const isProduction = NODE_ENV === "production";
export const shouldUseSourceMap = true
export const PORT = process.env.port || 9001;
export const PROTOCOL = process.env.HTTPS === 'true' ? 'https' : 'http';
export const HOST = "127.0.0.1";
export const appPath = path.join(process.cwd(), "./app");
export const isBundleAnalyze = BUNDLE_ANNALYZE === "analyze";
Автоматически генерировать основную логику конфигурации многостраничной записи
plugins.js
import path from "path";
import fs from "fs";
import glob from "glob";
import HtmlWebpackPlugin from "html-webpack-plugin";
import { CleanWebpackPlugin } from "clean-webpack-plugin";
import ProgressBarPlugin from "progress-bar-webpack-plugin";
import {
appPath,
} from "./env";
function getEntry () {
let entry = {};
glob.sync(path.resolve(appPath, "pages/**/index.js"))
.forEach(function (fileDir) {
let pathObj = path.parse(fileDir);
// 用文件夹名字作为入口名。
let entryName = pathObj.dir.match(/\/\w+$/g)[0].split("/")[1];
entry[entryName] = fileDir;
});
return entry;
};
const entry = getEntry();
const plugins = [
new CleanWebpackPlugin(),
new ProgressBarPlugin()
];
function getHtmlWebpackPluginConfigs () {
const res = [];
for (let [entryName] of Object.entries(entry)) {
const htmlFilePath = `${appPath}/pages/${entryName}/index.html`;
if (!fs.existsSync(htmlFilePath)) {
throw new Error(`file: ${htmlFilePath} not exist`);
}
const plugin = new HtmlWebpackPlugin({
template: htmlFilePath,
filename: `${entryName}.html`,
chunks: [entryName]
});
res.push(plugin);
}
return res;
}
export { entry };
export default [...plugins, ...getHtmlWebpackPluginConfigs()];
Модифицированный webpack.config.babel.js
import path from "path";
import plugins, { entry } from "./plugins";
const { NODE_ENV } = process.env;
export default {
target: "web",
mode: NODE_ENV,
entry,
output: {
path: path.resolve(__dirname, "../dist"),
filename: "[name].js"
},
plugins
}
В функции getEntry подключаемый модуль glob используется для сканирования index.js в каталоге pages в каталоге приложения, а каталог, содержащий indexjs, используется в качестве имени записи для получения конфигурации записи, а затем функция getHtmlWebpackPluginConfigs просматривает все конфигурации записи. для создания конфигурации плагина шаблона html. Это правило сканирования требует, чтобы все записи страниц находились в каталоге страниц первого уровня и существовали в виде папок. Имя папки используется в качестве имени конфигурации записи. Следующее должно содержать index.js и index.html в качестве реального файл ввода и файл шаблона html. .
Создайте новую запись файла3 в каталоге pages, создайте в нем новые index.html и index.js и выполнитеnpm run build
Вы можете видеть, что в каталоге dist уже есть три файла html и три файла js.
dist
├── entry1.html
├── entry1.js
├── entry2.html
├── entry2.js
├── entry3.html
└── entry3.js
Конечно, правила каталога и правила сканирования могут быть настроены и изменены в соответствии с ситуацией каждого отдельного и конкретного проекта. На данный момент общая структура многостраничного приложения webpack построена.Вы должны понимать идеи ввода и вывода webpack, а затем динамически сканировать файл как запись.сосредоточиться на мысли.
конфигурация среды es2015+
Самая мощная часть вебпака — это различные плагины и лодаеры (на самом деле, это еще и самое гадкое место~~~). Разные люди и проекты должны быть настроены в соответствии с реальными потребностями.Вот некоторые основные конфигурации загрузчика.
Создайте новый module.js в configs.js и напишите некоторые общие базовые конфигурации загрузчика.
module.js
import {
appPath,
isDevelopment,
isProduction,
shouldUseSourceMap
} from "./env";
import PostCssPresetEnv from "postcss-preset-env";
import PostcssFlexBugsfixes from "postcss-flexbugs-fixes";
import friendlyFormatter from "eslint-formatter-friendly"
const postCssLoaderConfig = {
loader: "postcss-loader",
options: {
ident: 'postcss',
plugins: () => [
PostcssFlexBugsfixes,
PostCssPresetEnv({
autoprefixer: {
flexbox: 'no-2009',
overrideBrowserslist: [
"last 100 version"
]
},
stage: 3,
})
],
sourceMap: isProduction && shouldUseSourceMap,
},
}
export default {
rules: [
{ // 这个要先与babel-loader之前定义
enforce: "pre",
test: /\.js$/,
exclude: /node_modules/,
loader: "eslint-loader",
options: {
formatter: friendlyFormatter
}
}, {
test: /\.js$/,
include: appPath,
use: "babel-loader"
}, {
test: /\.css$/,
use: [
isDevelopment && "style-loader",
"css-loader",
postCssLoaderConfig
].filter(Boolean)
}, {
test: /\.less$/,
include: appPath,
use: [
isDevelopment && "style-loader",
"css-loader",
"less-loader",
postCssLoaderConfig
].filter(Boolean)
}, {
test: /\.(png\jpe?g|gif)$/,
use: ["file-loader"]
}, {
test: /\.(png|jpg|gif)$/,
use: [{
loader: "url-loader",
options: {
limit: 8 * 1024, // 小于这个时将会已base64位图片打包处理
outputPath: "images"
}
}]
}, {
test: /\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/,
loader: "url-loader",
options: {
limit: 10000
}
}, {
test: /\.html$/,
use: ["html-withimg-loader"] // html中的img标签
}
]
}
Также добавьте .eslintrc.js
module.exports = {
"env": {
"browser": true,
"commonjs": true,
"es6": true
},
"extends": "standard",
"parserOptions": {
"sourceType": "module"
},
"rules": {
"indent": ["error", 2],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error", "always"],
"import/no-duplicates": [0]
}
};
и измените .babelrc
{
"presets": [
[
"@babel/preset-env", {
"corejs": 3,
"targets": {
"browsers": "> 0.25%"
},
"useBuiltIns": "usage"
}
]
],
"plugins": [
["@babel/plugin-transform-runtime"]
]
}
Слишком много загрузчиков и это слишком сложно Слишком много плагинов для установки Вы можете обратиться к моему github.package.json, правильно скопировать все плагины. Конкретная конфигурация различных плагинов пропущена, а официальный туториал очень хорош.
Общее разделение кода && Оптимизация сборки сборки
Для удобства проекта теперь в WebPack.Config.babel.js добавлена разрешающая конфигурация
resolve: {
extensions: [".js", ".css", ".less", ".json"],
alias: {
"@": appPath,
}
}
Теперь мы добавляем следующий код в js ниже entry1 и entry2.
import _ from "lodash";
import $ from "jquery";
import "reset.css";
import "bootstrap/dist/css/bootstrap.min.css";
console.log(_, $);
async function test () {
console.log("start");
}
test();
Конфигурация оптимизации в webpack4, как следует из названия, означает оптимизацию, и статей в ней действительно много.По сравнению с 1, 2 и 3, webpack4 добился явного прогресса. Создайте новую оптимизацию.js
import { isProduction, shouldUseSourceMap } from "./env";
import TerserWebpackPlugin from "terser-webpack-plugin";
export default {
minimizer: [
// This is only used in production mode
isProduction && new TerserWebpackPlugin({
// Use multi-process parallel running to improve the build speed
// Default number of concurrent runs: os.cpus().length - 1
parallel: false,
// Enable file caching
cache: false,
sourceMap: shouldUseSourceMap,
}),
].filter(Boolean)
}
Текущая оптимизация.js по-прежнему очень проста, просто добавлен плагин сжатия. Отныне мы должны не только разбивать код, но и проверять время упаковки после каждого шага оптимизации. Запустить статистику упаковки в первый раз:
Build completed in 6.304s
Hash: 74a31df5d396423a4b19
Version: webpack 4.40.2
Настоящим поясняю: мой компьютер - система macos 10.13.6, а процессор - intel i7 8700 (черное яблоко, которое лучше, чем белое яблоко).У каждого компьютера процессор, память, система и т. д. разные.Результаты для справки. Только процент сокращения времени после оптимизации подсборки является основным.
Включить многоядерный сжатый js
Плагин TerserWebpackPluginparallel
Параметр указывает, следует ли запускать многопоточность.Настройка по умолчанию — количество потоков процессора минус 1. Теперь установите для него значение true. Продолжить упаковку:
Build completed in 4.185s
Hash: 74a31df5d396423a4b19
Version: webpack 4.40.2
Значительное улучшение...
включить кеш
Плагин TerserWebpackPlugincache
Указывает, следует ли включать кеш. Если в следующем процессе сборки будет найден неизмененный фрагмент, кеш будет прочитан напрямую, чтобы уменьшить количество повторных пакетов и сэкономить время. Установите значение true, чтобы продолжить упаковку:
Build completed in 1.887s
Hash: 74a31df5d396423a4b19
Version: webpack 4.40.2
Обратите внимание, что после открытия кеша первый пакет все еще составляет около 4,2 с, как и раньше.Поскольку кеш не открывался раньше, нет кеша для чтения, а во второй раз для упаковки есть кеш для чтения, и время сильно сокращается.
разделение кода
По предыдущим результатам упаковки просмотр каталога dist выглядит следующим образом:
-rw-r--r-- 1 lijialin staff 296B 9 14 23:25 entry1.html
-rw-r--r-- 1 lijialin staff 372K 9 14 23:25 entry1.js
-rw-r--r-- 1 lijialin staff 296B 9 14 23:25 entry2.html
-rw-r--r-- 1 lijialin staff 372K 9 14 23:25 entry2.js
-rw-r--r-- 1 lijialin staff 296B 9 14 23:25 entry3.html
-rw-r--r-- 1 lijialin staff 955B 9 14 23:25 entry3.js
Внимательное наблюдение показало, что js-файлы entry1.js и entry2.js очень большие и оба содержат jquery и lodash, а также reset.css и bootstrap.css. Сейчас нужно сделать две вещи:
- Извлечь общедоступный код
- css разделение и упаковка
До webpack4 для извлечения общедоступного кода использовался CommonsChunkPlugin. Этот подключаемый модуль был удален из webpack4. В новой версии для разделения используется SplitChunksPlugin. Это параметр оптимизации, который поставляется с веб-пакетом и настраивает атрибут при оптимизации.
optimization.js
import { isProduction, shouldUseSourceMap } from "./env";
import TerserWebpackPlugin from "terser-webpack-plugin";
import OptimizeCSSAssetsPlugin from "optimize-css-assets-webpack-plugin";
export default {
splitChunks: {
cacheGroups: {
vendor: { // 抽离第三方插件
test: /node_modules/, // 指定是node_modules下的第三方包
chunks: "initial",
name: "vendor", // 打包后的文件名,任意命名
priority: 10, // 设置优先级,防止和自定义的公共代码提取时被覆盖,不进行打包
},
common: { // 抽离自己写的公共代码,common这个名字可以随意起
chunks: "all",
name: "common", // 任意命名
minSize: 0, // 只要大小超出设置的这个数值,就生成一个新包
minChunks: 2,
priority: 9
}
}
},
minimizer: [
// This is only used in production mode
isProduction && new TerserWebpackPlugin({
// Use multi-process parallel running to improve the build speed
// Default number of concurrent runs: os.cpus().length - 1
parallel: true,
// Enable file caching
cache: true,
sourceMap: shouldUseSourceMap,
}),
// This is only used in production mode
isProduction && new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
map: shouldUseSourceMap
? {
inline: false,
// `annotation: true` appends the sourceMappingURL to the end of
// the css file, helping the browser find the sourcemap
annotation: true,
}
: false,
},
}),
].filter(Boolean)
}
В module.js измените следующую конфигурацию в загрузчике css и меньше.После модификации некоторые коды выглядят следующим образом:
{
test: /\.css$/,
use: [
isDevelopment && "style-loader",
isProduction && {
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "../"
}
},
"css-loader",
postCssLoaderConfig
].filter(Boolean)
}, {
test: /\.less$/,
include: appPath,
use: [
isDevelopment && "style-loader",
isProduction && {
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "../"
}
},
"css-loader",
"less-loader",
postCssLoaderConfig
].filter(Boolean)
}
Заодно модифицируем плагины и добавим MiniCssExtractPlugin:
const plugins = [
new CleanWebpackPlugin(),
new ProgressBarPlugin(),
isProduction && new MiniCssExtractPlugin({
filename: 'css/[name].css',
chunkFilename: 'css/[id].css'
}),
].filter(Boolean);
Здесь мы делаем две важные вещи: во-первых, разделяем код и разделяем общий CSS и js в один файл, а во-вторых, извлекаем общий CSS из разделенного общедоступного файла. О вебпаке4splitChunksКонкретные правила лучше представить на официальном сайте.
Создайте новый файл common.less в каталоге assets приложения, небрежно напишите код, а затем одновременно введите файл less для entry1 и entry2.
Упакуйте его снова.После упаковки каталог dist выглядит следующим образом:
-rw-r--r-- 1 lijialin staff 111B 9 15 00:42 0.css
-rw-r--r-- 1 lijialin staff 388B 9 15 00:42 0.css.map
-rw-r--r-- 1 lijialin staff 206K 9 15 00:42 1.css
-rw-r--r-- 1 lijialin staff 286K 9 15 00:42 1.css.map
-rw-r--r-- 1 lijialin staff 80B 9 15 00:42 common.js
-rw-r--r-- 1 lijialin staff 296B 9 15 00:42 entry1.html
-rw-r--r-- 1 lijialin staff 1.9K 9 15 00:42 entry1.js
-rw-r--r-- 1 lijialin staff 296B 9 15 00:42 entry2.html
-rw-r--r-- 1 lijialin staff 1.9K 9 15 00:42 entry2.js
-rw-r--r-- 1 lijialin staff 296B 9 15 00:42 entry3.html
-rw-r--r-- 1 lijialin staff 955B 9 15 00:42 entry3.js
-rw-r--r-- 1 lijialin staff 160K 9 15 00:42 vendor.js
Общие js и css были разделены отдельно. 0.css — это контент в common.less, который мы написали Хотя он небольшой, он также разделен и контролируется minSize в разделенных чанках. 1.css представляет собой комбинацию reset.css и bootstrap.css.Поскольку все они находятся в node_modules, они упакованы в один файл. vender.js содержит jquery и lodash, а также некоторый код, преобразованный средой выполнения babel. напоминать:Сплитчанк webpack4 — очень важная вещь, вы должны хорошо ее изучить..
После того, как два последовательных пакета модифицируют файл конфигурации, кэш может быть прочитан во второй раз, информация упаковки:
Build completed in 2.86s
Hash: 2449ed9d5b9e289f3001
Version: webpack 4.40.2
Время растет, дробится код, неизбежно будут дополнительные накладные расходы. Если мы настроим Cache и Parallel в TerSerWebPackPlugin, повторить попытку?
Build completed in 5.31s
Hash: 2449ed9d5b9e289f3001
Version: webpack 4.40.2
Видно, что включение кэширования и улучшение многоядерности очень велико, ведь в реальном бизнесе большую часть времени изменяется бизнес-код, и несколько раз изменяется общедоступный код, а сторонний библиотека редко изменяется, например, при обновлении версии.
Извлеките трехстороннюю библиотеку на cdn
Мы представляем bootcdn в качестве примера трехстороннего cdn. Добавьте в index.html записи 1 и 2 соответственно.
<link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>
Удалите ссылку на bootstrap.css в index.js, добавьте внешние зависимости в webpack.config.babael.js
externals: {
jquery: "jQuery"
}
После модификации конфигурационного файла дважды подряд его упаковать, а во второй раз можно прочитать кэш и запаковать информацию:
Build completed in 2.302s
Hash: 15ae6682578c7c9e04e8
Version: webpack 4.40.2
Что ж, он уменьшился на 0,5 с, что все равно неплохо, чем дальше, тем сложнее оптимизировать... Посмотрите, как сейчас выглядит следующая директория dist:
-rw-r--r-- 1 lijialin staff 111B 9 15 01:00 0.css
-rw-r--r-- 1 lijialin staff 388B 9 15 01:00 0.css.map
-rw-r--r-- 1 lijialin staff 807B 9 15 01:00 1.css
-rw-r--r-- 1 lijialin staff 1.5K 9 15 01:00 1.css.map
-rw-r--r-- 1 lijialin staff 80B 9 15 01:00 common.js
-rw-r--r-- 1 lijialin staff 472B 9 15 01:00 entry1.html
-rw-r--r-- 1 lijialin staff 1.9K 9 15 01:00 entry1.js
-rw-r--r-- 1 lijialin staff 472B 9 15 01:00 entry2.html
-rw-r--r-- 1 lijialin staff 1.9K 9 15 01:00 entry2.js
-rw-r--r-- 1 lijialin staff 296B 9 15 01:00 entry3.html
-rw-r--r-- 1 lijialin staff 955B 9 15 01:00 entry3.js
-rw-r--r-- 1 lijialin staff 76K 9 15 01:00 vendor.js
После извлечения jquery и загрузки cdn размер вендора сильно уменьшился.Поскольку в наших бизнес-файлах почти нет кода, код, требуемый плагином @babel/tranform-runtime, тоже должен быть очень маленьким, так что можно сделать вывод что это в основном код lodash. В реальной разработке невозможно ссылаться на весь lodash, например, если мы используем clonedeep, мы будем ссылаться на него отдельно. lodash предоставляет версию es, которую, наконец, можно загрузить по запросу.
import { cloneDeep } from "lodash-es";
...
console.log(cloneDeep, $);
...
test();
Измените код в entry1 и entry2 и снова упакуйте
Build completed in 1.718s
Hash: ece442821ba575310bbd
Version: webpack 4.40.2
дист-файл
-rw-r--r-- 1 lijialin staff 111B 9 15 01:13 0.css
-rw-r--r-- 1 lijialin staff 388B 9 15 01:13 0.css.map
-rw-r--r-- 1 lijialin staff 807B 9 15 01:13 1.css
-rw-r--r-- 1 lijialin staff 1.5K 9 15 01:13 1.css.map
-rw-r--r-- 1 lijialin staff 81B 9 15 01:13 common.js
-rw-r--r-- 1 lijialin staff 472B 9 15 01:13 entry1.html
-rw-r--r-- 1 lijialin staff 1.9K 9 15 01:13 entry1.js
-rw-r--r-- 1 lijialin staff 472B 9 15 01:13 entry2.html
-rw-r--r-- 1 lijialin staff 1.9K 9 15 01:13 entry2.js
-rw-r--r-- 1 lijialin staff 296B 9 15 01:13 entry3.html
-rw-r--r-- 1 lijialin staff 955B 9 15 01:13 entry3.js
-rw-r--r-- 1 lijialin staff 20K 9 15 01:13 vendor.js
Время снова сокращается, и размер общих зависимостей vendor.js значительно уменьшается. До сих пор упаковка и создание многостраничной демонстрации, относящейся к начальной загрузке jquery lodash, были оптимизированы примерно до 1,7 с.Конкретное время, необходимое для конфигурации компьютера каждого человека, отличается, даже если некоторые библиотеки компонентов добавляются позже, и n несколько написаны бизнес-коды (пока вы не пишете вслепую), пока разумно используется трехсторонний cdn, извлекаются публичные ресурсы node_modules и устанавливается кеш, я оцениваю, что время строительства не должно превышают 10 с. Ну, даже если 10 с недостаточно, удвойте 20 с, это не очень долго, по сравнению с некоторыми глупыми Ходы проекта расфасованы по минутам, что тоже неплохо...
постоянный кеш
Каждый из приведенных выше выходных данных упаковки представляет собой имя записи, используемое напрямую, без номера версии оно определенно не будет работать в реальном производстве.
Немного изменим вывод в webpack.config.babel.js:
...
output: {
filename: isDevelopment ? "js/[name].bundle.js" : "js/[name].[contentHash:8].js",
path: isProduction ? path.resolve(__dirname, "../dist") : undefined
}
...
Плагин css, который извлекает вывод, также изменен на следующее: plugins.js часть кода
...
const plugins = [
new CleanWebpackPlugin(),
new ProgressBarPlugin(),
isDevelopment && new webpack.HotModuleReplacementPlugin(),
isProduction && new MiniCssExtractPlugin({
filename: 'css/[name].[contentHash:8].css',
chunkFilename: 'css/[id].[contentHash:8].css'
})
].filter(Boolean);
...
contentHash генерируется из содержимого, которое аналогично md5 файла.Пока содержимое остается неизменным, хэш не изменится, то есть, если содержимое не изменится, имя выходного файла будет тем же, что и для нашего проекта полезно сделать постоянное кэширование. Наконец, не забудьте добавить чанки, разделенные splitChunks в HtmlWebpackPlugin, то есть общий и вендорный.
plugins.js часть кода
...
function getHtmlWebpackPluginConfigs () {
const res = [];
for (let [entryName] of Object.entries(entry)) {
const htmlFilePath = `${appPath}/pages/${entryName}/index.html`;
if (!fs.existsSync(htmlFilePath)) {
throw new Error(`file: ${htmlFilePath} not exist`);
}
const plugin = new HtmlWebpackPlugin({
template: htmlFilePath,
filename: `${entryName}.html`,
chunks: ["vendor", "common", entryName]
});
res.push(plugin);
}
return res;
}
...
конец
Извините за плохое письмо. В этом руководстве основное внимание уделяется объяснению идей, и вы можете указывать на ошибки и давать ценные комментарии.
этот учебникгитхаб-адресЕсли это поможет вам, пожалуйста, как это.