JavaScript模塊化 --- Commonjs、AMD、CMD、es6 modules

今天面試問到模塊化的發展,下面是我找來解釋的比較明白的博客:html

隨着前端js代碼複雜度的提升,JavaScript模塊化這個概念便被提出來,前端社區也不斷地實現前端模塊化,直到es6對其進行了規範,下面就介紹JavaScript模塊化。 前端

  這篇文章仍是但願能給你們一個比較好的思路,即JavaScript模塊化是如何一步一步地發展起來的,而且也會主要對這些模塊化方式作一個簡單的比較。vue

  

第一階段:無模塊化

  JavaScript最初的做用僅僅是驗證表單,後來會添加一些動畫,可是這些js代碼不少在一個文件中就能夠完成了,因此,咱們只須要在html文件中添加一個script標籤。 node

  後來,隨着前端複雜度提升,爲了可以提升項目代碼的可讀性、可擴展性等,咱們的js文件逐漸多了起來,再也不是一個js文件就能夠解決的了,而是把每個js文件當作一個模塊。那麼,這時的js引入方式是怎樣的呢?大概是下面這樣:jquery

  <script src="jquery.js"></script>
  <script src="jquery_scroller.js"></script>
  <script src="main.js"></script>
  <script src="other1.js"></script>
  <script src="other2.js"></script>
  <script src="other3.js"></script>

  即簡單的將全部的js文件通通放在一塊兒。可是這些文件的順序還不能出錯,好比jquery須要先引入,才能引入jquery插件,才能在其餘的文件中使用jquery。 webpack

優勢ios

     相比於使用一個js文件,這種多個js文件實現最簡單的模塊化的思想是進步的。 es6

缺點: web

  • 污染全局做用域。 由於每個模塊都是暴露在全局的,簡單的使用,會致使全局變量命名衝突,固然,咱們也可使用命名空間的方式來解決。
  • 對於大型項目,各類js不少,開發人員必須手動解決模塊和代碼庫的依賴關係,後期維護成本較高。
  • 依賴關係不明顯,不利於維護。 好比main.js須要使用jquery,可是,從上面的文件中,咱們是看不出來的,若是jquery忘記了,那麼就會報錯。 

 

 

 

第二階段: CommonJS規範

  CommonJS就是一個JavaScript模塊化的規範,該規範最初是用在服務器端的node的,前端的webpack也是對CommonJS原生支持的。 面試

  根據這個規範,每個文件就是一個模塊,其內部定義的變量是屬於這個模塊的,不會對外暴露,也就是說不會污染全局變量。 

  CommonJS的核心思想就是經過 require 方法來同步加載所要依賴的其餘模塊,而後經過 exports 或者 module.exports 來導出須要暴露的接口。以下所示:

複製代碼
// a.js
var x = 5; var addX = function (value) { return value + x; }; module.exports.x = x; module.exports.addX = addX;
複製代碼

  這裏的a.js就是一個CommonJS規範的模塊了。 這裏的module就表明了這個模塊,module的exports屬性就是對外暴露的接口,能夠對外導出外部能夠訪問的變量,好比這裏的x和addX。 

 

exports 是對 module.exports 的引用。好比咱們能夠認爲在一個模塊的頂部有這句代碼:

   exports = module.exports

因此,咱們不能直接給exports賦值,好比number、function等。 

       注意:由於module.exports自己就是一個對象,因此,咱們在導出時可使用 module.exports = {foo: 'bar'} 也可使用 module.exports.foo = 'bar'。可是, exports 是 module.exports 的一個引用,或者理解爲exports是一個指針,exports指向module.exports,這樣,咱們就只能使用 exports.foo = 'bar' 的方式,而不能使用exports = {foo: 'bar'}這種方式,由於exports = {foo: 'bar'}這種方式的使用就會致使exports指向了別的對象,那麼這個模塊的輸出就會有問題了。

 

  而後咱們就能夠在其餘模塊中引入這個模塊使用了:

vara = require('./a.js');

console.log(example.x); // 5
console.log(example.addX(1)); // 6

  這裏的require就會獲取到a.js所暴露的module.exports變量,而後就可使用其暴露的x和addX了。 

 

優勢

  CommonJS規範在服務器端率先完成了JavaScript的模塊化,解決了依賴、全局變量污染的問題,這也是js運行在服務器端的必要條件。

 

缺點

  這篇文章咱們講的主要是瀏覽器端js的模塊化, 因爲 CommonJS 是同步加載模塊的,在服務器端,文件都是保存在硬盤上,因此同步加載沒有問題,可是對於瀏覽器端,須要將文件從服務器端請求過來,那麼同步加載就不適用了,因此,CommonJS是不適用於瀏覽器端的。 

 

 

 

第三階段: AMD規範

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

  AMD標準中,定義了下面兩個API:

  1. require([module], callback)
  2. define(id, [depends], callback)

  即經過define來定義一個模塊,而後使用require來加載一個模塊。 而且,require還支持CommonJS的模塊導出方式。 

定義alert模塊:

複製代碼
define(function () {
    var alertName = function (str) {
      alert("I am " + str);
    }
    var alertAge = function (num) {
      alert("I am " + num + " years old");
    }
    return {
      alertName: alertName,
      alertAge: alertAge
    };
  });
複製代碼

 

引入模塊:

require(['alert'], function (alert) {
  alert.alertName('JohnZhu');
  alert.alertAge(21);
});

 

可是,在使用require.js的時候,咱們必需要提早加載全部的依賴,而後纔可使用,而不是須要使用時再加載。

 

優勢

  1. 適合在瀏覽器環境中異步加載模塊。
  2. 能夠並行加載多個模塊。

缺點

         提升了開發成本,而且不能按需加載,而是必須提早加載全部的依賴。 

 

 

 

第四階段:CMD規範

  CMD規範是阿里的玉伯提出來的,實現js庫爲sea.js。 它和requirejs很是相似,即一個js文件就是一個模塊,可是CMD的加載方式更加優秀,是經過按需加載的方式,而不是必須在模塊開始就加載全部的依賴。以下:

efine(function(require, exports, module) {
  var $ = require('jquery');
  var Spinning = require('./spinning');
  exports.doSomething = ...
  module.exports = ...
})

 

優勢

  • 一樣實現了瀏覽器端的模塊化加載。
  • 能夠按需加載,依賴就近。 

缺點

  依賴SPM打包,模塊的加載邏輯偏重。 

 

 

其實,這時咱們就能夠看出AMD和CMD的區別了,前者是對於依賴的模塊提早執行,然後者是延遲執行。 前者推崇依賴前置,然後者推崇依賴就近,即只在須要用到某個模塊的時候再require。 以下:

複製代碼
// AMD
define(['./a', './b'], function(a, b) {  // 依賴必須一開始就寫好  
   a.doSomething()    
   // 此處略去 100 行    
   b.doSomething()    
   ...
});
// CMD
define(function(require, exports, module) {
   var a = require('./a')   
   a.doSomething()   
   // 此處略去 100 行   
   var b = require('./b') 
   // 依賴能夠就近書寫   
   b.doSomething()
   // ... 
});
複製代碼

 

 

 

 

第五階段: ES6模塊化

  以前的幾種模塊化方案都是前端社區本身實現的,只是獲得了你們的承認和普遍使用,而ES6的模塊化方案是真正的規範。 在ES6中,咱們可使用 import 關鍵字引入模塊,經過 exprot 關鍵字導出模塊,功能較之於前幾個方案更爲強大,也是咱們所推崇的,可是因爲ES6目前沒法在瀏覽器中執行,因此,咱們只能經過babel將不被支持的import編譯爲當前受到普遍支持的 require。 

  雖然目前import和require的區別不大,可是仍是推薦使用使用es6,由於將來es6一定是主流,對於代碼的遷移成本仍是很是容易的。  如:

複製代碼
  import store from '../store/index'
  import {mapState, mapMutations, mapActions} from 'vuex'
  import axios from '../assets/js/request'
  import util from '../utils/js/util.js'
  

  export default {
    created () {
      this.getClassify(); 

      this.RESET_VALUE();
      console.log('created' ,new Date().getTime());
      // 

    }
複製代碼
相關文章
相關標籤/搜索