非模块化文件
非模块化文件指的是并不遵循任何一种模块标准的文件。
最常见的就是在 script标签引用jQuey及其各种插件
如何使用 Webpack打包这类文件呢? 其实只要直接引人即可,如:
import './jquery.min.js';
但假如我们引人的非模块化文件是以隐式全局变量声明的方式暴露其接口的,则会发生问题,如:
// 通过在顶层作用城声明交量的方式暴露接口
var calculator = {
// ...
}
由于 Webpack在打包时会为每一个文件包装一层函数作用域来避免全局污染,上面的代码无法把calculator对象挂载全局,因此这种以隐式全局变量生命需要额外注意
AMD
AMD是英文Asynchronous Module Definition (异步模块定义)的缩写,从名字就可以看到和CommonJS和ES6 Module最大的区别在于它加模块的方式是异步的。下面就是一个例子
define('getsum' , ['calculator'], function(math) (
return function(a, b) {
console.log('sum:'+ calculator.add(a,b));
}
});
在AMD中使用define 函数来定义模块,它可以接受3个参数。
第1个参数是当前模块的id,相当于模块名;
第2个参数是当前模块的依赖,比如上面我们定义的getSum模块需要引人calculator模块作为依赖;
第3个参数用来描述模块的导出值,可以是函数或对象。如果是函数则导出的是函数的返回值;如果是对象则直接导出对象本身。
和CommonJS类似,AMD也使用require函数来加载模块,只不过采用异步的形式。
require(['getSum'], function(getSum)(
getSum(2,3);
}):
require的第1个参数指定了加载的模块,
第2个参数是当加载完成后执行的回调函数。
通过AMD这种形式定义模块的好处在于其模块加载是非阻塞性的,当执行到require 函数时并不会停下来去执行被加载的模块,而是继续执行require后面的代码这使得模块加载操作并不会阻塞浏览器。
尽管AMD的设计理念很好,但与同步加载的模块标准相比其语法要更加冗长。另外其异步加载的方式并不如同步显得清晰,并且容易造成回调地狱(callback hell)。在目前的实际应用中已经用得越来越少
UMD
严格来说,UMD并不能说是一种模块标准,不如说它是一组模块形式的集合。
UMD的全称是 Universal Module Definition,也就是通用模块标准,它的目标是一个模块能运行在各种环境下,不论是CommonJS、AMD,还是非块化的环境(当时ES6Module还未被提出)。
看下面的例子:
// calculator.js
(function(global, main){
//根据当前环境采取不同的导出方式
if(typeof define === 'function' && define.amd){
// AMD
define(...)
} else if (typeof exports === 'object'){
//ComnonJS
module.exports = ...;
}else{
//非模块化环境
global.add = ...
}
}(this, function(){
// 定义模块主体
return {...}
}));
可以看出,UMD其实就是根据当前全局对象中的值判断目前处于哪种模块环当前环境是AMD,就以AMD的形式导出。
需要注意的问题是,UMD 模块一般都最先判断AMD环境,也就是全局下是define函数,而通过AMD定义的模块是无法使用CommonJS 或ES6 Module的形式确引人的。
在 Webpack中,由于它同时支持AMD及CommonJS,也许工程中的所有块都是 CommonJS,而UMD 标准却发现当前有AMD环境,并使用了AMD方式导这会使得模块导人时出错。当需要这样做时,我们可以更改UMD模块中判断的顺使其以CommonS的形式导出即可。
加载npm模块
npm平台上已经有几十万个模块(pakage,也可称之为包),并且这个数字每天都在增加,各种主流的框架类库都可以在npm平台上找到。作为开发者,每个人也都可以自己封装模块并上传到npm,通过这种方式来与他人共享代码。
那么如何从我们的本地工程安装和加载一个外部的npm模块呢?首先我们需要初始化一个npm工程,并通过npm来获取模块。下面以lodash这个库为例:
#项目初始化
npm init -y
#安装1odash
npm install lodash
执行了上面的命令之后,npm会将lodash安装在工程的node_modules目录下,并将对该模块的依赖信息记录在 package.json中
在使用时,加载一个npm模块的方式很简单,只需要引人包的名字即可。
// index.js
import _ from 'lodaeh';
当Webpack在打包时解析到这条语句,就会自动去node _modules中寻找名为lodash的模块了,而不需要我们写出从源文件index.js到node_modules中lodaeh的路径。
现在知道,在导人一个nm模块时,只要写明它的名字就可以了。那么在实际打包的过程中具体加载的是npm模块中哪个JS文件呢?
每一个npm模块都有一个人口。当我们加载一个模块时,实际上就是加载该的人口文件。这个人口被维护在模块内部package.json文件的main字段中。
比如对于前面的lodash 模块来说,它的 package.json 内容如下:
//./node_modules/underscore/package.json
{
"name":"lodash",
...
"main":"lodash.js"
}
当加载该模块时,实际上加载的是 node_modules/lodash/lodash.js。
除了直接加载模块以外,我们也可以通过<package_name>/<path>
的形式单独加载模块内部的某个JS文件。如:
import all from 'lodash/fp/all.js';
console.log('all',all);
这样,Webpack最终只会打包node_modules/lodash/fp/all.js 这个文件,而不会打包全部的lodash库,通过这种方式可以减小打包资源的体积。