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會加載你引入的所有模塊,哪怕你并不會真的用到它 。

推薦閱讀