Используя url-loaderWebpack, я загружаю около 2300 иконок в формате PNG размером 40 на 30 пикселей (маленькие спрайты покемонов) в массив, в результате чего получается ~ 2,2 МБ минимизированного JavaScript. Эти образы и модуль, который их обертывает, практически никогда не меняются и не имеют изменяющихся зависимостей. Я хочу вывести их в отдельный файл js, чтобы при запуске webpack --watch каждый раз, когда я меняю код, который зависит от этого массива изображений, Webpack не запускал повторное извлечение и минификацию этих изображений, процесс, который берет на себя порядка 20 секунд.
Раньше для подобных вещей я использовал отдельную точку входа и CommonsChunkPlugin, как описано в разделе Разделение кода документации Webpack. (Я не знаю, так ли было использовано предполагаемый, но он работал.) Это больше не жизнеспособно, поскольку Webpack 4 устарел CommonsChunkPlugin, но я не могу понять, как использовать его замену, config.optimization.splitChunks, для добиться того же эффекта.
По какой-то причине Webpack отказался от своего CommonsChunkPlugin в пользу конфигурации webpack.optimization.splitChunks, которая с помощью CommonsChunkPlugin выдает ошибку при инициализации, а затем не обновляет свой документация по разделению кода, чтобы отразить это изменение. Я пару часов искал официальную документацию по splitChunks и о том, как ее использовать, и ближе всего я нашел эти "RIP CommonsChunkPlugin" суть и Статья на Medium.com, которые дают только высокоуровневые объяснения изменений.
client/pokemon-icons.jsimport Pokedex from 'pokedex'; // has no dependencies
class PokemonIcons {
constructor() {
let req = require.context('../resources/icons/regular', false, /\.png$/);
req.keys().forEach(fn => {
let species = /([-a-zA-Z0-9]+)\.png$/.exec(fn)[1];
this[species] = { regular: req(fn) };
});
req = require.context('../resources/icons/shiny', false, /\.png$/);
req.keys().forEach(fn => {
let species = /([-a-zA-Z0-9]+)\.png$/.exec(fn)[1];
this[species].shiny = req(fn);
});
let egg = require('../resources/icons/egg.png');
this.egg = { regular: egg, shiny: egg };
}
getIcon(pokemon) {
return this[Pokedex.FileNames[pokemon.species]][pokemon.shiny];
}
}
const icons = new PokemonIcons();
export default icons;
В документации есть несколько способов сообщить Webpack о необходимости извлечения кода в собственный файл фрагментов.
require.ensure()«Старый» метод использовал require.ensure(), который принимает массив модулей для загрузки и обратный вызов, вызываемый после загрузки модулей.
client/soullink/index.js примерrequire.ensure(['../pokemon-icons'], () => {
const config = require('config.json');
if (config.someFlag) {
require('./some-js'); // does not rely on `pokemon-icons.js`
} else {
require('./some-other-js'); // has dependencies which rely on `pokemon-icons.js`
}
});
Этот метод действительно создает отдельный файл фрагмента, но этот файл фрагмента по-прежнему восстанавливается каждый раз, когда изменяется другой код.
import()«Новый» метод использует import(), который возвращает Promise. Необязательно, с babel-ployfill и babel-preset-2017 от babel, мы можем использовать синтаксис await.
Promiseimport('../pokemon-images').then(icons => {
let icon = icons.getIcon(...);
// ...
});
awaitasync function getIcon(...args) {
let icons = await import('../pokemon-images');
return icons.getIcon(...args);
}
Я не могу заставить эти методы даже создать отдельный файл фрагмента, не говоря уже о его кешировании.
Ни один из этих методов в первую очередь не идеален, поскольку обертывание всего в обратных вызовах, обещаниях и асинхронных функциях - это боль, тем более что я вызываю getIcon() в конструкторах классов, которые нельзя пометить как асинхронные (это просто достаточно, чтобы обойти это, но все равно неприятно).
Я не знаю, имеет ли это значение, но я использую HtmlWebpackPlugin, который генерирует файл HTML с помощью ejs-loader, а затем вставляет ссылку на точки входа, перечисленные в массиве опций chunks плагина.
Моя конфигурация также компилирует две точки входа и два HtmlWebpackPlugin, для которых требуется соответствующая запись.
Это соответствующие части моего файла конфигурации веб-пакета.
import webpack from 'webpack';
import fs from 'fs';
import path from 'path';
import HtmlWebpackPlugin from 'html-webpack-plugin';
function genConfig(env, options) {
return {
entry: {
index: './client/slot-display/index',
soullink: './client/soullink-manager/index', // this is the entry that requires pokemon-icons.js
vendors: [ 'lodash' ],
},
output: {
filename: '[name].js',
chunkFilename: '[name].js',
path: path.resolve(__dirname, 'public')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.ejs$/,
loader: 'ejs-loader',
},
{
test: /\.png$/,
use: [
'url-loader'
]
}
],
},
plugins: [
new webpack.ProvidePlugin({
_: 'lodash',
}),
new HtmlWebpackPlugin({
template: '!!ejs-loader!./client/slot-display/index.ejs',
filename: 'index.html',
chunks: ['index'],
inject: 'body',
cache: true
}),
new HtmlWebpackPlugin({
template: '!!ejs-loader!./client/soulLink-manager/index.ejs',
filename: 'soullink/index.html',
chunks: ['soullink'],
inject: 'body',
cache: true
})
]
};
}
export default genConfig;
Я пробовал добавить
optimization: {
splitChunks: {
chunks: 'all', // with and without this line
cacheGroups: { // with and without this object
default: false, // with and without this line
pokemonIcons: {
test: /pokemon-icons\.js$/, // with and without this line
name: 'pokemon-icons', // also 'pokemonIcons'
minChunks: Infinity
}
}
}
}
а также
entry: {
pokemonIcons: './client/pokemon-icons', // with and without this separate entry
// the various soullink entries I've tried:
soullink: './client/soullink-manager/index',
soullink: [ './client/pokemon-icons', './client/soullink-manager/index' ],
soullink: [ 'babel-polyfill', './client/soullink-manager/index' ],
soullink: [ 'babel-polyfill', './client/pokemon-icons', './client/soullink-manager/index' ],
}
и при необходимости настройте:
plugins: [
new HtmlWebpackPlugin({
// ...
chunks: ['pokemonIcons', 'soullink'],
// ...
}
]



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Цитата: вам нужен только фрагмент времени выполнения при использовании долгосрочного кэширования и загрузки по запросу.
optimization.runtimeChunk: trueИ я думаю, что вам не нужно указывать поставщика в качестве точки входа, поскольку wp4 имеет собственный алгоритм для его генерации из существующих точек входа, но на эту часть можно повлиять с помощью конфигурацииsplitchunks.