3

我已经成功地让 Webpack 和 CommonsChunkPlugin 将我的代码分成两部分 - 一个包含我的代码库,一个包含从 node_modules 导入的所有内容。那是相对容易的一点。

我试图在我的应用程序中实现的下一件事是动态导入一个“子应用程序”,它具有完全不同的供应商包要求(例如 React - 我的主应用程序不使用它,但我的子应用程序使用它) ,所有这些包都没有出现在主供应商文件中。

如果我添加了 import(),但我的 Webpack 配置相对保持不变,我最终会得到 4 个包

  1. Webpack 运行时
  2. 主应用程序代码库
  3. 供应商捆绑包含在主应用程序代码库中导入的所有内容
  4. 动态导入的包,也包含所有导入的 node_modules :(

这是不可取的。我也希望从我的子应用程序的主代码库中获得“我的代码与供应商代码”的相同好处。理想情况下,我最终会得到 5 个捆绑包,上面列表中的 #4 分为两个。当动态导入在运行时发生时,它会以某种方式神奇地加载到我的子应用程序代码包和随附的子应用程序供应商包中。理想情况下,子供应商捆绑包不包含主供应商捆绑包中存在的任何内容。

在尝试了我在各种博客文章中找到的很多东西之后,我遇到了一种情况,我手动选择了我想包含在单独的供应商包中的 node_modules 目录,但问题是它不会包含他们的自动依赖,所以我的子应用程序包中仍然会有很多 node_modules - 我没有专门导入的那些。

如果我能让它正常工作,那么我想将它复制到我的主应用程序的更多子应用程序中。


更新 1

我的 Webpack 配置分为 3 个文件 - common、dev 和 prod。只有 common 和 dev 与此相关,所以我将在这里分享它们。

webpack.common.js

const webpack = require('webpack');
const path = require('path');

const NameAllModulesPlugin = require('name-all-modules-plugin');

module.exports = {
    entry: {
        /**
         * main.js - our global platform JS
         */
        main: './src/app.js'
    },
    module: {
        loaders: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                query: {
                    presets: [
                        [
                            'env',
                            {
                                'targets': {
                                    'browsers': ['last 3 versions', 'ie >= 11']
                                }
                            }
                        ],
                        'react'
                    ],
                    plugins: [
                        'transform-class-properties',
                        'transform-object-rest-spread',

                        // Followed instructions here to get dynamic imports working
                        // http://docs1.w3cub.com/webpack~2/guides/code-splitting-import/
                        'syntax-dynamic-import',
                        'transform-async-to-generator',
                        'transform-regenerator',
                        'transform-runtime'
                    ]
                }
            }
        ]
    },
    resolve: {
        alias: {
            src: path.resolve(__dirname, 'src/')
        }
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery',
            CodeMirror: 'codemirror'
        }),

        new webpack.NamedModulesPlugin(),

        new webpack.NamedChunksPlugin(),

        new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor',
            minChunks: (m) => /node_modules/.test(m.context)
        }),

        new webpack.optimize.CommonsChunkPlugin({
            name: 'runtime',
            minChunks: Infinity
        }),

        new NameAllModulesPlugin()
    ]
};

webpack.dev.js

const webpack = require('webpack');
const path = require('path');
const merge = require('webpack-merge');

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

const common = require('./webpack.common.js');

module.exports = merge(common, {
    devtool: 'inline-source-map',
    output: {
        filename: '[name].js',
        chunkFilename: '[name]-chunk.js', // used for async chunks (those loaded via import())
        path: path.resolve(__dirname, 'build'),
        publicPath: '/js/build/'
    },
    plugins: [
        // Uncomment and run build, to launch the bundle analyzer webpage
        new BundleAnalyzerPlugin(),

        new webpack.DefinePlugin({
            'process.env': { NODE_ENV: JSON.stringify('dev') }
        })
    ]
});

更新 2

我偶然发现了一个似乎有效的配置。它甚至在实际导入的同时自动加载到子应用的供应商块中。

// For the main app's modules
new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    minChunks: (m, count) => /node_modules/.test(m.context)
}),

// For my sub app's modules
new webpack.optimize.CommonsChunkPlugin({
    name: 'any-name-here', // doesn't appear to be used anywhere, but prevents 'main' from showing up in the chunk filename (?!)
    chunks: ['name-of-dynamic-import'], // this has to be the 'webpackChunkName' you've used within the import() statement
    async: 'name-of-dynamic-import-vendor', // name the chunk filename
    minChunks: (m, count) => /node_modules/.test(m.context)
}),

new webpack.optimize.CommonsChunkPlugin({
    name: 'runtime',
    minChunks: Infinity
}),
4

1 回答 1

0

因此,让我们分解一下您提供的 CommonsChunkPlugin “说明”:

动态供应商块创建

    new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor',
        minChunks: (m) => /node_modules/.test(m.context)
    }),

上面你告诉 webpack 它需要将“x”模块包含到一个名为“vendor”的特定包中,仅基于一个条件。那个条件是这个模块完全解析的路径在node_modules. 但是,如果您查看我们的文档,还有第二个参数:count它被调用并且您可以在您的应用程序中使用它。这很重要,因为现在,如果您有 20 个延迟加载的捆绑包,并且只有一个使用“react”,那么所有捆绑包都需要您从供应商块加载该响应依赖项(也称为 count=1)。相反,我建议您在此处附加您的谓词以显示:

minChunks: (m, count) => /node_modules/.test(m.context) && count >= 10

在会话术语中,您可以考虑这个告诉 webpack:

好,webpack!每当你遇到一个在 10 个或更多包 [chunks] 中引用的模块(其解析路径包括 node_modules)时,将其提取到一个名为 vendor 的单独包中。

这样,当且仅当x数量的惰性捆绑包需要这个 dep 时,它才会被移动到供应商块。现在,如果您达到 10 个需要此 dep 的惰性捆绑包,它们就会浮出水面。async: true如果您希望它不作为sync依赖项浮动,而是作为共享异步包浮动,您也可以在插件选项中添加标志。

强制 webpack 运行时到单独的包中

    new webpack.optimize.CommonsChunkPlugin({
        name: 'runtime',
        minChunks: Infinity
    })

这是一个专门用于长期缓存的功能,因此当您延迟加载捆绑包时,可能会更改的清单不会导致哈希更改。这可以保持不变。

于 2018-01-04T18:43:23.623 回答