1.CommonJS
node.js 即采用的commonJS的规范。
在node.js中,除了函数作用域外还有一个模块作用域,模块设计模式也因此体现,node就是基于模块编程,各个模块互不影响,耦合性很低,因此我们通常用一个模块封装一个功能。
在node 中,模块又分为两类,核心模块和文件模块。用户自定义模块的就是文件模块,下面我们来看看node里模块的定义与导入
1.1CommonJS里模块的定义
在文件模块中,一个文件就是一个模块,因此我们只需定义暴露给外部的接口就行。
比如下面我们声明一个sum函数,来计算两个数的和,将它暴露给外部调用,下面是实现代码:
let sum = (x,y) => {
return x + y;
};
module.exports = sum;
以上的代码都在一个模块的作用域里,module 是这个模块的顶层作用域,exports 初始化指向一个空对象,module.exports 是实际导出的接口,实际上exports指向module.exports的引用,如果exports发生改变,实际导出的还是module.exports。
这里我把sum函数当作外部接口暴露给外部调用。这里一个简单的模块就定义完成了,接下来我在另一个模块里去调用它。
1.2CommonJS里模块的导入
调用代码如下
console.log(require("test46.js")(1,2));
打印出来的结果 : 3
这里我们直接把它打印出来。以上代码等价于
let sum = require("test46.js");
console.log(sum(1,2));
我们直接调用了之前定义的模块,因此可以直接调用它暴露给我们的函数。
2.AMD异步模块加载
node作为一个服务端语言,在加载模块时,模块都是在本地磁盘,因此可以以可观的速度加载进来,因此,在node里的模块加载都是同步的,阻塞时间都不会太长。
但是如果我们要在浏览器端的js来实现模块设计模式呢,这就意味着我们需要远程从服务器加载所以定义的模块,浏览器js引擎的阻塞时间可想而知。
如果不使用模块设计模式的话,那各个js文件互相依赖,而且共享一个顶层作用域window,很容易出现变量重定义的错误。比如下面这种形式
<script src="js/test1.js" type="text/javascript" charset="utf-8"></script>
<script src="js/test2.js" type="text/javascript" charset="utf-8"></script>
<script src="js/test3.js" type="text/javascript" charset="utf-8"></script>
<script src="js/test4.js" type="text/javascript" charset="utf-8"></script>
<script src="js/test5.js" type="text/javascript" charset="utf-8"></script>
<script src="js/test6.js" type="text/javascript" charset="utf-8"></script>
各个文件互相依赖,耦合性很高。
但是如果我们要用模块设计模式的话,那么各个模块必须用异步加载,如果不用异步加载,浏览器很可能处于很长时间的阻塞状态,造成很糟糕的用户体验。因此AMD孕育而生。
下面我们看看AMD的模块定义。
2.1.1 AMD 没有依赖于其他模块的模块定义
我们的模块可以用define来定义。
define(arr[],function(){});
define 接受两个参数,第一个参数可以省略,在没有依赖于其他模块的情况下,可以将第一个参数省略,因此,我们可以这样定义。
define(() => {
let sum = (x,y) => {
return x + y;
};
return {sum:sum}
});
与commonJS 也有一些相似之处,commonJS 暴露给外部的接口为module.exports 所指向的对象,而AMD 暴露给外部的接口为return 的对象,这里我暴露了一个json对象,key 为外部调用的名称,value 为名称指向的对象。
2.1.2 AMD 依赖于其他模块的模块定义
如果我们定义的模块依赖于其他的模块,比如我们依赖于一个hello的模块。
这时,我们的模块需要这样定义
define(['hello'],() => {
let sum = (x,y) => {
return x + y;
};
return {sum:sum}
});
我们假定hello 模块和当前定义的模块在同一个目录下,因此我们直接写上模块名就行,如果没有在同一目录下需要加上路径。另外还可以通过require.config()方法来配置。
require.config(
path:{
"hello":"js/hello.js"
}
);
2.2.1 AMD 加载模块
同commonJS加载模块很相似的是,也是通过require方法来加载模块。
require(arr[],callback);
require方法接受两个参数,第一个参数为一个数组,代表需要加载的模块,第二个参数为一个回调函数。
假定我们要加载一个jquery 库,我们可以这样做:
require("jquery",$ => {
//some code here
});
说了这么多,我们要怎么来使用AMD 异步模块加载呢,我们需要先去require.js 官网下载,然后将它用script标签引入即可
http://requirejs.org/docs/download.html (download)
<script src="js/require.js" type="text/javascript" defer="defer" async="true" charset="utf-8"></script>
defer 属性规定当页面已完成加载后,才会执行脚本,async 属性则是规定该标签会异步加载。
这时我们已经异步把require.js 加载进来了,之后,我们需要规定一个主模块。程序将从这里开始执行。
这时我们只要指定一个 data-main属性即可,假定我们已经有main.js这个文件
<script src="js/require.js" type="text/javascript" data-main="js/main" charset="utf-8"></script>
直到这里,我们已经知道了如何去在前端实现模块设计模式的使用方法