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


173

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

我正在编写一个JavaScript库,其中包含几个可能相互依赖的模块。最重要的是,所有模块都使用jQuery,其中一些可能需要jQuery插件。然后,该库将在可能需要某些或所有模块的几个不同的网站上使用。

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

我想实现的目标是:对于每个应用程序,我都希望有两个捆绑文件,一个具有必要的第三方依赖关系,另一个具有我库中的必要模块。

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

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

我有一个需要模块a,b和c的应用程序(将其视为唯一的条目文件)。这种情况下的Webpack应生成以下文件:

  • 供应商捆绑包:带有jquery,jquery.plugin1和jquery.ui;
  • 网站捆绑:带有模块a,b和c;

最后,我希望将jQuery作为全局变量,所以我不需要在每个文件上都需要它(例如,我可以只在主文件上需要它)。jQuery插件只会在需要时扩展$全局(如果不需要其他模块的其他模块可以使用它们,这不是问题)。

假设有可能,这种情况下的webpack配置文件示例是什么?我在配置文件上尝试了几种加载程序,外部程序和插件的组合,但是我并没有真正了解它们在做什么以及应该使用哪个。谢谢!


2
您有什么解决方案?你设法找到一个体面的方法吗?如果是这样,请发布!谢谢
GeekOnGadgets's

Answers:


140

在我的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
]

现在,我有一个仅根据需要将第3方库添加到一个文件的文件。

如果您希望更细化将供应商和入口点文件分开的位置,请执行以下操作:

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搜索更改


1
我不知道在发布问题时是否已经可以做到这一点,但这确实是我想要的。使用此解决方案,我不再需要指定供应商条目块。非常感谢!
bensampaio

1
isExternal成就minChunks了我的一天。怎么没有记录?有缺点吗?
Wesley Schleumer deGóes16年

谢谢,但是将Windows路径的userRequest.indexOf('/ node_modules /')更改为userRequest.indexOf('node_modules')
Kinjeiro

@WesleySchleumerdeGóes已记录在案,但没有示例,options.minChunks (number|Infinity|function(module, count) -> boolean):我还没有看到不利的消息。
拉斐尔·

2
使用加载程序时,这将不起作用,因为加载程序的路径也将位于其中module.userRequest(并且加载程序可能位于中node_modules)。我的代码isExternal()return typeof module.userRequest === 'string' && !!module.userRequest.split('!').pop().match(/(node_modules|bower_components|libraries)/);
cdauth

54

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

供应商捆绑包。

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

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

现在很重要的部分,您现在必须指定什么意思是vendor库,然后在输入部分进行操作。条目列表中还有一项与新声明的块的名称相同(在这种情况下,即“供应商”)。该条目的值应该是要移动到vendor捆绑的所有模块的列表。在您的情况下,它应该类似于:

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

jQuery作为全局

遇到了同样的问题,并通过ProvidePlugin解决了。在这里,您不是在定义全局对象,而是定义模块的快捷方式。即,您可以像这样配置它:

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

如果某些库在入口点之间重叠(并且对于大多数库而言是重叠的),则可以使用具有不同配置的同一插件将它们提取到公共文件中。请参阅示例。


非常感谢您的回复。到目前为止,这是我见过的最好的方法,但是不幸的是,它仍然不能解决我的问题...我测试了您的示例,即使即使'jquery'和'jquery.plugin1',vendor.js文件仍将包含所有代码我的任何模块都不需要它们。这意味着最终它们将始终被加载到浏览器中。如果我有很多jquery插件,即使只使用其中一半,也会导致文件很大。仅在需要时才无法在供应商捆绑包中包含“ jquery.plugin1”吗?
2015年

谢谢,所以我也学到了一些:)我已经通过创建多个供应商块更新了答案。也许现在它会更适合您。
米哈尔Margiel

4
该解决方案的问题在于它假设我知道每个页面的依赖关系是什么。但是我无法预测...如果页面中使用的模块之一需要jQuery,则仅应将其包括在供应商捆绑软件中。通过在配置文件上指定它将始终位于供应商捆绑包中,即使该页面中使用的任何模块都不需要它,对吗?基本上,我无法预测供应商捆绑包的内容,否则我会工作很麻烦,因为我只有2页,而我只有数百页...您明白了吗?有任何想法吗?:)
bensampaio 2015年

我理解您在说什么,但我认为这不是问题。如果在页面中使用新库,则只需将其添加到该页面的供应商库列表中。它只是几个字符。无论如何,在解决方案中,您都必须通过指定loader来完成。如果您不知道哪些页面将使用您新创建的模块,则让CommonChuncks插件自动从模块中提取通用库。
米哈尔Margiel

如何为供应商文件分别设置上下文?
harshes53 2016年

44

如果您有兴趣将脚本与供应商的脚本分别自动捆绑在一起,请执行以下操作:

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'})
  ]
};

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


4
请注意,vendor : Object.keys(pkg.dependencies) 这并不总是有效,并且取决于软件包的构建方式。
markyph '16

1
您始终取决于自己package.json的设置。此解决方法在大多数情况下都有效,但是在某些情况下,您必须采用其他方法。发布您自己的问题答案以帮助社区可能会很有趣。
Freezystem '16

16
我喜欢这个。它使我尿了一下。
cgatian

3
请注意,它甚至会包含您的代码中甚至可能根本没有使用过的Object.keys(pkg.dependencies)软件包...由于它将捆绑所有内容!假设您那里列出了一堆装载机...是的,将包括在内!!!所以要小心...小心翼翼地分开什么是devDependency和什么是依赖
Rafael

1
@RafaelMilewski为什么要装载装载机dependencies
裤子

13

也不确定我是否完全理解您的情况,但是这里是配置代码段,用于为每个捆绑包创建单独的供应商块:

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


我相信此解决方案的问题与Michal提供的解决方案相同。您假设我知道bundle1和bundle2的供应商依存关系,但我不知道...想象一下,您是否想在配置文件中指定200个捆绑包?使用您的示例,react只有在bundle1和bundl2明确要求时,才应将其出现在供应商捆绑包中。我不必在配置文件上指定该名称。这有意义吗?有任何想法吗?
2015年

@Anakin的问题是,为什么要将200个供应商工具捆绑到一个单独的文件中。我只会将常用工具捆绑到一个单独的文件中,其余的与项目捆绑在一起。
maxisam

@Anakin我认为我正在处理同一问题,如果我错了,请纠正我吗?stackoverflow.com/questions/35944067/…–
pjdicke
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.