Open RainZhai opened 7 years ago
打包 CommonJS
在这个示例中,我们只有一个名为 myDependency 的模块依赖。通过下面的命令,Browserify会依次把main.js里引入的所有模块一同打包到一个名为 bundle.js 的文件里:
browserify main.js -o bundle.js
Browserify 首先会通过抽象语法树(AST)来解析代码中的每一个 require 语句,在分析完所有模块的依赖和结构之后,就会把所有的代码合并到一个文件中。然后你在HTML文件里引入一个bundle.js就够啦。
多个文件和多个依赖也只需要再稍微配置一下就能正常工作了。
之后你也可以使用工具来压缩代码。
打包 AMD
假若你使用的是AMD,你会需要一些例如RequireJS 或 Curl的AMD加载器。模块化加载工具可以在你的应用中按需加载模块代码。
模块加载不影响后续语句执行,逐步加载的的模块也不会导致页面阻塞无法响应。
不过在实际应用中,为了避免用户过多的请求对服务器造成压力。用RequireJS optimizer, r.js一类的构建工具来合并和压缩AMD的模块。
Webpack Webpack 是新推出的构建工具里最受欢迎的。它兼容CommonJS, AMD, ES6各类规范。 Webpack 提供许多例如 code splitting(代码分割) 的有用功能,它可以把你的代码分割成一个个的 chunk 然后按需加载优化性能。
举个例子,要是你的Web应用中的一些代码只在很少的情况下才会被用到,把它们全都打包到一个文件里是很低效的做法。所以我们就需要 code splitting 这样的功能来实现按需加载。而不是把那些很少人才会用到的代码一股脑儿全都下载到客户端去。
code splitting 只是 Webpack 提供的众多强大功能之一。
ES6 模块 ES6模块和CommonJS, AMD一类规范最主要的区别是,当你载入一个模块时,载入的操作实际实在编译时执行的——也就是在代码执行之前。所以去掉那些不必要的exports导出语句可以优化我们应用的性能。
有一个经常会被问到的问题:去除exports和冗余代码消除(UglifyJS一类工具执行后的效果)之间有什么区别? 让ES6模块与冗余代码消除(Dead code elimination)不同的是一种叫做tree shaking的技术。Tree shaking其实恰好是冗余代码消除的反向操作。它只加载你需要调用的代码,而不是删掉不会被执行的代码。我们还是用一个具体的例子说明吧: 假设我们有如下一个使用ES6语法,名为 utils.js 的函数:
export function each(collection, iterator) {
...
}
export function filter(collection, test) {
...
}
export function map(collection, iterator) {
...
}
export function reduce(collection, iterator, accumulator) {
...
}
现在我们也不清楚到底需要这个函数的哪些功能,所以先全部引入到 main.js 中:
//main.js
import * as Utils from './utils.js';
之后我们再调用一下 each 函数:
//main.js
import * as Utils from './utils.js';
Utils.each([1, 2, 3], function(x) { console.log(x) });
通过 "tree shaken" 之后的 main.js 看起来就像下面这样:
//treeshake.js
function each(collection, iterator) {
...
};
each([1, 2, 3], function(x) { console.log(x) });
注意到这里只导出了我们调用过的 each 方法。
构建ES6模块 因为浏览器对ES6模块的原生支持还不够完善,所以现阶段还需要我们做一些补充工作。 让ES6模块在浏览器中顺利运行的常用方法有以下几种:
1.使用语法编译器(Babel或Traceur)来把ES6语法的代码编译成ES5或者CommonJS, AMD, UMD等其他形式。然后再通过Browserify 或 Webpack 一类的构建工具来进行构建。
2.使用Rollup.js,这其实和上面差不多,只是Rollup还会捎带的利用“tree shaking”技术来优化你的代码。在构建ES6模块时Rollup优于Browserify或Webpack的也正是这一点,它打包出来的文件体积会更小。Rollup也可以把你的代码转换成包括ES6, CommonJS, AMD, UMD, IIFE在内的各种格式。其中IIFE和UMD可以直接在浏览器里运行,AMD, CommonJS, ES6等还需要你通过Browserify, Webpack, RequireJS一类的工具才能在浏览器中使用。 es6-module-loader
http://www.ruanyifeng.com/blog/2015/05/commonjs-in-browser.html 模块,又称构件,是能够单独命名并独立地完成一定功能的程序语句的集合(即程序代码和数据结构的集合体)。它具有两个基本的特征:外部特征和内部特征。外部特征是指模块跟外部环境联系的接口(即其他模块或程序调用该模块的方式,包括有输入输出参数、引用的全局变量)和模块的功能;内部特征是指模块的内部环境具有的特点(即该模块的局部数据和程序代码)。 我最喜欢的ES6 模块功能的特性是,导入是实时只读的。(CommonJS 只是相当于把导出的代码复制过来)。
什么是模块 一般来讲,模块是一个独立的JavaScript文件。模块文件可以包含一个类定义、一组相关的类、一个实用函数库的代码。 为什么要使用模块 模块化可以使你的代码低耦合,功能模块直接不相互影响。我个人认为模块化主要有以下几点好处:
1.可维护性:根据定义,每个模块都是独立的。良好设计的模块会尽量与外部的代码撇清关系,以便于独立对其进行改进和维护。
2.命名空间:在JavaScript中,最高级别的函数外定义的变量都是全局变量(这意味着所有人都可以访问到它们)。也正因如此,当一些无关的代码碰巧使用到同名变量的时候,我们就会遇到“命名空间污染”的问题。
3.可复用性
如何引入模块 模块模式 模块模式一般用来模拟类的概念(因为原生JavaScript并不支持类,虽然最新的ES6里引入了Class不过还不普及)这样我们就能把公有和私有方法还有变量存储在一个对象中。这样我们就能在公开调用API的同时,仍然在一个闭包范围内封装私有变量和方法。
实现模块模式的方法有很多种,下面的例子是通过匿名闭包函数的方法。(在JavaScript中,函数是创建作用域的唯一方式。)
例1:匿名闭包函数
通过这种构造,我们的匿名函数有了自己的作用域或“闭包”。 这允许我们从父(全局)命名空间隐藏变量。 这种方法的好处在于,你可以在函数内部使用局部变量,而不会意外覆盖同名全局变量,但仍然能够访问到全局变量。
例2:全局引入
另一种比较受欢迎的方法是一些诸如jQuery的库使用的全局引入。和我们刚才举例的匿名闭包函数很相似,只是传入全局变量的方法不同:
在这个例子中,globalVariable 是唯一的全局变量。这种方法的好处是可以预先声明好全局变量,让你的代码更加清晰可读。
例3:对象接口
像下面这样,还有一种创建模块的方法是使用独立的对象接口:
例4:揭示模块模式 Revealing module pattern 这和我们之前的实现方法非常相近,除了它会确保,在所有的变量和方法暴露之前都会保持私有.
CommonJS
CommonJS 扩展了JavaScript声明模块的API.
CommonJS模块可以很方便得将某个对象导出,让他们能够被其他模块通过 require 语句来引入。要是你写过 Node.js 应该很熟悉这些语法。
通过CommonJS,每个JS文件独立地存储它模块的内容(就像一个被括起来的闭包一样)。在这种作用域中,我们通过 module.exports 语句来导出对象为模块,再通过 require 语句来引入。
直观的例子:
通过指定导出的对象名称,CommonJS模块系统可以识别在其他文件引入这个模块时应该如何解释。
然后在某个人想要调用 myMoudle 的时候,只需要 require 一下:
这种实现比起模块模式有两点好处:
需要注意的一点是,CommonJS以服务器优先的方式来同步载入模块,假使我们引入三个模块的话,他们会一个个地被载入。
它在服务器端用起来很爽,可是在浏览器里就不会那么高效了。毕竟读取网络的文件要比本地耗费更多时间。只要它还在读取模块,浏览器载入的页面就会一直卡着不动。
AMD CommonJS已经挺不错了,但假使我们想要实现异步加载模块该怎么办?答案就是Asynchronous Module Definition(异步模块定义规范),简称AMD.
通过AMD载入模块的代码一般这么写:
这里我们使用 define 方法,第一个参数是依赖的模块,这些模块都会在后台无阻塞地加载,第二个参数则作为加载完毕的回调函数。
回调函数将会使用载入的模块作为参数。在这个例子里就是 myMoudle 和 myOtherModule.最后,这些模块本身也需要通过 define 关键词来定义。
拿 myModule 来举个例子:
重申一下,不像CommonJS,AMD是优先浏览器的一种异步载入模块的解决方案。
除了异步加载以外,AMD的另一个优点是你可以在模块里使用对象、函数、构造函数、字符串、JSON或者别的数据类型,而CommonJS只支持对象。
再补充一点,AMD不支持Node里的一些诸如 IO,文件系统等其他服务器端的功能。另外语法上写起来也比CommonJS麻烦一些。
UMD