使用webpack的splitChunks选项缓存大文件

时间:2018-03-29 21:55:24

标签: javascript webpack code-splitting webpack-4

TL;博士

使用Webpackurl-loader,我将一些2300 40px x 30px png图标(小型神奇宝贝精灵)加载到一个数组中,产生约2.2MB的缩小JavaScript。这些图像和包装它们的模块实际上永远不会改变,也没有任何可以改变的依赖关系。我想将它们拉入一个单独的js文件,以便在运行webpack --watch时,每次我更改依赖于该图像数组的代码时,Webpack都不会触发这些图像的重新提取和缩小,过程大约需要20秒。

以前,对于这样的事情,我使用了一个单独的入口点和CommonsChunkPlugin,如Webpack文档的Code Splitting部分所述。 (我不知道这是否是假设的使用方式,但它有效。)由于Webpack 4弃用了CommonsChunkPlugin,因此不再可行,但我不能弄清楚如何使用它的替代品config.optimization.splitChunks来达到同样的效果。

由于某种原因,Webpack弃用了CommonsChunkPlugin而使用webpack.optimization.splitChunks配置,使用CommonsChunkPlugin在初始化时抛出错误,然后没有更新他们的code splitting documentation反映这种变化。我找了几个小时的官方文档splitChunks以及如何使用它,我找到的最接近的是"RIP CommonsChunkPlugin" gistMedium.com article,它只给出了对更改的高级解释

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-ployfillbabel-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使用ejs-loader生成HTML文件,然后插入对插件{{1}中列出的入口点的引用选项数组。

我的配置还会编译两个入口点和两个需要相关条目的chunks

Base webpack.babel.config.js

这些是我的webpack配置文件的相关部分。

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' ],
}

0 个答案:

没有答案