摘要:可以看到,第三個(gè)參數(shù)是回調(diào)函數(shù),可以直接使用依賴的模塊,他們按依賴聲明順序作為參數(shù)提供給回調(diào)函數(shù)。結(jié)論規(guī)范是開發(fā)的一次重要嘗試,它以簡單而優(yōu)雅的方式統(tǒng)一了的模塊定義和加載機(jī)制,并迅速得到很多框架的認(rèn)可和采納。
1. AMD的由來
前端技術(shù)雖然在不斷發(fā)展之中,卻一直沒有質(zhì)的飛躍。除了已有的各大著名框架,比如Dojo,jQuery,ExtJs等等,很多公司也都有著自己的前端開發(fā)框架。這些框架的使用效率以及開發(fā)質(zhì)量在很大程度上都取決于開發(fā)者對其的熟悉程度,以及對JavaScript的熟悉程度,這也是為什么很多公司的技術(shù)帶頭人都喜歡開發(fā)一個(gè)自己的框架。開發(fā)一個(gè)自己會(huì)用的框架并不難,但開發(fā)一個(gè)大家都喜歡的框架卻很難。從一個(gè)框架遷移到一個(gè)新的框架,開發(fā)者很有可能還會(huì)按照原有框架的思維去思考和解決問題。這其中的一個(gè)重要原因就是JavaScript本身的靈活性:框架沒辦法絕對的約束你的行為,一件事情總可以用多種途徑去實(shí)現(xiàn),所以我們只能在方法學(xué)上去引導(dǎo)正確的實(shí)施方法。慶幸的是,在這個(gè)層面上的軟件方法學(xué)研究,一直有人在去不斷的嘗試和改進(jìn),CommonJS就是其中的一個(gè)重要組織。他們提出了許多新的JavaScript架構(gòu)方案和標(biāo)準(zhǔn),希望能為前端開發(fā)提供銀彈,提供統(tǒng)一的指引。
AMD規(guī)范就是其中比較著名一個(gè),全稱是Asynchronous Module Definition,即異步模塊加載機(jī)制。從它的規(guī)范描述頁面看,AMD很短也很簡單,但它卻完整描述了模塊的定義,依賴關(guān)系,引用關(guān)系以及加載機(jī)制。從它被requireJS,NodeJs,Dojo,JQuery使用也可以看出它具有很大的價(jià)值,沒錯(cuò),JQuery近期也采用了AMD規(guī)范。在這篇文章中,我們就將介紹AMD的性質(zhì),用法,優(yōu)勢以及應(yīng)用場景。從AMD中我們也能學(xué)習(xí)到如何在更高層面去設(shè)計(jì)自己的前端應(yīng)用。
2. AMD是什么
> 作為一個(gè)規(guī)范,只需定義其語法API,而不關(guān)心其實(shí)現(xiàn)。AMD規(guī)范簡單到只有一個(gè)API,即
define函數(shù): define([module-name?], [array-of-dependencies?], [module-factory-or-object]);
其中:
module-name: 模塊標(biāo)識(shí),可以省略。
array-of-dependencies: 所依賴的模塊,可以省略。
module-factory-or-object: 模塊的實(shí)現(xiàn),或者一個(gè)JavaScript對象。
從中可以看到,第一個(gè)參數(shù)和第二個(gè)參數(shù)都是可以省略的,第三個(gè)參數(shù)則是模塊的具體實(shí)現(xiàn)本身。后面將介紹在不同的應(yīng)用場景下,他們會(huì)使用不同的參數(shù)組合。
從這個(gè)define函數(shù)AMD中的A:Asynchronous,我們也不難想到define函數(shù)具有的另外一個(gè)性質(zhì),異步性。當(dāng)define函數(shù)執(zhí)行時(shí),它首先會(huì)異步的去調(diào)用第二個(gè)參數(shù)中列出的依賴模塊,當(dāng)所有的模塊被載入完成之后,如果第三個(gè)參數(shù)是一個(gè)回調(diào)函數(shù)則執(zhí)行,然后告訴系統(tǒng)模塊可用,也就通知了依賴于自己的模塊自己已經(jīng)可用。如果對應(yīng)到dojo1.6之前的實(shí)現(xiàn),那么在功能上可以有如下對應(yīng)關(guān)系:
module-name: dojo.provide dependencies: dojo.require module-factory: dojo.declare
不同的是,在加載依賴項(xiàng)時(shí),AMD用的是異步,而dojo.require是同步。異步和同步的區(qū)別顯而易見,前者不會(huì)阻塞瀏覽器,有更好的性能和靈活性。而對于NodeJs這樣的服務(wù)器端AMD,則模塊載入無需阻塞服務(wù)器進(jìn)程,同樣提高了性能
3,AMD實(shí)例:如何定義一個(gè)模塊
下面代碼定義了一個(gè)alpha模塊,并且依賴于內(nèi)置的require,exports模塊,以及外部的beta模塊??梢钥吹剑谌齻€(gè)參數(shù)是回調(diào)函數(shù),可以直接使用依賴的模塊,他們按依賴聲明順序作為參數(shù)提供給回調(diào)函數(shù)。
這里的require函數(shù)讓你能夠隨時(shí)去依賴一個(gè)模塊,即取得模塊的引用,從而即使模塊沒有作為參數(shù)定義,也能夠被使用;exports是定義的alpha 模塊的實(shí)體,在其上定義的任何屬性和方法也就是alpha模塊的屬性和方法。通過exports.verb = ...就是為alpha模塊定義了一個(gè)verb方法。例子中是簡單調(diào)用了模塊beta的verb方法。
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) { exports.verb = function() { return beta.verb(); //或者: return require("beta").verb(); } });
4. 匿名模塊
define 方法允許你省略第一個(gè)參數(shù),這樣就定義了一個(gè)匿名模塊,這時(shí)候模塊文件的文件名就是模塊標(biāo)識(shí)。如果這個(gè)模塊文件放在a.js中,那么a就是模塊名??梢栽谝蕾図?xiàng)中用"a"來依賴于這個(gè)匿名模塊。這帶來一個(gè)好處,就是模塊是高度可重用的。你拿來一個(gè)匿名模塊,隨便放在一個(gè)位置就可以使用它,模塊名就是它的文件路徑。這也很好的符合了DRY(Don"t Repeat Yourself)原則。
下面的代碼就定義了一個(gè)依賴于alpha模塊的匿名模塊:
define(["alpha"], function (alpha) { return { verb: function(){ return alpha.verb() + 2; } }; });
5. 僅有一個(gè)參數(shù)的define
前面提到,define的前兩個(gè)參數(shù)都是可以省略的。第三個(gè)參數(shù)有兩種情況,一種是一個(gè)JavaScript對象,另一種是一個(gè)函數(shù)。
如果是一個(gè)對象,那么它可能是一個(gè)包含方法具有功能的一個(gè)對象;也有可能是僅提供數(shù)據(jù)。后者和JSON-P非常類似,因此AMD也可以認(rèn)為包含了一個(gè)完整的 JSON-P實(shí)現(xiàn)。模塊演變?yōu)橐粋€(gè)簡單的數(shù)據(jù)對象,這樣的數(shù)據(jù)對象是高度可用的,而且因?yàn)槭庆o態(tài)對象,它也是CDN友好的,可以提高JSON-P的性能。考慮一個(gè)提供中國省市對應(yīng)關(guān)系的JavaScript對象,如果以傳統(tǒng)JSON-P的形式提供給客戶端,它必須提供一個(gè)callback函數(shù)名,根據(jù)這個(gè)函數(shù)名動(dòng)態(tài)生成返回?cái)?shù)據(jù),這使得標(biāo)準(zhǔn)JSON-P數(shù)據(jù)一定不是CDN友好的。但如果用AMD,這個(gè)數(shù)據(jù)文件就是如下的形式:
define({ provinces: [ { name: "上海", areas: ["浦東新區(qū)", "徐匯區(qū)"]}, { name: "江蘇", cities: ["南京", "南通"]} //..... ] });
假設(shè)這個(gè)文件名為china.js,那么如果某個(gè)模塊需要這個(gè)數(shù)據(jù),只需要:
define(["china", function(china){ //在這里使用中國省市數(shù)據(jù) });
通過這種方式,這個(gè)模塊是真正高度可復(fù)用的,無論是用遠(yuǎn)程的,還是Copy到本地項(xiàng)目,都節(jié)約了開發(fā)時(shí)間和維護(hù)時(shí)間。
如果參數(shù)是一個(gè)函數(shù),其用途之一是快速開發(fā)實(shí)現(xiàn)。適用于較小型的應(yīng)用,你無需提前關(guān)注自己需要什么模塊,自己給誰用。在函數(shù)中,可以隨時(shí)require自己需要的模塊。例如:
define(function(){ var p = require("china"); //使用china這個(gè)模塊 });
即你省略了模塊名,以及自己需要依賴的模塊。這不意味著你無需依賴于其他模塊,而是可以讓你在需要的時(shí)候去require這些模塊。define方法在執(zhí)行的時(shí)候,會(huì)調(diào)用函數(shù)的toString方法,并掃描其中的require調(diào)用,提前幫助你載入這些模塊,載入完成之后再執(zhí)行。這使得快速開發(fā)成為可能。需要注意的一點(diǎn)是,Opera不能很好的支持函數(shù)的toString方法,因此,在瀏覽器中它的適用性并不是很強(qiáng)。但如果你是通過build工具打包所有的 JavaScript文件,這將不是問題,構(gòu)建工具會(huì)幫助你掃描require并強(qiáng)制載入依賴的模塊。
6. Dojo中的AMD
Dojo 的1.6版本,其中一個(gè)重要的變化就是引入了AMD機(jī)制,取代了原來的dojo.provide和dojo.require方法。但是現(xiàn)在仍然保持了向后兼容性,你仍然可以用dojo.provide和dojo.require來定義和加載模塊。需要注意的是:在 Dojo 1.6 中, 針對 AMD 的重構(gòu)仍然屬于一個(gè)過渡期的改動(dòng) , 用戶自己開發(fā)的 AMD 模塊還不能被 Dojo 的加載器和 Build 系統(tǒng)支持 . 1.6 中現(xiàn)有的編譯系統(tǒng)對AMD的支持還非常局限。 如果你自己開發(fā)了 AMD 格式的模塊,并且你仍然在使用默認(rèn)的 Dojo 同步模塊加載器,那么你必須嚴(yán)格遵循 Dojo 模塊的格式 ( 包括換行的格式 ) 來保證你自己的模塊能夠成功編譯??偨Y(jié)起來有以下三點(diǎn):
用傳統(tǒng)的方法 (dojo.require()/dojo.provide()) – 這些模塊,只能被 Dojo 同步加載器 加載,但可以被 Dojo 編譯系統(tǒng)(Build System )正確的編譯
用 Dojo 同步加載器來加載 AMD 格式 ( define ()) 模塊 – 這些模塊可以被正常的加載,并且可以被其他兼容 AMD 格式的加載器加載 . 現(xiàn)在雖然 Dojo1.6 還沒有正式支持這種用法, 但在目前的 Dojo1.6 編譯系統(tǒng)中,是可以正常工作的 . ( 前提是你必須嚴(yán)格遵循 Dojo 模塊定義的代碼規(guī)范 )
使用第三方加載器來加載 AMD 格式( define ())模塊 – 模塊可以被正常加載,并且可以被其他加載器所使用 . 這些模塊可以使用 RequireJS 或 Backdraft 提供的編譯系統(tǒng)正常編譯,但是 Dojo 還沒有正式的測試過和其他加載器的兼容性 .
以Calendar為例,用define方法來定義這個(gè)模塊:
define("dijit/Calendar", ["dojo", "dijit", "text!dijit/templates/Calendar.html", "dojo/cldr/supplemental", "dojo/date", "dojo/date/locale", "dijit/_Widget", "dijit/_Templated", "dijit/_CssStateMixin", "dijit/form/DropDownButton"], function(dojo, dijit) { dojo.declare( "dijit.Calendar", [dijit._Widget, dijit._Templated, dijit._CssStateMixin], {...} ); return dijit.Calendar; } );
可以看到,模塊標(biāo)識(shí)就是模塊文件的路徑,模塊本身一般都是dojo.declare定義的類。Dojo1.6中的dojo和dijit命名空間下的模塊均已經(jīng)用AMD的形式進(jìn)行了重構(gòu),但dojox下仍然延用了傳統(tǒng)的dojo.provide和dojo.require形式。對AMD的引入是Dojo走向自動(dòng)化包管理的重要一步,在后續(xù)文章中我們也將繼續(xù)關(guān)注Dojo在這方面的進(jìn)展。
7. 結(jié)論
AMD 規(guī)范是JavaScript開發(fā)的一次重要嘗試,它以簡單而優(yōu)雅的方式統(tǒng)一了JavaScript的模塊定義和加載機(jī)制,并迅速得到很多框架的認(rèn)可和采納。這對開發(fā)人員來說是一個(gè)好消息,通過AMD我們降低了學(xué)習(xí)和使用各種框架的門檻,能夠以一種統(tǒng)一的方式去定義和使用模塊,提高開發(fā)效率,降低了應(yīng)用維護(hù)成本。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/81575.html
摘要:阮一峰的規(guī)范阮一峰的規(guī)范中中文版主要講函數(shù)中中文版主要講中函數(shù)總結(jié)所有的詳解模塊化開發(fā) 阮一峰的CommonJS規(guī)范阮一峰的AMD規(guī)范GitHub中AMD (中文版)主要講define函數(shù)GitHub中require (中文版)主要講AMD中define函數(shù)總結(jié)所有的詳解JavaScript模塊化開發(fā)
摘要:從到完美,寫一個(gè)庫庫前端組件庫之前講了很多關(guān)于項(xiàng)目工程化前端架構(gòu)前端構(gòu)建等方面的技術(shù),這次說說怎么寫一個(gè)完美的第三方庫。使用導(dǎo)出模塊,就可以在使用這個(gè)庫的項(xiàng)目中構(gòu)建時(shí)使用功能。 從 1 到完美,寫一個(gè) js 庫、node 庫、前端組件庫 之前講了很多關(guān)于項(xiàng)目工程化、前端架構(gòu)、前端構(gòu)建等方面的技術(shù),這次說說怎么寫一個(gè)完美的第三方庫。 1. 選擇合適的規(guī)范來寫代碼 js 模塊化的發(fā)展大致有...
摘要:從到完美,寫一個(gè)庫庫前端組件庫之前講了很多關(guān)于項(xiàng)目工程化前端架構(gòu)前端構(gòu)建等方面的技術(shù),這次說說怎么寫一個(gè)完美的第三方庫。使用導(dǎo)出模塊,就可以在使用這個(gè)庫的項(xiàng)目中構(gòu)建時(shí)使用功能。 從 1 到完美,寫一個(gè) js 庫、node 庫、前端組件庫 之前講了很多關(guān)于項(xiàng)目工程化、前端架構(gòu)、前端構(gòu)建等方面的技術(shù),這次說說怎么寫一個(gè)完美的第三方庫。 1. 選擇合適的規(guī)范來寫代碼 js 模塊化的發(fā)展大致有...
摘要:其中命令用于輸入其他模塊提供的功能,命令用于規(guī)定模塊的對外接口。 簡單記錄下自己對前端模塊化的一些淺薄理解,不對之處歡迎指正 在介紹前端模打包工具之前,我們先簡單說一下瀏覽器、v8和nodejs,因?yàn)槲覀儸F(xiàn)在用到的大部分前端模打包工具,都是需要nodejs環(huán)境運(yùn)行的,同時(shí)他們都能通過npm進(jìn)行下載 1. 瀏覽器: 瀏覽器組成可分兩部分:Shell+內(nèi)核。瀏覽器內(nèi)核又可以分成兩部分:渲染...
摘要:的主要思想是異步模塊,主邏輯在回調(diào)函數(shù)中執(zhí)行,這和瀏覽器前端所習(xí)慣的開發(fā)方式不謀而合,應(yīng)運(yùn)而生。是目前開發(fā)中使用率最高的模塊化標(biāo)準(zhǔn)。 原文鏈接: http://yanjiie.me 偶然的一個(gè)周末復(fù)習(xí)了一下 JS 的模塊標(biāo)準(zhǔn),刷新了一下對 JS 模塊化的理解。 從開始 Coding 以來,總會(huì)周期性地突發(fā)奇想進(jìn)行 Code Review。既是對一段時(shí)期的代碼進(jìn)行總結(jié),也是對那一段時(shí)光的...
閱讀 1743·2019-08-30 15:54
閱讀 3395·2019-08-26 17:15
閱讀 3594·2019-08-26 13:49
閱讀 2616·2019-08-26 13:38
閱讀 2350·2019-08-26 12:08
閱讀 3199·2019-08-26 10:41
閱讀 1407·2019-08-26 10:24
閱讀 3422·2019-08-23 18:35