Webpack ProvidePlugin vs externals?

88
我正在探索使用 WebpackBackbone.js 的想法。
我已经按照快速入门指南进行了操作,对 Webpack 的工作原理有了一个大致的了解,但是我不清楚如何加载依赖库,比如 jquery / backbone / underscore。
它们应该通过 <script> 外部加载,还是像 RequireJS 的 shim 一样由 Webpack 处理?
根据 webpack doc: shimming modulesProvidePluginexternals 似乎与此相关(还有 bundle! loader),但我无法确定何时使用哪个。
谢谢。
3个回答

162

这是完全可能的:您可以使用<script>标签引入库(例如从CDN使用库)或将它们包含在生成的捆绑包中。

如果您通过<script>标签加载它,您可以使用externals选项允许在您的模块中编写require(...)

从CDN中使用库的示例:

<script src="https://code.jquery.com/jquery-git2.min.js"></script>

// the artifial module "jquery" exports the global var "jQuery"
externals: { jquery: "jQuery" }

// inside any module
var $ = require("jquery");

在捆绑包中包含库的示例:

copy `jquery-git2.min.js` to your local filesystem

// make "jquery" resolve to your local copy of the library
// i. e. through the resolve.alias option
resolve: { alias: { jquery: "/path/to/jquery-git2.min.js" } }

// inside any module
var $ = require("jquery");

ProvidePlugin可以将模块映射到(自由)变量。因此,您可以定义:“每当我在模块内使用(自由)变量xyz时,您(webpack)都应该将xyz设置为require(“abc”)。”

没有ProvidePlugin的示例:

// You need to require underscore before you can use it
var _ = require("underscore");
_.size(...);

使用ProvidePlugin的示例:

plugins: [
  new webpack.ProvidePlugin({
    "_": "underscore"
  }) 
]

// If you use "_", underscore is automatically required
_.size(...)

摘要:

  • 从CDN使用库:使用<script>标签和externals选项。
  • 从文件系统中使用库:将库包含在捆绑包中。(可能需要修改resolve选项以找到库)
  • externals:将全局变量作为模块可用
  • ProvidePlugin:使模块在模块内作为自由变量可用

应该在 webpack.ProvidePlugin 前面添加 new。 http://webpack.github.io/docs/list-of-plugins.html - MK Yung
为什么不直接使用脚本加载器呢?这样会更容易,就像@dtothefp所解释的那样。 - timaschew
如果我的webpack.config文件在一个名为javascript的文件夹中,并且在其中有一个名为vendor的文件夹,里面有我的jquery文件。路径不应该是这样吗?resolve: { alias: { jquery: "vendor/jquery-1.10.2.js" } }。但是即使使用别名,对我来说仍然无法正常工作。 - me-me
3
只需将绝对路径传递给别名选项。如果传递相对路径,则在webpack 1中,它相对于require/import所在的位置。在webpack 2中,它相对于webpack.config.js文件或上下文选项。 - Tobias K.
@TobiasK。绝对路径与默认导出不兼容。在文件中,我得到了一个对象 {__esModule: true, default: MY_DEFAULT_EXPORT} 而不是 MY_DEFAULT_EXPORT - mgol

26

有一点很酷的是,如果你在使用ProvidePlugin时结合externals属性,它将允许你将jQuery传递到 webpack 模块闭包中,而无需显式地require它。这对于重构具有许多引用$的旧代码非常有用。

//webpack.config.js
module.exports = {
  entry: './index.js',
  output: { 
    filename: '[name].js' 
  },
  externals: {
    jquery: 'jQuery'
  },
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',
    })
  ]
};

现在在index.js中

console.log(typeof $ === 'function');

编译后的输出将类似于以下内容传递到webpackBootstrap闭包中:

/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

    /* WEBPACK VAR INJECTION */(function($) {
        console.log(typeof $ === 'function');

    /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1)))

/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {

    module.exports = jQuery;

/***/ }
/******/ ])
因此,您可以看到$从CDN引用全局/窗口jQuery,但被传递到闭包中。我不确定这是否是预期的功能或幸运的hack,但对于我的用例似乎效果很好。

如果你不打算require/import它,那么你就不需要这两个插件。$ 可以正常工作,因为它会无论如何到达全局作用域。ProviderPlugin需要解析 AST,所以它是一个昂贵的插件,并且会明显增加构建时间。所以基本上就是浪费。 - faceyspacey.com
@dtohefp 这个答案真是太及时了。您能否解释一下为什么 ProvidePlugin 返回一个像 myModule.default 这样的对象,除非我将模块添加到 externals 中?我从来没有想到过会有任何直接关系。 - Slbox

11

我知道这是一个旧帖子,但认为在此提到 webpack 脚本加载器可能也会有用。来自 webpack 文档:

"script:在全局上下文中执行 JavaScript 文件(就像在 script 标签中一样),不解析 require。"

http://webpack.github.io/docs/list-of-loaders.html

https://github.com/webpack/script-loader

当迁移旧版构建过程,将 JS 供应商文件和应用程序文件合并在一起时,我发现这特别有用。需要注意的是,脚本加载器似乎只能通过重载 require() 来工作,并且不能被指定在 webpack.config 文件中。尽管许多人认为重载 require 是不良实践,但它可以非常有用,将供应商脚本和应用程序脚本合并到一个捆绑包中,并同时暴露不必添加到其他 webpack 捆绑包中的 JS 全局变量。例如:

require('script!jquery-cookie/jquery.cookie');
require('script!history.js/scripts/bundled-uncompressed/html4+html5/jquery.history');
require('script!momentjs');

require('./scripts/main.js');

使用这种方法可以使 $.cookie、History 和 moment 库在打包后的代码中全局可用,同时将这些第三方库和所有通过 require 导入的文件一起打包到 main.js 脚本中。

此外,这种技术还很实用:

resolve: {
  extensions: ["", ".js"],
  modulesDirectories: ['node_modules', 'bower_components']
},
plugins: [
  new webpack.ResolverPlugin(
    new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("bower.json", ["main"])
   )
]

使用Bower的话,会检查每个require库的package.json中的main文件。在上面的例子中,History.js没有指定main文件,因此需要指定文件路径。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接