1

tl;博士

使用Webpack ,我将一些 2300 个 40 像素 x 30 像素的url-loaderpng 图标(小型神奇宝贝精灵)加载到一个数组中,从而生成约 2.2MB 的缩小 JavaScript。这些图像和包装它们的模块几乎从不改变,也没有改变的依赖关系。我想将它们拉到一个单独的 js 文件中,以便在运行时webpack --watch,每次我更改依赖于该图像数组的代码时,Webpack 都不会触发这些图像的重新提取和缩小,这个过程需要20 秒的顺序。

以前,对于这样的事情,我使用了一个单独的入口点,并且CommonsChunkPlugin如 Webpack 文档的代码拆分部分中所述。(我不知道这是否应该是这样使用的,但它确实有效。)由于 Webpack 4 弃用了 ,这不再可行CommonsChunkPlugin,但我不知道如何使用它的替代品config.optimization.splitChunks, 来实现同样的效果。


出于某种原因,Webpack 弃用了它CommonsChunkPlugin以支持webpack.optimization.splitChunks配置,在初始化时使用CommonsChunkPluginthrow 一个错误,然后没有更新他们的代码拆分文档以反映此更改。我找了几个小时的官方文档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-ployfilland 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'],
        // ...
    }
]
4

0 回答 0