libraryTarget
从上一篇Vue组件开发流程中,我们可以看到 webpack 里面的一个设置 output :1
2
3
4
5
6
7
8output: {
  path: config.build.assetsRoot,
  filename: 'vue-crop-avatar.min.js', // 输出的名字
  publicPath: '/dist/', // 输出的目录
  library: 'vue-crop-avatar', // 引入组件时的名字
  libraryTarget: 'umd',   // 输出格式
  umdNamedDefine: true    // 是否将模块名称作为 AMD 输出的命名空间
},
其中,libraryTarget 可选的值有以下几个:
varthiscommonjscommonjs2amdumd
libraryTarget 就是选择怎么暴露 library ,即打包后的代码以什么格式输出
var-暴露为一个变量
var 是 libraryTarget 的默认值,如果要用 var ,就一定要设置 library 的值,因为打包后会是这种形式:1
var MyLibrary = _entry_return_;
其中,MyLibrary 就是 library 的值,如果没有这个值,就会出错。_entry_return_ 是由 webpack 生成的,是一个函数。
使用方法:1
2// 另外的脚本里
MyLibrary.doSomething()
this-通过在对象上赋值暴露
如果要使用 this,推荐设置 library 的值,因为打包后是这种形式:1
this["MyLibrary"] = _entry_return_;
如果没有 library 那么所有 _entry_return_ 的东西都会赋值给 this
使用方法:1
2
3// 另外的脚本里
this.MyLibrary.doSomething();
MyLibrary.doSomething(); // 如果 this 是 window
模块定义系统
commonjs
libraryTarget: "commonjs" 入口起点的返回值将使用 output.library 中定义的值,分配给 exports 对象。这个名称也意味着,模块用于 CommonJS 环境,打包后的形式:1
exports["MyLibrary"] = _entry_return_;
使用方法:1
2// 另外的脚本里
require("MyLibrary").doSomething();
commonjs2
libraryTarget: "commonjs2"  入口起点的返回值将分配给 module.exports 对象。这个名称也意味着模块用于 CommonJS 环境,打包后的形式:1
module.exports = _entry_return_;
使用方法:1
2// 另外的脚本里
require("MyLibrary").doSomething();
commonjs 和 commonjs2
两者在 webpack 里面没有区别,用还是那么用,从定义上讲,Commonjs 规范仅仅定义了 exports ,module.exports 是 node 对 Commonjs 规范的具体实现 。
amd
libraryTarget: "amd"  将你的 library 暴露为 AMD 模块。
生成的 output 将会使用 “MyLibrary” 作为模块名定义,即:1
2
3define("MyLibrary", [], function() {
  return _entry_return_; // 此模块返回值,是入口 chunk 返回的值
});
使用方法:
可以在 script 标签中引入脚本,也需要 requirejs1
2
3require(['MyLibrary'], function(MyLibrary) {
  // 使用 library 做一些事……
});
amd 和 commonjs
AMD 和 CommonJS 都是模块规范,但是这两种是竞争关系。CommonJS 规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。AMD规范则是非同步加载模块,允许指定回调函数。由于 Node 主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以在服务端 CommonJS 规范比较适用。但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用 AMD 规范。
umd
libraryTarget: "umd"  将你的 library 暴露为所有的模块定义下都可运行的方式。它将在 CommonJS, AMD 环境下运行,或将模块导出到 global 下的变量。打包后的形式:1
2
3
4
5
6
7
8
9
10
11
12(function webpackUniversalModuleDefinition(root, factory) {
  if(typeof exports === 'object' && typeof module === 'object')
    module.exports = factory();
  else if(typeof define === 'function' && define.amd)
    define([], factory);
  else if(typeof exports === 'object')
    exports["MyLibrary"] = factory();
  else
    root["MyLibrary"] = factory();
})(typeof self !== 'undefined' ? self : this, function() {
  return _entry_return_; // 此模块返回值,是入口 chunk 返回的值
});
这里的 root ,就是 library 的值,不设置的话,也会像不设置 this 一样,暴露到全局。
既然 CommonJs 和 AMD 风格一样流行,似乎缺少一个统一的规范。所以人们产生了这样的需求,希望有支持两种风格的“通用”模式,于是通用模块规范(UMD)诞生了。
不得不承认,这个模式略难看,但是它兼容了 AMD 和 CommonJS ,同时还支持老式的“全局”变量规范。
其他规范
ES6 Module 和 CommonJS
ES6 Module 与 CommonJS 模块完全不同。它们有两个重大差异。
- CommonJS 模块输出的是一个值的拷贝,ES6 Module 输出的是值的引用。
 - CommonJS 模块是运行时加载,ES6 Module 是编译时输出接口。
 
CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。1
2
3
4
5
6
7
8
9// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};
上面代码输出内部变量 counter 和改写这个变量的内部方法 incCounter。然后,在 main.js 里面加载这个模块。1
2
3
4
5var mod = require('./lib');
console.log(mod.counter);  // 3
mod.incCounter();
console.log(mod.counter); // 3
上面代码说明,lib.js 模块加载以后,它的内部变化就影响不到输出的 mod.counter 了。这是因为mod.counter 是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动后的值。1
2
3
4
5
6
7
8
9
10
11// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  get counter() {
    return counter
  },
  incCounter: incCounter,
};
上面代码中,输出的 counter 属性实际上是一个取值器函数。现在再执行 main.js ,就可以正确读取内部变量 counter 的变动了。1
2
3$ node main.js
3
4
ES6 Module 的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令 import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的 import 有点像 Unix 系统的“符号连接”,原始值变了, import 加载的值也会跟着变。因此,ES6 Module 是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。1
2
3
4
5
6
7
8
9
10
11// lib.js
export let counter = 3;
export function incCounter() {
  counter++;
}
// main.js
import { counter, incCounter } from './lib';
console.log(counter); // 3
incCounter();
console.log(counter); // 4
上面代码说明,ES6 Module 输入的变量 counter 是活的,完全反应其所在模块 lib.js 内部的变化。
ES6 Module 不会缓存运行结果,而是动态地去被加载的模块取值,并且变量总是绑定其所在的模块。
由于 ES6 输入的模块变量,只是一个“符号连接”,所以这个变量是只读的,对它进行重新赋值会报错。
但是,commonjs 输出的是一个值的拷贝,仅仅在这个值是原始类型的值,即 Boolean,Number,String,Undefined,Null,Symbol 这 6 中原始类型
值为 Boolean:
1  | // lib.js  | 
1  | //main.js  | 
值为 String:
1  | // lib.js  | 
1  | var mod = require('./lib');  | 
值为 undefined:
1  | // lib.js  | 
1  | // main.js  | 
值为 Symbol :
1  | // lib.js  | 
1  | // main.js  | 
其他的就不做演示了,如果这个值是 Object 类型,那结果就会变:
1  | // lib.js  | 
1  | // main.js  |