本篇文章帶給大家的內容是關於為什麼需要模組化? js中常用模組化方案介紹,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
為什麼需要模組化
在ES6出現之前,JS語言本身並沒有提供模組化能力,這為開發帶來了一些問題,其中最重要的兩個問題應是全局污染和依賴管理混亂。
// file a.js var name = 'aaa'; var sayName = function() { console.log(name); };
<!-- file index.html --> <script src='xxx/xxx/a.js'></script> <script> sayName(); // 'aaa' // code... var name = 'bbb'; sayName(); // 'bbb' </script>
上面的程式碼中,我們兩次呼叫a.js所提供的sayName函數輸出了不同結果,很明顯這是因為兩個檔案都對變數name進行了賦值,因此彼此之間造成了影響。當然我們可以在編寫程式碼時注意不要定義已存在的變數名,但是當一個頁面引用了10幾個幾百行的檔案時,記住所有已經定義過的變數顯然不太現實。
// file a.js var name = getName(); var sayName = function() { console.log(name) };
// file b.js var getName = function() { return 'timo'; };
<script src='xxx/xxx/b.js'></script> <script src='xxx/xxx/a.js'></script> <script> sayName(); // 'timo' </script>
<script src='xxx/xxx/a.js'></script> <script src='xxx/xxx/b.js'></script> // Uncaught ReferenceError: getName is not defined
上面的程式碼說明,多個檔案有依賴關係時,我們需要確保其引入的順序,從而保證運行某個檔案時,其依賴已經提前加載,可以想像,面對越大型的項目,我們需要處理的依賴關係也就越多,這既麻煩又容易出錯。
而為了解決這些,社群中出現了許多為JS語言提供模組化能力的規範,借助這些規範,能讓我們的開發更加方便安全。
常見模組化方案
#CommonJS是由社群提出的模組化方案中的一種,Node.js遵循了這套方案。
// file a.js var obj = { sayHi: function() { console.log('I am timo'); }; }; module.exports obj;
// file b.js var Obj = require('xxx/xxx/a.js'); Obj.sayHi(); // 'I am timo'
上面的程式碼中,檔案a.js是模組的提供方,檔案b.js是模組呼叫方。
每個檔案都是模組;
#在模組內提供module#對象,表示當前模組;
模組使用exports對外暴露自身的函數/對象/變數等;
模組內透過require()方法導入其他模組;
CommonJS的規範,簡單來說就是上面4條,可以對照基本寫法中的例子來理解一下,在實際實作中,Node.js雖然遵循CommonJS規範,但仍然對其進行了一些調整。
AMD是模組化規格中的一種,RequireJS遵循了這套規格。
// file a.js define('module', ['m', './xxx/n.js'], function() { // code... })
上面的程式碼中,檔案a.js向外導出了模組;
AMD中,暴露模組使用define函數
define(moduleName, [], callback);
如上面程式碼,define函式共有三個參數
#moduleName此參數可以省略,表示該模組的名字,一般作用不大
['name1', 'name2'],第二個參數是數組,表示目前模組依賴的其他模組,如果沒有依賴模組,可以省略
callback,第三個參數是必傳參數,是一個回呼函數,內部是目前模組的相關程式碼
#ADM的特點是依賴前置,這是ADM規範與接下來要介紹的CMD規格最大的不同,依賴前置是指:在執行目前載入模組回調前,會先將所有依賴套件載入完畢,也是define函數的第二個參數中指定的依賴包。
define(function(require, exports, module) { var a = require('./a') a.doSomething(); // code... var b = require('./b') // code... })
上面程式碼是CMD規範導出模組的基本寫法;
從寫法可以看出,CMD的寫法和AMD非常像,其主要區別是對於依賴加載時機的不同,上面已經說過,AMD是依賴前置,而CMD規範推崇就近原則,簡單說就是在模組運行前並不加載依賴,模組運作過程中,當需要某個依賴時,再去進行載入。
CommonJS、AMD、CMD並行的狀態下,就需要一個方案能夠相容於他們,這樣我們在開發時,就不需要再去考慮依賴模組所遵循的規範了,而UMD的出現就是為了解決這個問題。
(function (root, factory) { if (typeof define === 'function' && define.amd) { //AMD define(['jquery'], factory); } else if (typeof exports === 'object') { //Node, CommonJS之类的 module.exports = factory(require('jquery')); } else { //浏览器全局变量(root 即 window) root.returnExports = factory(root.jQuery); } }(this, function ($) { //方法 function myFunc(){}; //暴露公共方法 return myFunc; }));
上面的程式碼是UMD的基本寫法,從程式碼就可以看出,其能夠同時支援CommonJS規範和AMD規範。
上面分別介紹了CommonJS、AMD、CMD和UMD,他們都是社群對於JS實作模組化的貢獻,這個規範其產生的根本原因,都是JS語言自身沒有模組化能力,而目前,在JS最新的語言規範ES6中,已經為JS增加了模組化能力,而JS自身的模組化方案,完全能夠取代目前社區提出的各類規範,且能夠做到瀏覽器端和Node端通用。
ES6中的模組化能力由兩個指令構成:export和import,export指令用於規定模組的對外接口, import指令用於輸入其他模組提供的功能。
ES6中一個檔案就是一個模組,模組內部的變數/函數等外部是無法存取的,如果希望將內部的函數/變數等對外暴露,供其他模組進行使用,就需要透過export指令進行匯出
// file a.js export let a = 1; export let b = 2; export let c = 3;
// file b.js let a = 1; let b = 2; let c = 3; export {a, b, c}
// file c.js export let add = (a, b) => { return a + b; };
上面三个文件的代码,都是通过export命令导出模块内容的示例,其中a.js文件和b.js文件都是导出模块中的变量,作用完全一致但写法不同,一般我们更推荐b.js文件中的写法,原因是这种写法能够在文件最底部清楚地知道当前模块都导出了哪些变量。
模块通过export命令导出变量/函数等,是为了让其他模块能够导入去使用,在ES6中,文件导入其他模块是通过import命令进行的
// file d.js import {a, b, c} from './a.js';
上面的代码中,我们引入了a.js文件中的变量a、b、c,import在引入其他模块内的函数/变量时,必须与原模块所暴露出来的函数名/变量名一一对应。
同时,import命令引入的值是只读的,如果尝试对其进行修改,则会报错
import {a} d from './a.js'; a = 2; // Syntax Error : 'a' is read-only;
从上面import的介绍可以看到,当需要引入其他模块时,需要知道此模块暴露出的变量名/函数名才可以,这显然有些麻烦,因此ES6还提供了一个import default命令
// file a.js let add = (a, b) => { return a+b }; export default add;
// file b.js import Add from './a.js'; Add(1, 2); // 3
上面的代码中,a.js通过export default命令导出了add函数,在b.js文件中引入时,可以随意指定其名称
export default命令是默认导出的意思,既然是默认导出,显然只能有一个,因此每个模块只能执行一次export default命令,其本质是导出了一个名为default的变量或函数。
以上是為什麼需要模組化? js中常用模組化方案介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!