如何单独捆绑供应商脚本并根据需要使用 Webpack?
Posted
技术标签:
【中文标题】如何单独捆绑供应商脚本并根据需要使用 Webpack?【英文标题】:How to bundle vendor scripts separately and require them as needed with Webpack? 【发布时间】:2015-07-31 12:00:27 【问题描述】:我正在尝试做一些我认为应该可行的事情,但我真的无法仅从 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 配置文件的示例是什么?我在我的配置文件上尝试了几种加载器、外部和插件的组合,但我并没有真正了解它们在做什么以及我应该使用哪些。谢谢!
【问题讨论】:
您的解决方案是什么?你有没有设法找到一个体面的方法。如果有请发帖!谢谢 【参考方案1】:在我的 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 搜索更改
【讨论】:
我不知道当我发布我的问题时这是否已经可行,但这确实是我想要的。有了这个解决方案,我不再需要指定我的供应商条目块。非常感谢!isExternal
in minChunks
让我很开心。这怎么没有记录?有缺点吗?
谢谢,但是对于 windows 路径将 userRequest.indexOf('/node_modules/') 更改为 userRequest.indexOf('node_modules')
这在使用加载器时不起作用,因为加载器的路径也会在module.userRequest
(加载器可能在node_modules
)。我的isExternal()
代码:return typeof module.userRequest === 'string' && !!module.userRequest.split('!').pop().match(/(node_modules|bower_components|libraries)/);
您能否谈谈 WebPack 4 的处理方式,或者说您不会这样做并在答案中提供链接?【参考方案2】:
我不确定我是否完全理解你的问题,但由于我最近遇到了类似的问题,我会尽力帮助你。
供应商捆绑包。
您应该为此使用CommonsChunkPlugin。在配置中指定块的名称(例如vendor
)和将生成的文件名(vendor.js
)。
new webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js", Infinity),
现在很重要的部分,您现在必须指定 vendor
library 的含义,然后在入口部分执行此操作。与新声明的块的名称相同的条目列表中的一个条目(即在这种情况下为“供应商”)。该条目的值应该是您要移动到vendor
捆绑包的所有模块的列表。
在您的情况下,它应该类似于:
entry:
app: 'entry.js',
vendor: ['jquery', 'jquery.plugin1']
JQuery 作为全局
遇到了同样的问题,用ProvidePlugin 解决了。在这里,您不是在定义全局对象,而是对模块的一种快捷方式。即你可以这样配置它:
new webpack.ProvidePlugin(
$: "jquery"
)
现在您可以在代码中的任何位置使用$
- webpack 会自动将其转换为
require('jquery')
我希望它有所帮助。你也可以看看我的webpack配置文件here
我喜欢 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'
]
,
如果某些库在入口点之间重叠(并且对于大多数库),那么您可以使用相同的插件将它们提取到公共文件中,但配置不同。请参阅this 示例。
【讨论】:
非常感谢您的回复。这是迄今为止我看到的最好的方法,但不幸的是它仍然不能解决我的问题......我测试了你的示例,vendor.js 文件仍将包含来自“jquery”和“jquery.plugin1”的所有代码,即使我的任何模块都不需要它们。这意味着最终它们将始终加载到浏览器中。如果我有很多 jquery 插件,即使只使用了一半,这也会导致文件非常大。是否只有在需要时才在供应商捆绑包中包含“jquery.plugin1”? 谢谢,所以我也学到了一些东西 :) 我已经通过创建多个供应商块更新了我的答案。也许现在它会更适合你。 这个解决方案的问题是它假设我知道每个页面的依赖关系是什么。但我无法预测……如果页面中使用的模块之一需要 jQuery,则它应该只包含在供应商捆绑包中。通过在配置文件中指定它始终位于供应商捆绑包中,即使页面中使用的任何模块都不需要,对吧?基本上,我无法预测供应商捆绑包的内容,否则我的工作会很糟糕,因为我没有只有 2 页我有数百页......你明白问题了吗?有任何想法吗? :) 我明白你在说什么,但我不认为这是一个问题。如果您在页面中使用新库,则只需将其添加到该页面的供应商库列表中。它只是几个字符。无论如何,在您的解决方案中,您必须通过指定加载程序来完成。如果您不知道哪些页面将使用您新创建的模块 - 然后让 CommonChuncks 插件自动从您的模块中提取公共库。 如何为供应商文件单独设置上下文?【参考方案3】:如果您有兴趣将您的脚本与供应商的脚本分开自动捆绑:
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')
]
;
您可以在official documentation 中阅读有关此功能的更多信息。
【讨论】:
请注意vendor : Object.keys(pkg.dependencies)
并不总是有效,它取决于包的构建方式。
您始终取决于您的package.json
的设置方式。这种解决方法在大多数情况下都有效,但在某些情况下您必须采取不同的路径。发布您自己的问题答案以帮助社区可能会很有趣。
我喜欢这个。这让我尿了一点。
请注意,它甚至会包含您的代码中甚至可能根本没有使用的包...由于Object.keys(pkg.dependencies)
将捆绑所有内容!!!!假设您在那里列出了一堆装载机......是的,这将包括在内!所以要小心......仔细区分什么是devDependency和什么是依赖
@RafaelMilewski 为什么你会在dependencies
有装载机?【参考方案4】:
我也不确定我是否完全理解你的情况,但这里是 config sn-p 为你的每个包创建单独的供应商块:
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 明确要求时才应出现在供应商捆绑包中。我不应该在配置文件中指定...这有意义吗?有什么想法吗?
@Anakin 问题是您为什么要将 200 个供应商工具捆绑到一个单独的文件中。我只会将常用工具捆绑到一个单独的文件中,并将其余的与项目捆绑包一起保存。
@Anakin 我想我正在处理同样的问题,如果我错了,请纠正我? ***.com/questions/35944067/…以上是关于如何单独捆绑供应商脚本并根据需要使用 Webpack?的主要内容,如果未能解决你的问题,请参考以下文章
如何在供应商捆绑包上使用 babel 的 `useBuiltIns: 'usage'` 选项?