如何单独捆绑供应商脚本并根据需要使用 Webpack 需要它们?

IT技术 javascript webpack external-dependencies
2021-02-24 22:13:24

我正在尝试做一些我认为应该可行的事情,但我真的无法仅从 webpack 文档中了解如何做到这一点。

我正在编写一个 JavaScript 库,其中包含多个可能相互依赖或不相互依赖的module。最重要的是,所有module都使用 jQuery,其中一些可能需要 jQuery 插件。该库随后将用于可能需要部分或全部module的多个不同网站。

定义我的module之间的依赖关系非常容易,但定义它们的第三方依赖关系似乎比我预期的要困难。

我想要实现的目标:对于每个应用程序,我希望有两个包文件,一个包含必要的第三方依赖项,另一个包含我的库中的必要module。

示例:假设我的库具有以下module:

  • a(需要:jquery、jquery.plugin1)
  • b (需要: jquery, a)
  • c (需要: jquery, jquery.ui, a, b)
  • d(需要:jquery、jquery.plugin2、a)

我有一个需要module a、b 和 c 的应用程序(将其视为唯一的入口文件)。这种情况下的 Webpack 应该生成以下文件:

  • 供应商包:使用 jquery、jquery.plugin1 和 jquery.ui;
  • 网站包:包含module a、b 和 c;

最后,我更愿意将 jQuery 作为全局变量,因此我不需要在每个文件上都需要它(例如,我可以只在主文件上需要它)。并且 jQuery 插件只会扩展 $ global 以防万一(如果它们可用于不需要它们的其他module,这不是问题)。

假设这是可能的,对于这种情况,webpack 配置文件的示例是什么?我在我的配置文件中尝试了几种加载器、外部和插件的组合,但我真的不明白它们在做什么以及我应该使用哪些。谢谢!

4个回答

在我的 webpack.config.js(版本 1,2,3)文件中,我有

function isExternal(module) {
  var context = module.context;

  if (typeof context !== 'string') {
    return false;
  }

  return context.indexOf('node_modules') !== -1;
}

在我的插件数组中

plugins: [
  new CommonsChunkPlugin({
    name: 'vendors',
    minChunks: function(module) {
      return isExternal(module);
    }
  }),
  // Other plugins
]

现在我有一个文件,它只根据需要将 3rd 方库添加到一个文件中。

如果您想更详细地分离供应商和入口点文件:

plugins: [
  new CommonsChunkPlugin({
    name: 'common',
    minChunks: function(module, count) {
      return !isExternal(module) && count >= 2; // adjustable
    }
  }),
  new CommonsChunkPlugin({
    name: 'vendors',
    chunks: ['common'],
    // or if you have an key value object for your entries
    // chunks: Object.keys(entry).concat('common')
    minChunks: function(module) {
      return isExternal(module);
    }
  })
]

请注意,插件的顺序很重要。

此外,这将在第 4 版中更改。当正式发布时,我会更新此答案。

更新: windows 用户的 indexOf 搜索更改

Thx,但将 userRequest.indexOf('/node_modules/') 更改为 userRequest.indexOf('node_modules') 以获取 Windows 路径
2021-04-24 22:13:24
我不知道当我发布我的问题时这是否已经可能,但这确实是我正在寻找的。有了这个解决方案,我不再需要指定我的供应商条目块。非常感谢!
2021-05-06 22:13:24
这在使用加载器时不起作用,因为加载器的路径也将在module.userRequest(并且加载器可能在 中node_modules)。我的代码isExternal()return typeof module.userRequest === 'string' && !!module.userRequest.split('!').pop().match(/(node_modules|bower_components|libraries)/);
2021-05-10 22:13:24
您能否谈谈 WebPack 4 的处理,或者提到您不会这样做并在答案中提供链接?
2021-05-12 22:13:24
isExternalminChunks让我的一天。这如何没有记录?有坏处吗?
2021-05-13 22:13:24

我不确定我是否完全理解您的问题,但由于我最近遇到了类似的问题,我会尽力帮助您。

供应商捆绑。

您应该为此使用CommonsChunkPlugin在配置中,您指定块的名称(例如vendor),以及将生成的文件名(vendor.js)。

new webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js", Infinity),

现在是重要的部分,您现在必须指定vendor库的含义,并在条目部分执行此操作。与新声明的块的名称(即在这种情况下为“供应商”)的名称相同的条目列表中的一项。该条目的值应该是您要移动到vendor捆绑包的所有module的列表在您的情况下,它应该类似于:

entry: {
    app: 'entry.js',
    vendor: ['jquery', 'jquery.plugin1']
}

JQuery 作为全局

遇到了同样的问题并使用ProvidePlugin解决了它在这里,您不是在定义全局对象,而是在定义module的一种快捷方式。即你可以这样配置它:

new webpack.ProvidePlugin({
    $: "jquery"
})

现在你可以$在代码中的任何地方使用——webpack 会自动将其转换为

require('jquery')

我希望它有所帮助。您还可以看看我的WebPack配置文件,它是在这里

我喜欢 webpack,但我同意该文档不是世界上最好的文档......但是嘿......人们一开始对 Angular 文档也说同样的话:)


编辑:

要拥有特定于入口点的供应商块,只需多次使用 CommonsChunkPlugins :

new webpack.optimize.CommonsChunkPlugin("vendor-page1", "vendor-page1.js", Infinity),
new webpack.optimize.CommonsChunkPlugin("vendor-page2", "vendor-page2.js", Infinity),

然后为不同的文件声明不同的外部库:

entry: {
    page1: ['entry.js'],
    page2: ['entry2.js'],
    "vendor-page1": [
        'lodash'
    ],
    "vendor-page2": [
        'jquery'
    ]
},

如果某些库在入口点之间重叠(对于大多数库),那么您可以使用相同的插件将它们提取到公共文件中,只是配置不同。请参阅示例。

谢谢,所以我也学到了一些东西:) 我已经通过创建多个供应商块更新了我的答案。也许现在它会更适合你。
2021-04-24 22:13:24
此解决方案的问题在于它假设我知道每个页面的依赖项是什么。但我无法预测......如果页面中使用的module之一需要 jQuery,它应该只包含在供应商包中。通过在配置文件中指定它,即使页面中使用的任何module不需要它,它也将始终在供应商包中,对吗?基本上,我无法预测供应商捆绑包的内容,否则我将有很多工作,因为我没有只有 2 页,我有数百页......你明白了吗?有任何想法吗?:)
2021-04-26 22:13:24
非常感谢您的回复。这是迄今为止我看到的最好的方法,但不幸的是它仍然不能解决我的问题......我测试了你的例子,vendor.js 文件仍然包含来自“jquery”和“jquery.plugin1”的所有代码,即使我的任何module都不需要它们。这意味着最终它们将始终加载到浏览器。如果我有很多 jquery 插件,即使只使用了其中的一半,这也会导致一个非常大的文件。只有在需要时才能在供应商包中包含“jquery.plugin1”吗?
2021-05-01 22:13:24
如何为供应商文件单独设置上下文?
2021-05-08 22:13:24
我明白你在说什么,但我不认为这是一个问题。如果您在页面中使用新库,则只需将其添加到该页面的供应商库列表中。它只是几个字符。无论如何,在您的解决方案中,您必须通过指定加载程序来完成。如果您不知道哪些页面将使用您新创建的module - 然后让 CommonChuncks 插件自动从您的module中提取公共库。
2021-05-09 22:13:24

如果您有兴趣将您的脚本与供应商的脚本分开自动捆绑:

var webpack = require('webpack'),
    pkg     = require('./package.json'),  //loads npm config file
    html    = require('html-webpack-plugin');

module.exports = {
  context : __dirname + '/app',
  entry   : {
    app     : __dirname + '/app/index.js',
    vendor  : Object.keys(pkg.dependencies) //get npm vendors deps from config
  },
  output  : {
    path      : __dirname + '/dist',
    filename  : 'app.min-[hash:6].js'
  },
  plugins: [
    //Finally add this line to bundle the vendor code separately
    new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.min-[hash:6].js'),
    new html({template : __dirname + '/app/index.html'})
  ]
};

您可以在官方文档中阅读有关此功能的更多信息

我喜欢这个。它让我尿了一点。
2021-04-20 22:13:24
请注意,vendor : Object.keys(pkg.dependencies) 这并不总是有效,并且取决于包的构建方式。
2021-05-01 22:13:24
请注意,它甚至会包含您甚至可能根本不会在代码中使用的Object.keys(pkg.dependencies)……因为将捆绑所有内容!!!!假设您在那里列出了一堆装载机……是的,将包括在内!!!所以要小心......仔细分开什么是devDependency和什么是dependency
2021-05-01 22:13:24
你总是取决于你package.json的设置。此解决方法在大多数情况下都有效,但也有例外,您必须采用不同的路径。发布您自己的问题答案以帮助社区可能会很有趣。
2021-05-11 22:13:24
@RafaelMilewski 为什么要装装载机dependencies
2021-05-14 22:13:24

也不确定我是否完全理解你的情况,但这里是配置片段,为你的每个包创建单独的供应商块:

entry: {
  bundle1: './build/bundles/bundle1.js',
  bundle2: './build/bundles/bundle2.js',
  'vendor-bundle1': [
    'react',
    'react-router'
  ],
  'vendor-bundle2': [
    'react',
    'react-router',
    'flummox',
    'immutable'
  ]
},

plugins: [
  new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor-bundle1',
    chunks: ['bundle1'],
    filename: 'vendor-bundle1.js',
    minChunks: Infinity
  }),
  new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor-bundle2',
    chunks: ['bundle2'],
    filename: 'vendor-bundle2-whatever.js',
    minChunks: Infinity
  }),
]

并链接到CommonsChunkPlugin文档:http : //webpack.github.io/docs/list-of-plugins.html#commonschunkplugin

@Anakin 的问题是您为什么要将 200 个供应商工具捆绑到一个单独的文件中。我只会将常用工具捆绑到一个单独的文件中,并将其余的与项目捆绑在一起。
2021-04-23 22:13:24
@Anakin 我想我正在处理同样的问题,如果我错了,请纠正我?stackoverflow.com/questions/35944067/...
2021-04-30 22:13:24
我相信此解决方案的问题与 Michal 提供的问题相同。您假设我知道 bundle1 和 bundle2 的供应商依赖关系,但我不知道...假设您有 200 个 bundle,您想在配置文件中指定所有这些吗?使用您的示例,react如果bundle1 和bundl2 明确要求则仅应出现在供应商包中。我不应该在配置文件中指定它......这有意义吗?有任何想法吗?
2021-05-11 22:13:24