標籤:data- ack issues 職責 自動 耦合 需要 關於 foo
原文地址:http://blog.chinaunix.net/uid-26672038-id-4112229.html
JavaSript模組化在瞭解AMD,CMD規範前,還是需要先來簡單地瞭解下什麼是模組化,模組化開發? 模組化是指在解決某一個複雜問題或者一系列的雜糅問題時,依照一種分類的思維把問題進行系統性的分解以之處理。模組化是一種處理複雜系統分解為代碼結構更合理,可維護性更高的可管理的模組的方式。可以想象一個巨大的系統代碼,被整合最佳化分割成邏輯性很強的模組時,對於軟體是一種何等意義的存在。對於軟體行業來說:解耦軟體系統的複雜性,使得不管多麼大的系統,也可以將管理,開發,維護變得“有理可循”。 還有一些對於模組化一些專業的定義為:模組化是軟體系統的屬性,這個系統被分解為一組高內聚,低耦合的模組。那麼在理想狀態下我們只需要完成自己部分的核心商務邏輯代碼,其他方面的依賴可以通過直接載入被人已經寫好模組進行使用即可。 首先,既然是模組化設計,那麼作為一個模組化系統所必須的能力: 1. 定義封裝的模組。 2. 定義新模組對其他模組的依賴。 3. 可對其他模組的引入支援。 好了,思想有了,那麼總要有點什麼來建立一個模組化的規範制度吧,不然各式各樣的模組載入方式只會將局攪得更為混亂。那麼在JavaScript中出現了一些非傳統模組開發方式的規範 CommonJS的模組規範,AMD(Asynchronous Module Definition),CMD(Common Module Definition)等。
AMD 與 RequireJS
AMD Asynchronous Module Definition,用白話文講就是 非同步模組定義,對於 JSer 來說,非同步是再也熟悉不過的詞了,所有的模組將被非同步載入,模組載入不影響後面語句運行。所有依賴某些模組的語句均放置在回呼函數中。 AMD規範定義了一個自由變數或者說是全域變數 define 的函數。
define( id?, dependencies?, factory );
AMD規範 https://github.com/amdjs/amdjs-api/wiki/AMD 第一個參數 id 為字串類型,表示了模組標識,為選擇性參數。若不存在則模組標識應該預設定義為在載入器中被請求指令碼的標識。如果存在,那麼模組標識必須為頂層的或者一個絕對的標識。 第二個參數,dependencies ,是一個當前模組依賴的,已被模組定義的模組標識的數組字面量。 第三個參數,factory,是一個需要進行執行個體化的函數或者一個對象。 建立模組標識為 alpha 的模組,依賴於 require, export,和標識為 beta 的模組
- define("alpha", [ "require", "exports", "beta" ], function( require, exports, beta ){
- export.verb = function(){
- return beta.verb();
- // or:
- return require("beta").verb();
- }
- });
一個返回對象字面量的非同步模組
- define(["alpha"], function( alpha ){
- return {
- verb : function(){
- return alpha.verb() + 1 ;
- }
- }
- });
無相依模組可以直接使用對象字面量來定義
- define( {
- add : function( x, y ){
- return x + y ;
- }
- } );
類似與 CommonJS 方式定義
- define( function( require, exports, module){
- var a = require(‘a‘),
- b = require(‘b‘);
- exports.action = function(){};
- } );
require();
require API 介紹 https://github.com/amdjs/amdjs-api/wiki/require 在 AMD 規範中的 require 函數與一般的 CommonJS中的 require 不同。由於動態檢測依賴關係使載入非同步,對於基於回調的 require 需求強烈。
局部 與 全域 的require
局部的 require 需要在AMD模式中的 define 工廠函數中傳入 require。
- define( [‘require‘], function( require ){
- // ...
- } );
- or:
- define( function( require, exports, module ){
- // ...
- } );
局部的 require 需要其他特定的 API 來實現。 全域的 require 函數是唯一全域範圍下的變數,像 define一樣。全域的 require 並不是規範要求的,但是如果實現全域的 require函數,那麼其需要具有與局部 require 函數 一樣的以下的限定: 1. 模組標識視為絕對的,而不是相對的對應另一個模組標識。 2. 只有在非同步情況下,require的回調方式才被用來作為互動操作使用。因為他不可能在同步的情況下通過 require(String) 從頂層載入模組。 依賴相關的API會開始模組載入。如果需要有互操作的多個載入器,那麼全域的 reqiure 應該被載入頂層模組來代替。
- require(String)
- define( function( require ){
- var a = require(‘a‘); // 載入模組a
- } );
- require(Array, Function)
- define( function( require ){
- require( [‘a‘, ‘b‘], function( a,b ){ // 載入模組a b 使用
- // 依賴 a b 模組的運行代碼
- } );
- } );
- require.toUrl( Url )
- define( function( require ){
- var temp = require.toUrl(‘./temp/a.html‘); // 載入頁面
- } );
amdjs 的API https://github.com/amdjs/amdjs-api/wiki
RequireJS 官網 http://www.requirejs.org/ API http://www.requirejs.org/docs/api.html RequireJS 是一個前端的模組化管理的工具庫,遵循AMD規範,它的作者就是AMD規範的創始人 James Burke。所以說RequireJS是對AMD規範的闡述一點也不為過。 RequireJS 的基本思想為:通過一個函數來將所有所需要的或者說所依賴的模組實現裝載進來,然後返回一個新的函數(模組),我們所有的關於新模組的業務代碼都在這個函數內部操作,其內部也可無限制的使用已經載入進來的以來的模組。
- <script data-main=‘scripts/main‘ src=‘scripts/require.js‘></script>
那麼scripts下的main.js則是指定的主代碼指令檔,所有的相依模組代碼檔案都將從該檔案開始非同步載入進入執行。
defined用於定義模組,RequireJS要求每個模組均放在獨立的檔案之中。按照是否有依賴其他模組的情況分為獨立模組和非獨立模組。 1. 獨立模組,不依賴其他模組。直接定義:
- define({
- method1: function(){},
- method2: function(){}
- });
也等價於
- define(function(){
- return{
- method1: function(){},
- method2: function(){}
- }
- });
2. 非獨立模組,對其他模組有依賴。
- define([ ‘module1‘, ‘module2‘ ], function(m1, m2){
- ...
- });
或者:
- define( function( require ){
- var m1 = require( ‘module1‘ ),
- m2 = require( ‘module2‘ );
- ...
- });
簡單看了一下RequireJS的實現方式,其 require 實現只不過是將 function 字串然後提取 require 之後的模組名,將其放入依賴關係之中。
require方法調用模組 在require進行調用模組時,其參數與define類似。
- require( [‘foo‘, ‘bar‘], function( foo, bar ){
- foo.func();
- bar.func();
- } );
在載入 foo 與 bar 兩個模組之後執行回呼函數實現具體過程。 當然還可以如之前的例子中的,在define定義模組內部進行require調用模組
- define( function( require ){
- var m1 = require( ‘module1‘ ),
- m2 = require( ‘module2‘ );
- ...
- });
define 和 require 這兩個定義模組,調用模組的方法合稱為AMD模式,定義模組清晰,不會汙染全域變數,清楚的顯示依賴關係。AMD模式可以用於瀏覽器環境並且允許非同步載入模組,也可以按需動態載入模組。
CMD 與 seaJS
CMD 在CMD中,一個模組就是一個檔案,格式為:
define( factory ); 全域函數define,用來定義模組。 參數 factory 可以是一個函數,也可以為對象或者字串。 當 factory 為對象、字串時,表示模組的介面就是該對象、字串。 定義JSON資料模組:
- define({ "foo": "bar" });
通過字串定義模板模組:
- define(‘this is {{data}}.‘);
factory 為函數的時候,表示模組的構造方法,執行構造方法便可以得到模組向外提供的介面。
- define( function(require, exports, module) {
- // 模組代碼
- });
define( id?, deps?, factory ); define也可以接受兩個以上的參數,字串id為模組標識,數組deps為模組依賴:
- define( ‘module‘, [‘module1‘, ‘module2‘], function( require, exports, module ){
- // 模組代碼
- } );
其與 AMD 規範用法不同。
require 是 factory 的第一個參數。 require( id ); 接受模組標識作為唯一的參數,用來擷取其他模組提供的介面:
- define(function( require, exports ){
- var a = require(‘./a‘);
- a.doSomething();
- });
require.async( id, callback? ); require是同步往下執行的,需要的非同步載入模組可以使用 require.async 來進行載入:
- define( function(require, exports, module) {
- require.async(‘.a‘, function(a){
- a.doSomething();
- });
- });
require.resolve( id ) 可以使用模組內部的路徑機制來返回模組路徑,不會載入模組。
exports 是 factory 的第二個參數,用來向外提供模組介面。
- define(function( require, exports ){
- exports.foo = ‘bar‘; // 向外提供的屬性
- exports.do = function(){}; // 向外提供的方法
- });
當然也可以使用 return 直接向外提供介面。
- define(function( require, exports ){
- return{
- foo : ‘bar‘, // 向外提供的屬性
- do : function(){} // 向外提供的方法
- }
- });
也可以簡化為直接對象字面量的形式:
- define({
- foo : ‘bar‘, // 向外提供的屬性
- do : function(){} // 向外提供的方法
- });
與nodeJS中一樣需要注意的是,一下方式是錯誤的:
- define(function( require, exports ){
- exports = {
- foo : ‘bar‘, // 向外提供的屬性
- do : function(){} // 向外提供的方法
- }
- });
需要這麼做
- define(function( require, exports, module ){
- module.exports = {
- foo : ‘bar‘, // 向外提供的屬性
- do : function(){} // 向外提供的方法
- }
- });
傳入的對象引用可以添加屬性,一旦賦值一個新的對象,那麼值錢傳遞進來的對象引用就會失效了。開始之初,exports 是作為 module.exports 的一個引用存在,一切行為只有在這個引用上 factory 才得以正常運行,賦值新的對象後就會斷開引用,exports就只是一個新的對象引用,對於factory來說毫無意義,就會出錯。
module 是factory的第三個參數,為一個對象,上面儲存了一些與當前模組相關聯的屬性與方法。 module.id 為模組的唯一標識。 module.uri 根據模組系統的路徑解析規則得到模組的絕對路徑。 module.dependencies 表示模組的依賴。 module.exports 當前模組對外提供的介面。
seaJS 官網 http://seajs.org/docs/ API快速參考 https://github.com/seajs/seajs/issues/266
sea.js 核心特徵: 1. 遵循CMD規範,與NodeJS般的書寫模組代碼。 2. 依賴自動載入,配置清晰簡潔。 相容 Chrome 3+,Firefox 2+,Safari 3.2+,Opera 10+,IE 5.5+。
seajs.use 用來在頁面中載入一個或者多個模組
- // 載入一個模組
- seajs.use(‘./a‘);
- // 載入模組,載入完成時執行回調
- seajs.use(‘./a‘,function(a){
- a.doSomething();
- });
- // 載入多個模組執行回調
- seajs.use([‘./a‘,‘./b‘],function(a , b){
- a.doSomething();
- b.doSomething();
- });
其define 與 require 使用方式基本就是CMD規範中的樣本。
AMD 與 CMD 區別到底在哪裡?
看了以上 AMD,requireJS 與 CMD, seaJS的簡單介紹會有點感覺模糊,總感覺較為相似。因為像 requireJS 其並不是只是純粹的AMD固有思想,其也是有CMD規範的思想,只不過是推薦 AMD規範方式而已, seaJS也是一樣。 下面是玉伯對於 AMD 與 CMD 區別的解釋: AMD 是 RequireJS 在推廣過程中對模組定義的正常化產出。 CMD 是 SeaJS 在推廣過程中對模組定義的正常化產出。 類似的還有 CommonJS Modules/2.0 規範,是 BravoJS 在推廣過程中對模組定義的正常化產出還有不少?? 這些規範的目的都是為了 JavaScript 的模組化開發,特別是在瀏覽器端的。 目前這些規範的實現都能達成瀏覽器端模組化開發的目的。 區別: 1. 對於依賴的模組,AMD 是提前執行,CMD 是順延強制。不過 RequireJS 從 2.0 開始,也改成可以順延強制(根據寫法不同,處理方式不同)。CMD 推崇 as lazy as possible. 2. CMD 推崇依賴就近,AMD 推崇依賴前置。看代碼:
- // CMD
- define(function(require, exports, module) {
- var a = require(‘./a‘)
- a.doSomething()
- // 此處略去 100 行
- var b = require(‘./b‘) // 依賴可以就近書寫
- b.doSomething()
- // ...
- })
- // AMD 預設推薦的是
- define([‘./a‘, ‘./b‘], function(a, b) { // 依賴必須一開始就寫好
- a.doSomething()
- // 此處略去 100 行
- b.doSomething()
- // ...
- })
雖然 AMD 也支援 CMD 的寫法,同時還支援將 require 作為依賴項傳遞,但 RequireJS 的作者預設是最喜歡上面的寫法,也是官方文檔裡預設的模組定義寫法。 3. AMD 的 API 預設是一個當多個用,CMD 的 API 嚴格區分,推崇職責單一。比如 AMD 裡,require 分全域 require 和局部 require,都叫 require。CMD 裡,沒有全域 require,而是根據模組系統的完備性,提供 seajs.use 來實現模組系統的載入啟動。CMD 裡,每個 API 都簡單純粹。 4. 還有一些細節差異,具體看這個規範的定義就好,就不多說了。 另外,SeaJS 和 RequireJS 的差異,可以參考:https://github.com/seajs/seajs/issues/277
總結 本文主要是介紹了一下 AMD CMD的規範,順便簡單的講述了一下 requireJS 與 seaJS。講的較為籠統,下面的擴充閱讀可以更好的協助你理解模組化以及各個規範。
擴充閱讀: AMD規範文檔 https://github.com/amdjs/amdjs-api/wiki/AMD
amdjs 的 require 介面文檔 https://github.com/amdjs/amdjs-api/wiki/require
amdjs 的介面文檔 https://github.com/amdjs/amdjs-api/wikiRequireJS官網介面文檔 http://www.requirejs.org/docs/api.html 模組系統 https://github.com/seajs/seajs/issues/240前端模組化開發的價值 https://github.com/seajs/seajs/issues/547前端模組化開發那點曆史 https://github.com/seajs/seajs/issues/588CMD 模組定義規範 https://github.com/seajs/seajs/issues/242SeaJS API快速參考 https://github.com/seajs/seajs/issues/266從 CommonJS 到 Sea.js https://github.com/seajs/seajs/issues/269
RequireJS和AMD規範 http://javascript.ruanyifeng.com/tool/requirejs.html
CommonJS規範 http://javascript.ruanyifeng.com/nodejs/commonjs.htmlJavascript模組化編程 http://www.ruanyifeng.com/blog/2012/10/javascript_module.htmlJavascript模組化編程 http://www.ruanyifeng.com/blog/2012/10/asynchronous_module_definition.html
知乎 AMD 和 CMD 的區別有哪些? http://www.zhihu.com/question/20351507
JavaScript模組化開發 - CommonJS規範 http://www.feeldesignstudio.com/2013/09/javascript-module-pattern-commonjs
JavaScript模組化開發 - AMD規範 http://www.feeldesignstudio.com/2013/09/javascript-module-pattern-amd
模組化設計 http://baike.baidu.com/view/189730.htm
模組化 http://baike.baidu.com/view/182267.htm
JavaSript模組規範 - AMD規範與CMD規範介紹[轉]