JavaScript之无题之让人烦躁的模块化( 二 )


而后,在同一年的年底 , NodeJs出现了,Javascript不仅仅可以用于浏览器,在服务器端也开始攻城略地 。NodeJs的初衷是基于commonJs社区的模块化规范 , 但是NodeJs并没有完全遵循于社区的一些腐朽过时的约束 , 它实现了自己的想法 。
commonJs规范的写法,如果大家写过NodeJs一定都有所了解,大概是这样的:
// a.jsmodule.exports = 'zaking'// b.jsconst a = require("./a");console.log(a); // zaking看起来挺简单的 , 但是这里隐藏了一些不那么容易被理解的特性 。
在NodeJs中,一个文件就是一个模块,有自己的作用域,在一个文件里面定义的函数、对象都是私有的,对其他文件不可见 。并且,当第一次加载某个模块的时候,NodeJ会缓存该模块,待再次加载的时候 , 会直接从模块中取出module.exports属性返回 。比如:
// a.jsvar name = "zaking";exports.name = name;// b.jsvar a = require("./a.js");console.log(a.name); // zakinga.name = "xiaoba";var b = require("./a.js");console.log(b.name); // xiaoba诶?为啥你写的是“exports.”,不是module.exports?NodeJs在实现CommonJs规范的时候为了方便,给每个模块都提供了一个exports私有变量,指向module.exports 。有一点要尤其注意,exports 是模块内的私有局部变量,它只是指向了 module.exports,所以直接对 exports 赋值是无效的,这样只是让 exports 不再指向 module.exports了而已 。
我们回到上面的代码 , 按理来说,我第二次引入的b的name应该是“zaking”啊 。但是实际上,在第一次引入之后的引入,并不会再次执行模块的内容,只是返回了缓存的结果 。
另外一个核心的点是 , 我们导入的是导出值的拷贝,也就是说一旦引入之后 , 模块内部关于该值的变化并不会被影响 。
// a.jsvar name = "zaking";function changeName() {name = "xiaowangba";}exports.name = name;exports.changeName = changeName;// b.jsvar a = require("./a.js");console.log(a.name); // zakinga.changeName();console.log(a.name); // zaking嗯,一切看起来都很不错 。
三、争奇斗艳,百家争鸣在上一小节,我们简单介绍了模块化的始祖也就是CommonJs以及实现了该规范的NodeJs的一些核心内容 。但是NodeJs的实现的一个关键的点是,它在读取或者说加载模块的时候是同步的,这在服务器没什么问题 , 但是对于浏览器来说,这个问题就很严重 , 因为大量的同步模块加载意味着大量的白屏等待时间 。
基于这样的问题,从CommonJs中独立出了AMD规范 。
1、AMD规范与RequireJsAMD , 即Asynchronous Module Definition , 翻译过来就是异步模块化规范,它的主要目的就是解决CommonJs不能在浏览器中使用的问题 。但是RequireJs在实现上,希望可以通吃,也就是可以在任何宿主环境下使用 。
我们先来看个例子:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><script src="http://img.zhejianglong.com/231018/022RB2Z-0.jpg"></script><body></body><script>require(["./a"]);</script></html>然后,我们的a.js是这样的:
define(function () {function fun1() {alert("it works");}fun1();});define用来声明一个模块,require导入 。我们还可以这样:
require(["./a"], function () {alert("load finished");});导入前置依赖的模块,在第二个参数也就是回调中执行 。RequireJs会在所有的模块解析完成后执行回调函数 。就算你倒入了一个没有使用的模块,RequireJs也一样会加载:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><script src="http://img.zhejianglong.com/231018/022RB2Z-0.jpg"></script><body></body><script>require(["./a", "./b"], function (a, b) {a.fun1();});</script></html>然后分别是a.js和b.js:
// a.jsdefine(function () {function fun1() {alert("it works fun1");}return {fun1: fun1,};});// b.jsdefine(function () {function fun2() {alert("it works fun2");}return {fun2: fun2,};});结果大家可以试一试~
以上就是RequireJs的简单用法,我们据此知道了两个核心内容,RequireJs基于AMD规范 , RequireJs会加载你引入的所有模块,哪怕你并不会真的用到它 。

推荐阅读