回顧:前端模塊化和AMD、CMD規範(全)

先列舉下一些著名言論:前端

「我想定義一個 each 方法遍歷對象,但頁頭的 util.js 裏已經定義了一個,個人只能叫 eachObject 了,好無奈。」jquery

「RequireJS 是沒有明顯的 bug,SeaJS 是明顯沒有 bug。」git

「在用SeaJS,除了打包很是痛苦外,其餘的還好」程序員

「你變了精彩的魔術,咱們會爲你喝彩。但你想讓咱們信任你,你得主動解釋魔術的奧祕。不然我會以爲本身被耍了。」github

「這兩個加載器和標準沒有優劣之分,只有差異。具體仍是要根據實際狀況進行選擇;」正則表達式

……npm

前端模塊化

這個是個老掉牙的話題,我就再也不誤人子弟了,歡迎閱讀這篇由 玉伯 大大執筆的《 前端模塊化開發的價值api

模塊化能解決:

  • 模塊的版本管理 。經過別名等配置,配合構建工具,能夠比較輕鬆地實現模塊的版本管理。瀏覽器

  • 提升可維護性 。模塊化可讓每一個文件的職責單一,很是有利於代碼的維護。Sea.js 還提供了 nocache、debug 等插件,擁有在線調試等功能,能比較明顯地提高效率。性能優化

  • 前端性能優化 。Sea.js 經過異步加載模塊,這對頁面性能很是有益。Sea.js 還提供了 combo、flush 等插件,配合服務端,能夠很好地對頁面性能進行調優。

  • 跨環境共享模塊 。CMD 模塊定義規範與 Node.js 的模塊規範很是相近。經過 Sea.js 的 Node.js 版本,能夠很方便實現模塊的跨服務器和瀏覽器共享。

sea.js 舉例

sea.js是一個專門爲解決瀏覽器模塊化開發的方案,SeaJS定義了一個全局函數 define 它用來定義一個模塊,SeaJS提倡:文件即模塊。

  • 先貼一段模塊代碼

全部模塊都經過 define 來定義

define(function(require, exports, module) {

    // 經過 require 引入依賴 注意 .js 能夠省略
    var $ = require('jquery');
    // 你也能夠引入本身的函數依賴
    var Spinning = require('./yourFunction');
    var util = {};
    util.sayHello = function(){
        return 'seajs向你問好';
    }
    // 經過 exports 對外提供接口
    // exports 和 module.exports 區別見下文:
    //exports.doSomething = function() {

    //};
    // 或者經過 module.exports 提供整個接口
    module.exports = util;

});
// 看完這一段 你應該明白了 require, exports, module 三個參數

前端模塊化開發的歷史

既然是 回顧 那就應該好文不斷: 《 前端模塊化開發那點歷史

AMD規範及其表明: RequireJS

AMD規範

  • 中文版在這裏AMD (中文版),或者: 鳥語原版

  • 簡單說: 就是 異步模塊定義(Asynchronous Module Definition),它是 依賴前置 (由於依賴必須一開始就寫好)會先 儘早地執行(依賴)模塊 , 至關於全部的require都被提早了,它的 require 分全局和局部, 一個API當多個用

  • define() 函數

看下它的具體參數說明

define(
    //這個模塊的名稱
    "types/Manager",
    //依賴的模塊
    ["types/Employee"],
    //函數執行時,全部依賴項加載。這個函數的參數依賴上述模塊。
    function (Employee) {
        function Manager () {
            this.reports = [];
        }
        //開始執行
        Manager.prototype = new Employee();
        //返回經理構造函數能夠由其餘模塊的應用。
        return Manager;
    }
);

一些例子

建立一個名爲"alpha"的模塊,使用了require,exports,和名爲"beta"的模塊:

define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
   exports.verb = function() {
       return beta.verb();
       //或者:
       //return require("beta").verb();
   }
});

一個返回對象的匿名模塊:

define(["alpha"], function (alpha) {
   return {
     verb: function(){
       return alpha.verb() + 2;
     }
   };
});

一個使用了簡單CommonJS轉換的模塊定義:

define(function (require, exports, module) {
    var a = require('a'),
        b = require('b');

    exports.action = function () {};
});

CMD規範及其表明: SeaJS

CMD規範

  • 中文版在這裏CMD 模塊定義規範 ,也有 鳥語版

  • 簡單說 : CMD(Common Module Definition)更貼近 CommonJS Modules/1.1 和 Node Modules 規範,一個模塊就是一個文件;它推崇 依賴就近 想何時 require 就何時加載,實現了 懶加載, 延遲執行 (as lazy as possible) ;也沒有全局 require, 每一個API都簡單純粹

  • define() 函數相似,看定義便可

module.exports 和 exports 區別

常用的 API 只有 define, require, require.async, exports, module.exports 這五個。其餘 API 有個印象就好。

傳給 factory 構造方法的 exports 參數是 module.exports 對象的一個引用。只經過 exports 參數來提供接口,有時沒法知足開發者的全部需求。 好比當模塊的接口是某個類的實例時,須要經過 module.exports 來實現:

define(function(require, exports, module) {
    // exports 是 module.exports 的一個引用
    console.log(module.exports === exports); // true
    // 從新給 module.exports 賦值
    module.exports = new SomeClass();
    // exports 再也不等於 module.exports
    console.log(module.exports === exports); // false
});

對 module.exports 的賦值須要同步執行,不能放在回調函數裏。下面這樣是不行的:

// x.js
define(function(require, exports, module) {
    // 錯誤用法
    setTimeout(function() {
        module.exports = { a: "hello" };
    }, 0);

});

在 y.js 裏有調用到上面的 x.js:

// y.js
define(function(require, exports, module) {

    var x = require('./x');

    // 沒法馬上獲得模塊 x 的屬性 a
    console.log(x.a); // undefined

});

AMD(RequireJS)和CMD(SeaJS)異同

看看執行流程

當咱們看到RequireJS 的接口,

require(['a','b'],function(){
    //Do something
})

實際作的事情是:
一、require 函數檢查依賴的模塊,根據配置文件,獲取js文件的實際路徑
二、根據js文件實際路徑,在dom中插入script節點,並綁定onload事件來獲取該模塊加載完成的通知。
三、依賴script所有加載完成後,調用回調函數

Sea.js在調用時

define('a',function(require,exports,modules){
    var b = require('b')
})

一、經過回調函數的Function.toString函數,使用正則表達式來捕捉內部的require字段,找到require('jquery')內部依賴的模塊jquery
二、根據配置文件,找到jquery的js文件的實際路徑
三、在dom中插入script標籤,載入模塊指定的js,綁定加載完成的事件,使得加載完成後將js文件綁定到require模塊指定的id(這裏就是jquery這個字符串)上
四、回調函數內部依賴的js所有加載(暫不調用)完後,調用回調函數
五、當回調函數調用require('jquery'),即執行綁定在'jquery'這個id上的js文件,即刻執行,並將返回值傳給var b

注意: SeaJS 這種用 ** 正則表達式 捕捉require內部依賴模塊的方式** ,使得沒法利用還沒有執行的回調函數中的js運行環境,致使require函數的內部只能將依賴的模塊名稱硬編碼,就不能寫下面這樣的代碼了

define('a',function(require,exports,modules){
    //錯誤
    var b = require('Us'+'er');
})

而只能寫成

define('a',function(require,exports,modules){
    //正確
    var b = require('User');
})

其餘的區別其實介紹時已經說明了,上文~ 小總結:玉伯:與 RequireJS 的異同

相同之處

RequireJS 和 Sea.js 都是模塊加載器,倡導模塊化開發理念,核心價值是讓 JavaScript 的模塊化開發變得簡單天然。

不一樣之處

二者的主要區別以下:

  • 定位有差別。RequireJS 想成爲瀏覽器端的模塊加載器,同時也想成爲 Rhino / Node 等環境的模塊加載器。Sea.js 則專一於 Web 瀏覽器端,同時經過 Node 擴展的方式能夠很方便跑在 Node 環境中。

  • 遵循的規範不一樣。RequireJS 遵循 AMD(異步模塊定義)規範,Sea.js 遵循 CMD (通用模塊定義)規範。規範的不一樣,致使了二者 API 不一樣。Sea.js 更貼近 CommonJS Modules/1.1 和 Node Modules 規範。

  • 推廣理念有差別。RequireJS 在嘗試讓第三方類庫修改自身來支持 RequireJS,目前只有少數社區採納。Sea.js 不強推,採用自主封裝的方式來「海納百川」,目前已有較成熟的封裝策略。

  • 對開發調試的支持有差別。Sea.js 很是關注代碼的開發調試,有 nocache、debug 等用於調試的插件。RequireJS 無這方面的明顯支持。

  • 二者代碼質量有差別。RequireJS 是沒有明顯的 bug,SeaJS 是明顯沒有 bug。

  • 插件機制不一樣。RequireJS 採起的是在源碼中預留接口的形式,插件類型比較單一。Sea.js 採起的是通用事件機制,插件類型更豐富。

還有很多差別,涉及具體使用方式和源碼實現,歡迎有興趣者研究並發表見解。

總之,若是說 RequireJS 是 Prototype 類庫的話,則 Sea.js 致力於成爲 jQuery 類庫。

還不夠

這裏有一個圖文並茂的 「栗子」 SeaJS與RequireJS最大的區別,固然樓主的觀點更同意這個「栗子」: LABjs、RequireJS、SeaJS 哪一個最好用?爲何?

結論: 這兩個加載器和標準沒有優劣之分,只有差異。具體仍是要根據實際狀況進行選擇;


相關文章
相關標籤/搜索