webpack中的externals vs libraryTarget vs library

来源:互联网 发布:火影忍者刷碎片软件 编辑:程序博客网 时间:2024/06/05 07:29

建议在github阅读,并欢迎提出问题讨论

1.webpack中的externals vs libraryTarget vs library

我们的目的只有一个,那就是编译script标签上的模块来使用。

下面的例子有两个条件:

1.我们的模块依赖于jQuery,但是我们不希望jQuery打包到最后的文件中去

2.我们的模块要存在于全局的变量Foo上面

webpack配置如下:

module.exports = {  entry:   {    main:process.cwd()+'/example1/main.js',  },  output: {    path:process.cwd()+'/dest/example1',    filename: '[name].js',    // export itself to a global var    libraryTarget: "var",    // name of the global var: "Foo"    library: "Foo"  },    externals: {        // require("jquery") is external and available        //  on the global var jQuery        "jquery": "jQuery"    },  plugins: [   new CommonsChunkPlugin({       name:"chunk",       minChunks:2   }),    new  HtmlWebpackPlugin()  ]};

1.1 externals参数

其中external配置表示我们的模块中的require(‘jquery’)中的jquery来自于window.jQuery,也就是来自于全局对象jQuery,而不要单独打包到我们的入口文件的bundle中,在页面中我们通过script标签来引入!

 externals: {        // require("jquery") is external and available        //  on the global var jQuery        "jquery": "jQuery"    }

1.2 libraryTarget vs library

library:在output中配置,可以指定你的库的名称

libraryTarget:指定你的模块输出类型,可以是commonjs,AMD,script形式,UMD模式

例子1:

其中我们的libraryTarget设置为var,而library设置为’Foo’。也就是表示我们把入口文件打包的结果封装到遍历Foo上面(以下例子的external全部是一样的,见webpack.config.js文件)

output: {    path:process.cwd()+'/dest/example1',    filename: '[name].js',    // export itself to a global var    libraryTarget: "var",    // name of the global var: "Foo"    library: "Foo"  }

我们看看打包的结果:

var Foo =webpackJsonpFoo([0,1],[/* 0 *//***/ function(module, exports, __webpack_require__) {  var jQuery = __webpack_require__(1);  var math = __webpack_require__(2);  function Foo() {}  // ...  module.exports = Foo;/***/ },/* 1 *//***/ function(module, exports) {       module.exports = jQuery/***/ },/* 2 *//***/ function(module, exports) {       console.log('main1');/***/ }]);

从结果分析我们的目的,我们的入口文件的bunle被打包成为一个变量,变量名就是library指定的Foo。而且我们externals中指定的jQuery也被打包成为一个模块,但是这个模块是没有jQuery源码的,他的模块内容很简单,就是引用window.jQuery:

/* 1 *//***/ function(module, exports) {       module.exports = jQuery;/***/ },

例子2:

修改webpack配置为:

    libraryTarget: "commonjs",  // name of the global var: "Foo"    library: "Foo"

根据library,libraryTarget定义,我们知道打包结果如下:

exports["Foo"] =webpackJsonpFoo([0,1],[/* 0 *//***/ function(module, exports, __webpack_require__) {  var jQuery = __webpack_require__(1);  var math = __webpack_require__(2);  function Foo() {}     module.exports = Foo;/***/ },/* 1 *//***/ function(module, exports) {       module.exports = require("jQuery");/***/ },/* 2 *//***/ function(module, exports) {        console.log('main1');/***/ }]);

其中library指定的库的名称转化为exports[“Foo”],!最后,很显然指定id为0的模块,也就是入口模块,最后得到的结果是符合commonjs规范的!

例子3:

修改webpack配置为:

  // export itself to a global var    libraryTarget: "amd",  // name of the global var: "Foo"    library: "Foo"

编译结果为:

define("Foo", ["jQuery"], function(__WEBPACK_EXTERNAL_MODULE_1__) {   return webpackJsonpFoo([0,1],[/* 0 *//***/ function(module, exports, __webpack_require__) {  var jQuery = __webpack_require__(1);  // var math = require(["./main1.js",'./main2.js']);  var math = __webpack_require__(2);  function Foo() {}  // ...  module.exports = Foo;/***/ },/* 1 *//***/ function(module, exports) {       //jQuery加载由amd完成,其返回的是require('jQuery')后得到的结果        module.exports = __WEBPACK_EXTERNAL_MODULE_1__;/***/ },/* 2 *//***/ function(module, exports) {  console.log('main1');/***/ }])});;

很显然是符合AMD规范的,第二个参数表示依赖的模块。jQuery被打包成为:

/***/ function(module, exports) {       //jQuery加载由amd完成        module.exports = __WEBPACK_EXTERNAL_MODULE_1__;/***/ }

WEBPACK_EXTERNAL_MODULE_1指的就是jQuery,其加载有amd完成,直接require!

例子4:

webpack配置为:

  libraryTarget: "this",  // name of the global var: "Foo"    library: "Foo"

打包结果为:

this["Foo"] =webpackJsonpFoo([0,1],[/* 0 *//***/ function(module, exports, __webpack_require__) {  var jQuery = __webpack_require__(1);  // var math = require(["./main1.js",'./main2.js']);  var math = __webpack_require__(2);  function Foo() {}  // ...  module.exports = Foo;/***/ },/* 1 *//***/ function(module, exports) {  (function() { module.exports = this["jQuery"]; }());/***/ },/* 2 *//***/ function(module, exports) {  console.log('main1');/***/ }]);

此时我们的library指定为this,我们的external指定的模块编译如下:

/* 1 *//***/ function(module, exports) {     //this.jQuery和this.Foo    (function() { module.exports = this["jQuery"]; }());/***/ },

例子5:

webpack配置为:

 libraryTarget: "umd",  // name of the global var: "Foo"    library: "Foo"

打包结果为:

//接受到第一个参数为this(编译到this上),第二个参数就是我们的一个函数,函数参数为__WEBPACK_EXTERNAL_MODULE_1__表示我们的jQuery模块(function webpackUniversalModuleDefinition(root, factory) {  //commonjs直接require  if(typeof exports === 'object' && typeof module === 'object')    module.exports = factory(require("jQuery"));    //amd让define函数加载  else if(typeof define === 'function' && define.amd)    define(["jQuery"], factory);    //cmd(require,jQuery的处理由cmd处理),直接require  else if(typeof exports === 'object')    exports["Foo"] = factory(require("jQuery"));  else  //this    root["Foo"] = factory(root["jQuery"]);})(this, function(__WEBPACK_EXTERNAL_MODULE_1__) {return webpackJsonpFoo([0,1],[/* 0 *//***/ function(module, exports, __webpack_require__) {  var jQuery = __webpack_require__(1);  // var math = require(["./main1.js",'./main2.js']);  var math = __webpack_require__(2);  function Foo() {}  // ...  module.exports = Foo;/***/ },/* 1 *//***/ function(module, exports) {        module.exports = __WEBPACK_EXTERNAL_MODULE_1__;/***/ },/* 2 *//***/ function(module, exports) {  console.log('main1');/***/ }])});;

注意,从上面的分析可以知道,我们除了将入口文件打包成为特定的模块以外,其external模块(require(‘jQuery’))也要进行处理的,external的key是require时候模块名称,value是我们在页面中通过script引入的文件名!

官方实例如下:

{    output: { libraryTarget: "commonjs" },    externals: [        {            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`  <-- would be syntax error                        "./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"]; }())`        },        // Every non-relative module is external        // abc -> require("abc")        /^[a-z\-0-9]+$/,        function(context, request, callback) {            // Every module prefixed with "global-" becomes external            // "global-abc" -> abc            if(/^global-/.test(request))                return callback(null, "var " + request.substr(7));            callback();        },        "./e" // "./e" is external (require("./e"))    ]}

1.3 require数组情况

上图中我们还有很多种情况没有考虑到,如’abc.def’,[‘abc’,’def’],我们按照上面的方式继续分析。

例子1:

其中我们的libraryTarget设置为var,而library设置为’Foo’。也就是表示我们把入口文件打包的结果封装到遍历Foo上面(以下例子的external全部是一样的,见webpack.config.js文件)

output: {    path:process.cwd()+'/dest/example1',    filename: '[name].js',    // export itself to a global var    libraryTarget: "var",    // name of the global var: "Foo"    library: "Foo"  }

其中main.js修改为如下:

var jQuery = require("jquery");var math = require(["./main1.js",'./main2.js']);// var math = require("./main1.js");function Foo() {}// ...module.exports = Foo;

我们看看打包的结果:

var Foo =webpackJsonpFoo([0,2],[/* 0 *//***/ function(module, exports, __webpack_require__) {  var jQuery = __webpack_require__(1);  var math = __webpack_require__.e/* require */(1, function() {[__webpack_require__(2),__webpack_require__(3)];});  // var math = require("./main1.js");  function Foo() {}  // ...  module.exports = Foo;/***/ },/* 1 *//***/ function(module, exports) {  module.exports = jQuery;/***/ }]);

大部分都是和上面一样的,只是这里多了一个webpack_require.e方法(见下面分析),其作用就是保证模块的加载顺序!因为这里的jQuery是external表示不要单独打包进来,而是直接使用script引入,所以这里的提前加载的id为1,其就是我们的jQuery!

//提前加载external指定的jQuery,因为他是通过script直接引入,在其他模块中requirevar math = __webpack_require__.e/* require */(1, function() {[__webpack_require__(2),__webpack_require__(3)];});

1.4 当require的是external中指定的数组或者’abc.def格式’

官网说明:

如果external指定为数组 [“abc”, “def”],那么我们会打包成如下的格式:

module.exports = require("abc").def;

也就相当于对于abc这个external文件,我们是通过script引入的,但是我们只会引入这个模块的def部分!

实际上:

main.js如下:

var main1=require('abc');var main1=require('def');function Foo() {}// ...module.exports = Foo;

我们的webpack配置为如下:

  externals:["abc", "def"],

我们require(‘abc’)得到的结果是:

exports["Foo"] =webpackJsonpFoo([0,1],[/* 0 *//***/ function(module, exports, __webpack_require__) {  var main1=__webpack_require__(1);  var main1=__webpack_require__(2);  function Foo() {}  // ...  module.exports = Foo;/***/ },/* 1 *//***/ function(module, exports) {  module.exports = require("abc");/***/ },/* 2 *//***/ function(module, exports) {  module.exports = require("def");/***/ }]);

此处结果暂时存疑,以后弄懂了会及时更新

2. webpack_require.e方法分析

/******/  __webpack_require__.e = function requireEnsure(chunkId, callback) {/******/    // "0" is the signal for "already loaded"            //0表示已经加载了这个模块,直接回调加载另外一个模块            //这样可以保证我们的第一个参数的chunk是提前加载好的!/******/    if(installedChunks[chunkId] === 0)/******/      return callback.call(null, __webpack_require__);/******/    // an array means "currently loading".            //数组表示模块在加载中,这时候只是添加回调而已,所以回调可以是一个数组,但是            //模块直接加载一次/******/    if(installedChunks[chunkId] !== undefined) {/******/      installedChunks[chunkId].push(callback);/******/    } else {/******/      // start chunk loading             //开始加载模块/******/      installedChunks[chunkId] = [callback];/******/      var head = document.getElementsByTagName('head')[0];/******/      var script = document.createElement('script');/******/      script.type = 'text/javascript';/******/      script.charset = 'utf-8';/******/      script.async = true;              //__webpack_require__.p = "";/******/      script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"main"}[chunkId]||chunkId) + ".js";              //直接通过chunId加载模块,文件名chunkId + "." + ({"0":"main"}[chunkId]||chunkId) + ".js"/******/      head.appendChild(script);/******/    }/******/  };

参考资料:

webpack官网

webpack中library和libraryTarget与externals的使用

0 0