tl;博士
使用Webpack ,我将一些 2300 个 40 像素 x 30 像素的url-loader
png 图标(小型神奇宝贝精灵)加载到一个数组中,从而生成约 2.2MB 的缩小 JavaScript。这些图像和包装它们的模块几乎从不改变,也没有改变的依赖关系。我想将它们拉到一个单独的 js 文件中,以便在运行时webpack --watch
,每次我更改依赖于该图像数组的代码时,Webpack 都不会触发这些图像的重新提取和缩小,这个过程需要20 秒的顺序。
以前,对于这样的事情,我使用了一个单独的入口点,并且CommonsChunkPlugin
如 Webpack 文档的代码拆分部分中所述。(我不知道这是否应该是这样使用的,但它确实有效。)由于 Webpack 4 弃用了 ,这不再可行CommonsChunkPlugin
,但我不知道如何使用它的替代品config.optimization.splitChunks
, 来实现同样的效果。
出于某种原因,Webpack 弃用了它CommonsChunkPlugin
以支持webpack.optimization.splitChunks
配置,在初始化时使用CommonsChunkPlugin
throw 一个错误,然后没有更新他们的代码拆分文档以反映此更改。我找了几个小时的官方文档splitChunks
以及如何使用它,我找到的最接近的是这个“RIP CommonsChunkPlugin” gist和这个Medium.com 文章,它只给出了这个变化的高级解释。
client/pokemon-icons.js
import 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 的babel-ployfill
and babel-preset-2017
,我们可以使用await
语法。
Promise
句法
import('../pokemon-images').then(icons => {
let icon = icons.getIcon(...);
// ...
});
await
句法
async function getIcon(...args) {
let icons = await import('../pokemon-images');
return icons.getIcon(...args);
}
我无法让这些方法甚至生成一个单独的块文件,更不用说缓存它了。
首先,这两种方法都不是理想的,因为将所有内容包装在回调、承诺和异步函数中是一件痛苦的事情,特别是因为我正在调用getIcon()
类的构造函数,而不能将其标记为异步(这很简单解决这个问题,但仍然令人讨厌)。
我尝试过的 Webpack 配置
我不知道这是否重要,但我正在使用HtmlWebpackPlugin
它生成一个 HTML 文件ejs-loader
,然后插入对插件chunks
选项数组中列出的入口点的引用。
我的配置还编译了两个入口点和两个HtmlWebpackPlugin
需要关联条目的 s。
基础 webpack.babel.config.js
这些是我的 webpack 配置文件的相关部分。
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'],
// ...
}
]