前端工程師必備:前端的模塊化思想

JS模塊化

@(工程化和項目管理)css

模塊化的理解

  • 什麼是模塊?
    • 將一個複雜的程序依據必定的規則(規範)封裝成幾個塊(文件), 並進行組合在一塊兒;
    • 塊的內部數據/實現是私有的, 只是向外部暴露一些接口(方法)與外部其它模塊通訊;
  • 一個模塊的組成
    • 數據--->內部的屬性;
    • 操做數據的行爲--->內部的函數;
  • 模塊化是指解決一個複雜的問題時自頂向下把系統劃分紅若干模塊的過程,有多種屬性,分別反映其內部特性;
  • 模塊化編碼:編碼時是按照模塊一個一個編碼的, 整個項目就是一個模塊化的項目;

非模塊化的問題

  • 頁面加載多個js的問題:
<script type="text/javascript" src="module1.js"></script>
<script type="text/javascript" src="module2.js"></script>
<script type="text/javascript" src="module3.js"></script>
<script type="text/javascript" src="module4.js"></script>
  • 發生問題:
    • 難以維護 ;
    • 依賴模糊;
    • 請求過多;
  • 因此,這些問題能夠經過現代模塊化編碼和項目構建來解決;

模塊化的優勢

  • 更好地分離:避免一個頁面中放置多個script標籤,而只需加載一個須要的總體模塊便可,這樣對於HTML和JavaScript分離頗有好處;
  • 更好的代碼組織方式:有利於後期更好的維護代碼;
  • 按需加載:提升使用性能,和下載速度,按需求加載須要的模塊
  • 避免命名衝突:JavaScript自己是沒有命名空間,常常會有命名衝突,模塊化就能使模塊內的任何形式的命名都不會再和其餘模塊有衝突。
  • 更好的依賴處理:使用模塊化,只須要在模塊內部申明好依賴的就行,增長刪除都直接修改模塊便可,在調用的時候也不用管該模塊依賴了哪些其餘模塊。

模塊化的發展歷程

原始寫法

  • 只是把不一樣的函數簡單地放在一塊兒,就算一個模塊;
function fun1(){
  //...
}
function fun2(){
  //...
}
//上面的函數fun1,fun2組成了一個模塊,使用的時候直接調用某個函數就好了。
  • 缺點:
    • "污染"了全局變量,沒法保證不與其餘模塊發生變量名衝突;
    • 模塊成員之間看不出直接關係。

對象寫法

  • 爲了解決污染全局變量的問題,能夠把模塊寫成一個對象,全部的模塊成員都放到這個對象裏面。
 var module1 = new Object({
    count : 0,
    fun1 : function (){
     //...
    },
  fun2 : function (){
     //...
   }
 });
 //這個裏面的fun1和fun2都封裝在一個賭俠寧裏,能夠經過對象.方法的形式進行調用;
 module1.fun1();
  • 優勢:
    • 減小了全局上的變量數目;
  • 缺點:
    • 本質是對象,而這個對象會暴露全部模塊成員,內部狀態能夠被外部改寫。

當即執行函數(IIFE模式)

  • 避免暴露私有成員,因此使用當即執行函數(自調函數,IIFE);
  • 做用: 數據是私有的, 外部只能經過暴露的方法操做
var module1 = (function(){
    var count = 0;
    var fun1 = function(){
        //...
    }
    var fun2 = function(){
        //...
    }
    //將想要暴露的內容放置到一個對象中,經過return返回到全局做用域。
    return{
        fun1:fun1,
        fun2:fun2
    }
})()
//這樣的話只能在全局做用域中讀到fun1和fun2,可是讀不到變量count,也修改不了了。
//問題:當前這個模塊依賴另外一個模塊怎麼辦?

IIFE的加強(引入依賴)

  • 若是一個模塊很大,必須分紅幾個部分,或者一個模塊須要繼承另外一個模塊,這時就有必要採用"加強模式";
  • IIFE模式加強:引入依賴;
  • 這就是現代模塊實現的基石;
var module1 = (function (mod){
  mod.fun3 = function () {
   //...
    };
    return mod;
})(module1);
//爲module1模塊添加了一個新方法fun3(),而後返回新的module1模塊。

//引入jquery到項目中;
var Module = (function($){
    var _$body = $("body");     // we can use jQuery now!
    var foo = function(){
    console.log(_$body);    // 特權方法
    }
  // Revelation Pattern
      return {
          foo: foo
      }
})(jQuery)
Module.foo();

js模塊化須要解決那些問題:

  • 1.如何安全的包裝一個模塊的代碼?(不污染模塊外的任何代碼)
  • 2.如何惟一標識一個模塊?
  • 3.如何優雅的把模塊的API暴漏出去?(不能增長全局變量)
  • 4.如何方便的使用所依賴的模塊?

模塊化規範

  • Node: 服務器端
  • Browserify : 瀏覽器端html

    CommonJS:服務器端

  • 概述
    • Node 應用由模塊組成,採用 CommonJS 模塊規範。
    • CommonJS規範規定,每一個模塊內部,module變量表明當前模塊。這個變量是一個對象,它的exports屬性(即module.exports)是對外的接口。加載某個模塊,實際上是加載該模塊的module.exports屬性。
  • 特色
    • 全部代碼都運行在模塊做用域,不會污染全局做用域。
    • 模塊能夠屢次加載,可是隻會在第一次加載時運行一次,而後運行結果就被緩存了,之後再加載,就直接讀取緩存結果。要想讓模塊再次運行,必須清除緩存。
    • 模塊加載的順序,按照其在代碼中出現的順序。
  • 基本語法:
    • 定義暴露模塊 : exports
    exports.xxx = value
    // 經過module.exports指定暴露的對象value
    module.exports = value
    • 引入模塊 : require
    var module = require('模塊相對路徑')
  • 引入模塊發生在何時?
    • Node:運行時, 動態同步引入;
    • Browserify:在運行前對模塊進行編譯/轉譯/打包的處理(已經將依賴的模塊包含進來了), 運行的是打包生成的js, 運行時不須要再從遠程引入依賴模塊;

CommonJS通用的模塊規範(同步)

  • Node內部提供一個Module構建函數。全部模塊都是Module的實例。
  • 每一個模塊內部,都有一個module對象,表明當前模塊。
  • module.exports屬性表示當前模塊對外輸出的接口,其餘文件加載該模塊,實際上就是讀取module.exports變量。
  • Node爲每一個模塊提供一個exports變量,指向module.exports。
  • 若是一個模塊的對外接口,就是一個單一的值,不能使用exports輸出,只能使用module.exports輸出。
  • Modules/1.0規範包含內容:
    1. 模塊的標識應遵循的規則(書寫規範)
    2. 定義全局函數require,經過傳入模塊標識來引入其餘模塊,執行的結果即爲模塊暴露出來的API;
    3. 若是被require函數引入的模塊中也包含依賴,那麼依次加載這些依賴;
    4. 若是引入模塊失敗,那麼require函數應該報一個異常;
    5. 模塊經過變量exports來向外暴露API,exports賦值暴露的只能是一個對象exports = {Obj},暴露的API須做爲此對象的屬性。exports本質是引入了module.exports的對象。不能直接將exports變量指向一個值,由於這樣等於切斷了exports與module.exports的聯繫。
    6. 若是暴露的不是變量exports,而是module.exports。module變量表明當前模塊,這個變量是一個對象,它的exports屬性(即module.exports)是對外的接口。加載某個模塊,實際上是加載該模塊的module.exports屬性。exports=module.exports={Obj}
node中的commonJS教程
  • 1.安裝node.js;
  • 2.建立項目結構
//結構以下
|-modules
    |-module1.js//待引入模塊1
    |-module2.js//待引入模塊2
    |-module3.js//待引入模塊3
|-app.js//主模塊
|-package.json
    {
      "name": "commonjsnode",
      "version": "1.0.0"
    }
  • 3.下載第三方模塊:舉例express
    npm i express --savejava

  • 4.模塊化編碼
// module1 
// 使用module.exports = value向外暴露一個對象
module.exports = {
    name: 'this is module1',
    foo(){
        console.log('module1 foo()');
    }
}
// module2 
// 使用module.exports = value向外暴露一個函數 
module.exports = function () {
    console.log('module2()');
}
// module3 
// 使用exports.xxx = value向外暴露一個對象
 exports.foo = function () {
     console.log('module3 foo()');
 };
 exports.bar = function () {
     console.log('module3 bar()');
 };
 exports.name = 'this is module3'

//app.js文件
var uniq = require('uniq');
//引用模塊
let module1 = require('./modules/module1');
let module2 = require('./modules/module2');
let module3 = require('./modules/module3');
//使用模塊
module1.foo();
module2();
module3.foo();
module3.bar();
module3.name;
  • 5.經過node運行app.js
    • 命令:node.app.js
    • 工具:右鍵-->運行
瀏覽器中的commonJS教程
  • 藉助Browserify
  • 步驟
    1. 建立項目結構
    |-js
         |-dist //打包生成文件的目錄
         |-src //源碼所在的目錄
               |-module1.js
               |-module2.js
               |-module3.js
               |-app.js //應用主源文件
    |-index.html //瀏覽器上的頁面
    |-package.json
        {
           "name": "browserify-test",
           "version": "1.0.0"
        }
    1. 下載browserify
      • 全局: npm install browserify -g
      • 局部: npm install browserify --save-dev
    2. 定義模塊代碼:index.html文件要運行在瀏覽器上,須要藉助browserify將app.js文件打包編譯,若是直接在index.html引入app.js就會報錯。
    3. 打包處理js:根目錄下運行browserify js/src/app.js -o js/dist/bundle.js
    4. 頁面使用引入:
    <script type="text/javascript" src="js/dist/bundle.js"></script>

AMD : 瀏覽器端

  • CommonJS規範加載模塊是同步的,也就是說,只有加載完成,才能執行後面的操做。
  • AMD規範則是非同步加載模塊,容許指定回調函數,能夠實現異步加載依賴模塊,而且會提早加載;
  • 因爲Node.js主要用於服務器編程,模塊文件通常都已經存在於本地硬盤,因此加載起來比較快,不用考慮非同步加載的方式,因此CommonJS規範比較適用。
  • 若是是瀏覽器環境,要從服務器端加載模塊,這時就必須採用非同步模式,所以瀏覽器端通常採用AMD規範。node

    語法

    AMD規範基本語法
  • 定義暴露模塊: define([依賴模塊名], function(){return 模塊對象})
  • 引入模塊: require(['模塊1', '模塊2', '模塊3'], function(m1, m2){//使用模塊對象})jquery

兼容CommonJS規範的輸出模塊
define(function (require, exports, module) { 
    var reqModule = require("./someModule");
    requModule.test();
    exports.asplode = function () {
        //someing
    }
});

AMD:異步模塊定義規範(預加載)

  • AMD規範:https://github.com/amdjs/amdjs-api/wiki/AMD
  • AMD是"Asynchronous Module Definition"的縮寫,意思就是"異步模塊定義"。
  • 它採用異步方式加載模塊,模塊的加載不影響它後面語句的運行。全部依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成以後,這個回調函數纔會運行。
  • AMD也採用require()語句加載模塊,可是不一樣於CommonJS,它要求兩個參數:
    require([module], callback);
    • 第一個參數[module],是一個數組,裏面的成員就是要加載的模塊;
    • 第二個參數callback,則是加載成功以後的回調函數。
  • 目前,主要有兩個Javascript庫實現了AMD規範:RequireJS和curl.js。

RequireJS

  • 優勢
    • 實現js文件的異步加載,避免網頁失去響應;
    • 管理模塊之間的依賴性,便於代碼的編寫和維護。git

      require.js使用教程
  1. 下載require.js, 並引入
  • 官網: https://requirejs.org/
  • 中文:https://blog.csdn.net/sanxian_li/article/details/39394097
  • github : https://github.com/requirejs/requirejs
  • 將require.js導入項目: js/libs/require.js
  1. 建立項目結構
|-js
  |-libs
    |-require.js // 引入的require.js
  |-modules
    |-alerter.js
    |-dataService.js
  |-main.js
|-index.html
  1. 定義require.js的模塊代碼
    • require.js加載的模塊,採用AMD規範。也就是說,模塊必須按照AMD的規定來寫。
    • 具體來講,就是模塊必須採用特定的define()函數來定義;
      • 若是一個模塊不依賴其餘模塊,那麼能夠直接定義在define()函數之中。
      define(['myLib'], function(myLib){
        function foo(){
          myLib.doSomething();
        }
        // 暴露模塊
        return {foo : foo};
      });
      //當require()函數加載上面這個模塊的時候,就會先加載myLib.js文件。
      • 若是這個模塊還依賴其餘模塊,那麼define()函數的第一個參數,必須是一個數組,指明該模塊的依賴性;
      // dataService.js
      define(function () {
        let msg = 'this is dataService'
        function getMsg() {
          return msg.toUpperCase()
        }
        return {getMsg}
      })
      
      // alerter.js
      define(['dataService', 'jquery'], function (dataService, $) {
        let name = 'Tom2'
        function showMsg() {
          $('body').css('background', 'gray')
          alert(dataService.getMsg() + ', ' + name)
        }
        return {showMsg}
      })
  2. 應用主(入口)js: main.js
    • 使用require.config()方法,咱們能夠對模塊的加載行爲進行自定義。require.config()就寫在主模塊main.js的頭部,參數就是一個對象,這個對象的paths屬性指定各個模塊的加載路徑。
    (function () {
        //配置
        require.config({
          //基本路徑
          baseUrl: "js/",
          //模塊標識名與模塊路徑映射
          paths: {
            "alerter": "modules/alerter",//此處不能寫成alerter.js,會報錯
            "dataService": "modules/dataService",
          }
        })
    
        //引入使用模塊
        require( ['alerter'], function(alerter) {
          alerter.showMsg()
        })
      })()
  3. 頁面使用模塊:
<script data-main="js/main" src="js/libs/require.js"></script>
定義模塊
  • require.config()接受一個配置對象,這個對象除了有前面說過的paths屬性以外,還有一個shim屬性,專門用來配置不兼容的模塊。
  • 具體來講,每一個模塊要定義:
    • 一、exports值(輸出的變量名),代表這個模塊外部調用時的名稱;
    • 二、deps數組,代表該模塊的依賴性。
  • 支持的配置項:
    • baseUrl :全部模塊的查找根路徑。
      • 當加載純.js文件(依賴字串以/開頭,或者以.js結尾,或者含有協議),不會使用baseUrl。
      • 如未顯式設置baseUrl,則默認值是加載require.js的HTML所處的位置。若是用了data-main屬性,則該路徑就變成baseUrl。
      • baseUrl可跟require.js頁面處於不一樣的域下,RequireJS腳本的加載是跨域的。惟一的限制是使用text! plugins加載文本內容時,這些路徑應跟頁面同域,至少在開發時應這樣。優化工具會將text! plugin資源內聯,所以在使用優化工具以後你可使用跨域引用text! plugin資源的那些資源。
    • paths:path映射那些不直接放置於baseUrl下的模塊名。
      • 設置path時起始位置是相對於baseUrl的,除非該path設置以"/"開頭或含有URL協議(如http:)。
      • 用於模塊名的path不該含有.js後綴,由於一個path有可能映射到一個目錄。路徑解析機制會自動在映射模塊名到path時添加上.js後綴。在文本模版之類的場景中使用require.toUrl()時它也會添加合適的後綴。
      • 在瀏覽器中運行時,可指定路徑的備選(fallbacks),以實現諸如首先指定了從CDN中加載,一旦CDN加載失敗則從本地位置中加載這類的機制;
    • shim: 爲那些沒有使用define()來聲明依賴關係、設置模塊的"瀏覽器全局變量注入"型腳本作依賴和導出配置。
使用第三方基於require.js的框架(jquery)
  • 將jquery的庫文件導入到項目: js/libs/jquery-1.10.1.js
  • 在main.js中配置jquery路徑
paths: {
   'jquery': 'libs/jquery-1.10.1'
}
  • 在alerter.js中使用jquery
define(['dataService', 'jquery'], function (dataService, \$) {
     var name = 'xfzhang'
     function showMsg() {
         $('body').css({background : 'red'})
         alert(name + ' '+dataService.getMsg())
     }
     return {showMsg}
 })
使用第三方不基於require.js的框架(angular)
  • 將angular.js導入項目:js/libs/angular.js
    • 流行的函數庫(好比jQuery)符合AMD規範,更多的庫並不符合。這樣的模塊在用require()加載以前,要先用require.config()方法,定義它們的一些特徵。
    // main.js中配置
    (function () {
     //配置
     require.config({
     //基本路徑
     baseUrl: "js/",
     //模塊標識名與模塊路徑映射
     paths: {
       //第三方庫做爲模塊
       'jquery' : './libs/jquery-1.10.1',
       'angular' : './libs/angular',
       //自定義模塊
       "alerter": "./modules/alerter",
       "dataService": "./modules/dataService"
     },
     /*
      配置不兼容AMD的模塊
      exports : 指定與相對應的模塊名對應的模塊對象
      */
     shim: {
       'angular' : {
         exports : 'angular'
       }
     }
     })
     //引入使用模塊
     require( ['alerter', 'angular'], function(alerter, angular) {
       alerter.showMsg()
       console.log(angular);
     })
    })()

CMD : 瀏覽器端

  • CMD規範:https://github.com/seajs/seajs/issues/242
  • CMD規範專門用於瀏覽器端,模塊的加載是異步的,模塊使用時纔會加載執行。
  • CMD規範整合了CommonJS和AMD規範的特色。
  • 在 Sea.js 中,全部 JavaScript 模塊都遵循 CMD模塊定義規範
  • 基本語法
    • 定義暴露模塊:es6

      // 沒有依賴的模塊
      define(function(require, module, exports){
         let value = 'xxx';
         //經過require引入依賴模塊
         //經過module.exports/exports來暴露模塊
         exports.xxx = value
         module.exports = value
      })
      // 有依賴的模塊
      define(function(require, exports, module){
         //引入依賴模塊(同步)
         var module2 = require('./module2')
         //引入依賴模塊(異步)
         require.async('./module3', function (m3) {
             ......
         })
         //暴露模塊
         exports.xxx = value
      })
    • 使用模塊seajs.use(['模塊1', '模塊2'])github

sea.js簡單使用教程

  1. 下載sea.js, 並引入
    • 官網: http://seajs.org/
    • github : https://github.com/seajs/seajs
    • 將sea.js導入項目: js/libs/sea.js
    • 如何定義導出模塊 :
    define()
    exports
    module.exports
    • 如何依賴模塊:require()
    • 如何使用模塊: seajs.use()
  2. 建立項目結構
|-js
  |-libs
    |-sea.js
  |-modules
    |-module1.js
    |-module2.js
    |-module3.js
    |-module4.js
    |-main.js
|-index.html
  1. 定義sea.js的模塊代碼
    • module1.js
    define(function (require, exports, module) {
      //內部變量數據
      var data = 'this is module1'
      //內部函數
      function show() {
        console.log('module1 show() ' + data)
      }
      //向外暴露
      exports.show = show
    })
    • module2.js
    define(function (require, exports, module) {
      module.exports = {
        msg: 'I Will Back'
      }
    })
    • module3.js
    define(function (require, exports, module) {
     const API_KEY = 'abc123'
     exports.API_KEY = API_KEY
    })
    • module4.js
    define(function (require, exports, module) {
     //引入依賴模塊(同步)
     var module2 = require('./module2');
     function show() {
       console.log('module4 show() ' + module2.msg)
     }
     exports.show = show
     //引入依賴模塊(異步)
     require.async('./module3', function (m3) {
       console.log('異步引入依賴模塊3  ' + m3.API_KEY)
     })
    })
    • main.js : 主(入口)模塊
    define(function (require) {
      var m1 = require('./module1')
      var m4 = require('./module4')
      m1.show()
      m4.show()
    })
  2. index.html:
<script type="text/javascript" src="js/libs/sea.js"></script>
<script type="text/javascript">
  seajs.use('./js/modules/main')
</script>

ES6模塊化

  • 模塊化的規範:CommonJS和AMD兩種。前者用於服務器,後者用於瀏覽器。
  • 而ES6 中提供了簡單的模塊系統,徹底能夠取代現有的CommonJS和AMD規範,成爲瀏覽器和服務器通用的模塊解決方案。
  • ES6 模塊的設計思想,是儘可能的靜態化,使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。CommonJS 和 AMD 模塊,都只能在運行時肯定這些東西。

基本用法

  • es6 中新增了兩個命令 export 和 import ;
    • export 命令用於規定模塊的對外接口;
    • import 命令用於輸入其餘模塊提供的功能。
    • 一個模塊就是一個獨立的文件。該文件內部的全部變量,外部沒法獲取。
    • 若是你但願外部可以讀取模塊內部的某個變量,就必須使用export關鍵字輸出該變量。
    • 下面是一個JS文件,裏面使用export命令輸出變量。
    // math.js
    export const add = function (a, b) {
        return a + b
    }
    export const subtract = function (a, b) {
        return a - b
    }
    • 使用export命令定義了模塊的對外接口之後,其餘JS文件就能夠經過import命令加載這個模塊(文件)。
    // main.js
    import { add, subtract } from './math.js'
    add(1, 2)
    substract(3, 2)
  • 定義暴露模塊 : export
    • 暴露一個對象:
      • 默認暴露,暴露任意數據類型,暴露什麼數據類型,接收什麼數據類型
      export default 對象
    • 暴露多個:
      • 常規暴露,暴露的本質是對象,接收的時候只能以對象的解構賦值的方式來接收值
      export var xxx = value1
      export let yyy = value2
      // 暴露一個對象
      var xxx = value1
      let yyy = value2
      export {xxx, yyy}
  • 引入使用模塊 : import
    • default模塊:
    import xxx  from '模塊路徑/模塊名'
    • 其它模塊
    import {xxx, yyy} from '模塊路徑/模塊名'
    import * as module1 from '模塊路徑/模塊名'

export 詳細用法

  • export不止能夠導出函數,還能夠導出,對象、類、字符串等等;express

  • 暴露多個:
    1. 分別暴露
    export const obj = {test1: ''}
    export const test = ''
    export class Test {
       constuctor() {
       }
    }
    // 或者,直接在暴露的地方定義導出函數或者變量
    export let foo = ()=>{console.log('fnFoo');return "foo"},bar="stringBar"
    1. 一塊兒暴露,推薦使用這種寫法,這樣能夠寫在腳本尾部,一眼就看清楚輸出了哪些變量。
    let a=1
    let b=2
    let c=3
    export { a,b,c }
    1. 還能夠經過as改變輸出名稱
    // test.js
    let a = 1
    let b = 2
    let c = 3
    export {
        a as test,
        b,
        c
    };
    import { test, b, c } from './test.js' // 改變命名後只能寫 as 後的命名
    1. 經過通配符暴露其餘引入的模塊
    // test.js
    let a = 1
    let b = 2
    let c = 3
    export {
        a as test,
        b,
        c
    };
    // lib.js引入test.js的內容
    export * from './test.js'
    // 引入
    import {test,b,c} from './lib.js'
  • 暴露一個對象,默認暴露
    • export default指定默認輸出,import無需知道變量名就能夠直接使用
    // test.js
    export default function () {
        console.log('hello world')
    }
    //引入
    import say from './test.js' // 這裏能夠指定任意變量名
    say() // hello world
    • 經常使用的模塊
    import $ from 'jQuery'   // 加載jQuery 庫
    import _ from 'lodash'   // 加載 lodash
    import moment from 'moment' // 加載 moment

import詳細用法

  • import 爲加載模塊的命令,基礎使用方式和以前同樣
// main.js
import { add, subtract } from './test'

// 對於export default 導出的
import say from './test'
  • 經過 as 命令修改導入的變量名
import {add as sum, subtract} from './test'
sum (1, 2)
  • 加載模塊的所有,除了指定輸出變量名或者 export.default 定義的導入, 還能夠經過 * 號加載模塊的所有。
// math.js
export const add = function (a, b) {
    return a + b
}
export const subtract = function (a, b) {
    return a - b
}

//引入
import * as math from './test.js'
math.add(1, 2)
math.subtract(1, 2)

ES6-Babel-Browserify使用教程

  • 問題: 全部瀏覽器還不能直接識別ES6模塊化的語法
  • 解決:
    • 使用Babel將ES6--->ES5(使用了CommonJS) ----瀏覽器還不能直接執行;
    • 使用Browserify--->打包處理js----瀏覽器能夠運行
  1. 定義package.json文件
{
  "name" : "es6-babel-browserify",
  "version" : "1.0.0"
}
  1. 安裝babel-cli, babel-preset-es2015和browserify
npm install babel-cli browserify -g
npm install babel-preset-es2015 --save-dev
  1. 定義.babelrc文件,這是一個babel的設置文件
{
   "presets": ["es2015"]
 }
  1. 編碼
// js/src/module1.js
export function foo() {
     console.log('module1 foo()');
};
export let bar = function () {
     console.log('module1 bar()');
};
export const DATA_ARR = [1, 3, 5, 1];

// js/src/module2.js
let data = 'module2 data'; 
function fun1() {
    console.log('module2 fun1() ' + data);
};
function fun2() {
    console.log('module2 fun2() ' + data);
};
export {fun1, fun2};

// js/src/module3.js
export default {
  name: 'Tom',
  setName: function (name) {
    this.name = name
  }
}

// js/src/app.js
import {foo, bar} from './module1'
import {DATA_ARR} from './module1'
import {fun1, fun2} from './module2'
import person from './module3'
import $ from 'jquery'
//引入完畢
$('body').css('background', 'red')
foo()
bar()
console.log(DATA_ARR);
fun1()
fun2()
person.setName('JACK')
console.log(person.name);
  1. 編譯
  • 使用Babel將ES6編譯爲ES5代碼(但包含CommonJS語法) : babel js/src -d js/lib
  • 使用Browserify編譯js : browserify js/lib/app.js -o js/lib/bundle.js
  1. 頁面中引入測試
<script type="text/javascript" src="js/lib/bundle.js"></script>
  1. 引入第三方模塊(jQuery)
    • 1). 下載jQuery模塊:
    npm install jquery@1 --save
    • 2). 在app.js中引入並使用
    import $ from 'jquery'
    $('body').css('background', 'red')

總結

模塊化方案 優勢 缺點
commonJS 複用性強;
使用簡單;
實現簡單;
有很多能夠拿來即用的模塊,生態不錯;
同步加載不適合瀏覽器,瀏覽器的請求都是異步加載;
不能並行加載多個模塊。
AMD 異步加載適合瀏覽器 可並行加載多個模塊;
模塊定義方式不優雅,不符合標準模塊化
ES6 可靜態分析,提早編譯 面向將來的標準;
瀏覽器原生兼容性差,因此通常都編譯成ES5;
目前能夠拿來即用的模塊少,生態差

AMD和CMD區別:

  • 權威參考:https://github.com/seajs/seajs/issues/277
  • 對於依賴的模塊,AMD 是提早執行,CMD 是延遲執行。
    • 不過 RequireJS 從 2.0 開始,也改爲能夠延遲執行(根據寫法不一樣,處理方式不一樣)。CMD 推崇 as lazy as possible.
  • 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 的做者默認是最喜歡上面的寫法,也是官方文檔裏默認的模塊定義寫法。
  • AMD 的 API 默認是一個當多個用,CMD 的 API 嚴格區分,推崇職責單一。好比 AMD 裏,require 分全局 require 和局部 require,都叫 require。CMD 裏,沒有全局 require,而是根據模塊系統的完備性,提供 seajs.use 來實現模塊系統的加載啓動。CMD 裏,每一個 API 都簡單純粹。
  • 還有一些細節差別,具體看這個規範的定義就好,就很少說了。

參考:使用 AMD、CommonJS 及 ES Harmony 編寫模塊化的 JavaScripth

相關文章
相關標籤/搜索