likes
comments
collection
share

webpack中external的使用

作者站长头像
站长
· 阅读数 9

webpack中external的使用

前段时间发现公司有个项目,打包出来的体积比较大,所以对该项目进行了优化。优化过程中发现一些包由于被重复的在不同的组件内被引用,所以导致其被多次打包到了输出的bundle文件中。然后调研发现了webpack中又个属性 - Externals

外部扩展(Externals)

防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。

这是webpack官网上给出的解释,在这里我们来具体看一下现象:

文件 test.js

import $ from 'jquery'
 
$('.main').animate(/* ... */);

使用Webpack打包发布这个库:

// 入口文件
entry: {
  test: './test.js',
}
// 输出文件
output: {
  path: './dist',
  filename: 'bundle.js'
}

这样打包出来的bundle.js文件会把jquery的代码完整地注入进去,因为你的test中使用到了它。

但是这往往并不符合我们的预期,因为jquery是很通用的模块,在一个项目中,很可能其它的文件也会用到它,如果每一个文件模块的发布版本都将jquery原封不动地打包进了自己的bundle,最后拼到一起,在最终的发布代码里就会有很多份jquery的复制,当然这可能并不会影响它的正常功能,但是会占据很大的代码体积,显然不符合我们的预期。

所以通常情况下当你的库需要依赖到例如jquery,bootstrap这样的通用JS模块时,我们可以不将它打包进bundle,而是在Webpack的配置中声明external:

externals: {
  jquery: {
    root: 'jquery',
    commonjs: 'jquery',
    commonjs2: 'jquery',
    amd: 'jquery',
  },
},
  • root:可以通过一个全局变量访问 library(例如,通过 script 标签)。

  • commonjs:可以将 library 作为一个 CommonJS 模块访问。

  • commonjs2:和上面的类似,但导出的是 module.exports.default.

  • amd:类似于 commonjs,但使用 AMD 模块系统。

    这样的话,jquery就不会被打包进我们的文件中了

    externals这个参数的传入形式有多种,但是总结而言,实际上就是array, object,reg 1、数组

     数组内的每一个元素又可以是多种形式,包括object, reg, function, string等

externals: [
  { // ① object形式
        a: false, // a is not external
        b: true, // b is external `module.exports = b`
        "./c": "c", // "./c" is external `module.exports = c`
        "./d": "var d", // "./d" is external `module.exports = ./d` 
        "./f": "commonjs2 ./a/b", // "./f" is external `module.exports = require("./a/b")`
        "./f": "commonjs ./a/b", // 同上
    },
 
   
    /^[a-z\-0-9]+$/, // ② reg形式
 
    function(context, request, callback) { // ③ function形式
        if(/^global-/.test(request))
        return callback(null, "var " + request.substr(7));
        callback();
    },
 
    "./e" // "./e" is external (require("./e")) // ④ string形式
]

2.对象

上面array中,有object形式,当然了也可以把它直接作为externals的值。

这种形式应该是绝大部分项目中的配置形式,也是我们最为常见的一种形式。object形式最为复杂,因为它里面一定是key: value的形式,所以像上面那种string的形式就不可能出现在object形式中,当然function和reg形式也不可能出现在object中

如下代码:

externals: {
  a: false, // a is not external
  b: true, // b is external `module.exports = b`
 
  jquery: 'jQuery', // 这种最常见,下面会讲具体为什么要这样用
  lodash : { // 这种其实和上面这种是一个道理,只不过分的更细一些
    commonjs: "lodash",
    amd: "lodash",
    root: "_" // indicates global variable
  },
  
    "./c": "c", // "./c" is external `module.exports = c`
    "./d": "var d", // "./d" is external `module.exports = ./d` 
    "./f": "commonjs2 ./a/b", // "./f" is external `module.exports = require("./a/b")`
    "./f": "commonjs ./a/b", // ...same as commonjs2
    "./f": "this ./a/b", // "./f" is external `(function() { module.exports = this["./a/b"]; }())`
}

从上面可以看出,object形式的value部分,有boolean, string, array, object这几种形式。

后面那几个以.开头的key,是指externals还可以通过对内部模块进行排除。

也就是说,你的代码里面使用了require('./d')这样的代码,也可以不把d.js这个文件的内容打包进来,不过它们的value值其实都是的string形式。

3、reg形式

也就是正则匹配的形式,通过传入正则表达式/reg/或者new RegExp()来实现匹配。其实在上面的array形式中已经用到了,只不过如果为了实现某种匹配,可以直接将正则reg传给externals:

externals: /^[a-z\-0-9]+$/

注意:

执行webpack打包,得到的bundle结果中,每个形式对应的结果可能不同,最典型的就是如果你的bundle打算运行在node环境中,你的bundle中可能使用module.exports导出接口,而如果是在浏览器中,常常需要采用umd兼容方案,所以在你的源码中的一句 require('underscore') 在根据不同的环境进行打包之后,bundle文件里面的结果可能不同

那么具体是什么类型,这和output.libraryTarget有关系,libraryTarget这个参数是用来确定你的bundle中,模块组织是遵循的什么规范,有以下几种类型:

"var" - Export by setting a variable: var Library = xxx (default)
"this" - Export by setting a property of this: this["Library"] = xxx
"commonjs" - Export by setting a property of exports: exports["Library"] = xxx
"commonjs2" - Export by setting module.exports: module.exports = xxx
"amd" - Export to AMD (optionally named - set the name via the library option)
"umd" - Export to AMD, CommonJS2 or as property in root
'amd-require'
'assign'
'commonjs-module'
'global'
'import' - uses import() to load a native EcmaScript module (async module)
'jsonp'
'module'
'node-commonjs'
'promise' - 与 'var' 相同,但是会 await 结果(适用于 async 模块)
'self'
'system'
'script'
'umd2'
'window'

举个栗子: 假如说你的value值为'commonjs2 jquery',那么bundle结果的地方就不是直接使用module.exports = jQuery了,而应该会使用module.exports = require('jquery'),这就是使用string形式的区别。这里也提醒你,当你打包一个给node使用的包时,应该要在externals的string类型中加入commonjs前缀

小结:

以上就是关于Webpack的external选项的使用,建议可以去看下webpack打包后生成的文件,这样才能知道我们是如何进行external的,也方便我们去debugger问题

参考:webpack中文文档