You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Preact-CLI项目Webpack生产构建Chunk加载报错求助

Fixing "webpackJsonp is not defined" with Custom CommonsChunkPlugin in Preact-CLI

Let's break down what's going wrong here and fix it step by step.

Root Cause

When you add multiple CommonsChunkPlugin instances, Webpack expects the manifest chunk (the one with minChunks: Infinity) to be the last CommonsChunkPlugin you define. This is because it extracts the Webpack runtime code that needs to load all other chunks. Additionally, Preact-CLI's default CommonsChunkPlugin is conflicting with your custom splits, and your chunk sorting logic wasn't properly enforcing the correct load order in the HTML.

Step-by-Step Solution

1. Remove Preact-CLI's Default CommonsChunkPlugin

First, we need to get rid of the default plugin that comes with preact-cli, since it's conflicting with your custom splits:

// Find and remove the default CommonsChunkPlugin
const defaultCommonsChunk = helpers.getPluginsByName(config, 'CommonsChunkPlugin')[0];
if (defaultCommonsChunk) {
    config.plugins.splice(config.plugins.indexOf(defaultCommonsChunk.plugin), 1);
}

2. Reorder Your Custom CommonsChunkPlugins

Make sure your manifest chunk is the last one defined. This ensures Webpack correctly extracts the runtime after processing all other split chunks:

if(process.env.NODE_ENV === "production") {
    // Split vendor code (node_modules except specific libs)
    config.plugins.push(new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor',
        minChunks: ({ resource }) => (
            resource !== undefined && resource.indexOf('node_modules') !== -1
                && !/preact|preact-compat|redux|preact-redux|redux-form|lodash|sane-email-validation|moment|core-decorators|lodash-decorators|material-design-lite|preact-mdl/.test(resource)
        ),
    }));

    // Split Preact-related code
    config.plugins.push(new webpack.optimize.CommonsChunkPlugin({
        name: 'preact-chunk',
        filename: 'static/js/[name].[chunkhash:8].js',
        minChunks: (m) => m.resource && /preact|preact-compat/.test(m.resource)
    }));

    // Split other utility libs
    config.plugins.push(new webpack.optimize.CommonsChunkPlugin({
        name: 'others-chunk',
        filename: 'static/js/[name].[chunkhash:8].js',
        minChunks: (m) => m.resource && /redux|preact-redux|redux-form|lodash|sane-email-validation|moment|core-decorators|lodash-decorators/.test(m.resource)
    }));

    // Split Material-UI related code
    config.plugins.push(new webpack.optimize.CommonsChunkPlugin({
        name: 'material-ui-chunk',
        filename: 'static/js/[name].[chunkhash:8].js',
        minChunks: (m) => m.resource && /material-design-lite|preact-mdl/.test(m.resource)
    }));

    // Manifest chunk MUST be last
    config.plugins.push(new webpack.optimize.CommonsChunkPlugin({
        name: 'manifest',
        filename: 'static/js/[name].[chunkhash:8].js',
        minChunks: Infinity,
    }));
}

3. Fix Chunk Sorting in HtmlWebpackPlugin

Your previous sorting function was too simplistic — it only prioritized the manifest but didn't handle the rest of the chunks correctly. Use this improved sorting logic to ensure manifest loads first, followed by your split chunks, then the main bundle:

htmlPlugin.options.chunksSortMode = (entry1, entry2) => {
    // Define the order we want chunks to load
    const chunkOrder = ['manifest', 'preact-chunk', 'material-ui-chunk', 'others-chunk', 'vendor', 'bundle'];
    return chunkOrder.indexOf(entry1.names[0]) - chunkOrder.indexOf(entry2.names[0]);
};

4. Ensure ScriptExtHtmlWebpackPlugin Uses Defer Correctly

Keep defer as the default attribute (async can cause race conditions with chunk loading). We just need to make sure the plugin applies it consistently:

const scriptExtPlugin = helpers.getPluginsByName(config, 'ScriptExtHtmlWebpackPlugin')[0];
if (scriptExtPlugin) {
    scriptExtPlugin.plugin.options.defaultAttribute = 'defer';
}

Full Modified preact.config.js

Putting it all together, here's the complete config:

import webpack from 'webpack';
import path from 'path';
import { InterpolateHtmlPlugin } from 'react-dev-utils';
import WatchMissingNodeModulesPlugin from 'react-dev-utils/WatchMissingNodeModulesPlugin';

export default (config, env, helpers) => {
    // Get and modify babel loader config
    let { rule } = helpers.getLoadersByName(config, 'babel-loader')[0];
    let babelConfig = rule.options;
    babelConfig.plugins.push('transform-decorators-legacy');
    babelConfig.presets.push('flow');
    rule.options.plugins.push('transform-regenerator');
    rule.options.plugins.push(["transform-runtime", { "helpers": false, "polyfill": false, "regenerator": true }]);

    // Add custom plugins
    const envs = env; // Assuming envs is available from your setup
    const paths = { appNodeModules: path.resolve(__dirname, 'node_modules') };
    config.plugins.push(new InterpolateHtmlPlugin(envs.raw));
    config.plugins.push(new webpack.DefinePlugin(envs.stringified));
    config.plugins.push(new WatchMissingNodeModulesPlugin(paths.appNodeModules));

    // Modify HtmlWebpackPlugin
    let htmlPlugin = helpers.getPluginsByName(config, 'HtmlWebpackPlugin')[0].plugin;
    htmlPlugin.options.template = `!!ejs-loader!${path.resolve(__dirname, './template.html')}`;
    htmlPlugin.options.title = "JApp";
    htmlPlugin.options.manifest = Object.assign({}, { name: 'JH', short_name: 'JApp', theme_color: '#007DC5' });

    // Remove default CommonsChunkPlugin
    const defaultCommonsChunk = helpers.getPluginsByName(config, 'CommonsChunkPlugin')[0];
    if (defaultCommonsChunk) {
        config.plugins.splice(config.plugins.indexOf(defaultCommonsChunk.plugin), 1);
    }

    // Configure custom CommonsChunkPlugins (production only)
    if(process.env.NODE_ENV === "production") {
        config.plugins.push(new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor',
            minChunks: ({ resource }) => (
                resource !== undefined && resource.indexOf('node_modules') !== -1
                    && !/preact|preact-compat|redux|preact-redux|redux-form|lodash|sane-email-validation|moment|core-decorators|lodash-decorators|material-design-lite|preact-mdl/.test(resource)
            ),
        }));

        config.plugins.push(new webpack.optimize.CommonsChunkPlugin({
            name: 'preact-chunk',
            filename: 'static/js/[name].[chunkhash:8].js',
            minChunks: (m) => m.resource && /preact|preact-compat/.test(m.resource)
        }));

        config.plugins.push(new webpack.optimize.CommonsChunkPlugin({
            name: 'others-chunk',
            filename: 'static/js/[name].[chunkhash:8].js',
            minChunks: (m) => m.resource && /redux|preact-redux|redux-form|lodash|sane-email-validation|moment|core-decorators|lodash-decorators/.test(m.resource)
        }));

        config.plugins.push(new webpack.optimize.CommonsChunkPlugin({
            name: 'material-ui-chunk',
            filename: 'static/js/[name].[chunkhash:8].js',
            minChunks: (m) => m.resource && /material-design-lite|preact-mdl/.test(m.resource)
        }));

        config.plugins.push(new webpack.optimize.CommonsChunkPlugin({
            name: 'manifest',
            filename: 'static/js/[name].[chunkhash:8].js',
            minChunks: Infinity,
        }));
    }

    // Fix chunk sorting
    htmlPlugin.options.chunksSortMode = (entry1, entry2) => {
        const chunkOrder = ['manifest', 'preact-chunk', 'material-ui-chunk', 'others-chunk', 'vendor', 'bundle'];
        return chunkOrder.indexOf(entry1.names[0]) - chunkOrder.indexOf(entry2.names[0]);
    };

    // Ensure ScriptExtHtmlWebpackPlugin uses defer
    const scriptExtPlugin = helpers.getPluginsByName(config, 'ScriptExtHtmlWebpackPlugin')[0];
    if (scriptExtPlugin) {
        scriptExtPlugin.plugin.options.defaultAttribute = 'defer';
    }
};

Why This Works

  • Removing the default CommonsChunkPlugin eliminates conflicts between preact-cli's default split and your custom splits.
  • Ordering CommonsChunkPlugins correctly ensures the manifest chunk (which contains webpackJsonp) is generated last, capturing the runtime needed to load all other chunks.
  • Explicit chunk sorting forces the manifest to load first in the HTML, so webpackJsonp is defined before the main bundle tries to use it.
  • Using defer ensures scripts load in the order they appear in the HTML, preventing race conditions.

内容的提问来源于stack exchange,提问作者Vineet 'DEVIN' Dev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:09:59