Webpack和CommonsChunkPlugin,为每个异步块

时间:2018-01-04 17:14:15

标签: webpack commonschunkplugin

我已经成功地使用Webpack和CommonsChunkPlugin将我的代码拆分为两个 - 我的代码库捆绑,一个包含从node_modules导入的所有代码。那是相对容易的。

我在我的应用中尝试实现的下一件事是动态导入一个"子应用程序",它具有完全不同的供应商包装要求(例如React - 我的主要应用程序没有&#39 ; t使用它,但我的子应用程序确实),没有所有这些包出现在主供应商文件中。

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

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

    在尝试了很多我在各种博客文章中发现的内容后,我遇到了一种情况,我手动选择了node_modules目录,我希望将其包含在一个单独的供应商包中,但问题是它不会自动包含它们的依赖项,所以我的子应用程序包中仍然会有很多node_modules - 我没有专门导入它们。

    如果我能让这个工作正常,我就可以将其复制到主应用程序的更多子应用程序中。

    更新1

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

    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
    }),
    

1 个答案:

答案 0 :(得分:0)

让我们分解一下CommonsChunkPlugin"指令"你提供:

动态供应商块创建

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

上面你告诉webpack它需要包括" x"将模块转换为特定的 new 捆绑包,名为" vendor"仅基于一个条件。而且这个条件是这个模块完全解析的路径在node_modules之内。但是,如果您查看our documentation,则会有第二个参数:count被调用,您可以在应用中使用。这很重要,因为现在,如果您有20个延迟加载的包,并且只有一个使用"反应",那么所有包都需要您从供应商块加载响应依赖项(aka count = 1)。相反,我建议您将谓词附加到此处以显示:

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

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

  

Sup,webpack!每当遇到一个模块(解析路径包含node_modules),在10个或更多个bundle [chunks]中引用时,将其解压缩到一个名为vendor的独立包中。

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

强制webpack运行时进入单独的捆绑包

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

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