談談前端模塊化的演變歷程

前言

Javascript不是一種模塊化編程語言,它不支持"類"(class),更遑論"模塊"(module)了,隨着前端發展對 模塊需求愈來愈大,模塊也是經歷了從最初的簡單模塊寫法到AMD和CMD規範的出現,再到ES6發佈,目前已經能夠 很方便的在Javascript中使用"類"和"模塊"了。javascript

1、之前的寫法

一、原始寫法html

function m1(){
    //...
  }

  function m2(){
    //...
  }

缺點 : "污染"了全局變量,沒法保證不與其餘模塊發生變量名衝突,並且模塊成員之間看不出直接關係。前端

二、對象寫法 爲了解決上面缺點,把模塊寫成一個對象,全部的模塊成員都放到這個對象裏面java

var module1 = new Object({

    _count : 0,

    m1 : function (){
      //...
    },

    m2 : function (){
      //...
    }

  });

使用node

module1.m1();

存在缺點: 可是,這樣的寫法會暴露全部模塊成員,內部狀態能夠被外部改寫。好比,外部代碼能夠直接改變內部計數器的值。jquery

module1._count = 5;

三、當即執行函數寫法webpack

當即執行函數(IIFE),能夠達到不暴露私有成員的目的。程序員

var module1 = (function(){

    var _count = 0;

    var m1 = function(){
      //...
    };

    var m2 = function(){
      //...
    };

    return {
      m1 : m1,
      m2 : m2
    };

  })();

上面的寫法,外部代碼沒法讀取內部的_count變量。es6

console.info(module1._count); //undefined

四、放大模式 若是一個模塊很大,必須分紅幾個部分,或者一個模塊須要繼承另外一個模塊web

  var module1 = (function (mod){

    mod.m3 = function () {
      //...
    };

    return mod;

  })(module1);

上面的代碼爲module1模塊添加了一個新方法m3(),而後返回新的module1模塊。

五、寬放大模式 在瀏覽器環境中,模塊的各個部分一般都是從網上獲取的,有時沒法知道哪一個部分會先加載。若是採用上一節的寫法,第一個執行的部分有可能加載一個不存在空對象,這時就要採用"寬放大模式"。

var module1 = ( function (mod){

    //...

    return mod;

  })(window.module1 || {});

與"放大模式"相比,"寬放大模式"就是"當即執行函數"的參數能夠是空對象。

六、輸入全局變量 獨立性是模塊的重要特色,模塊內部最好不與程序的其餘部分直接交互。 爲了在模塊內部調用全局變量,必須顯式地將其餘變量輸入模塊

var module1 = (function ($, YAHOO) {

    //...

  })(jQuery, YAHOO);

上面的module1模塊須要使用jQuery庫和YUI庫,就把這兩個庫(實際上是兩個模塊)看成參數輸入module1 好處: 這樣作除了保證模塊的獨立性,還使得模塊之間的依賴關係變得明顯

2、CommonJS、AMD和CMD的出現

談到AMD和CMD,不得不說下CommonJS。2009年,美國程序員Ryan Dahl創造了node.js項目,將javascript語言用於服務器端編程, 因爲瀏覽器端網頁還比較簡單 ,對於模塊不是特別依賴,但在服務器端由於要與操做系統和其餘應用程序互動,CommonJS 就在這樣的背景下誕生了。

引入第三方模塊並調用方法

var math = require('math');
math.add(2,3); // 5

這裏有一個CommonJS模塊使用的例子

//模塊定義 myModule.js
var name = 'Byron';
function printName(){
    console.log(name);
}
function printFullName(firstName){
    console.log(firstName + name);
}
module.exports = {
    printName: printName,
    printFullName: printFullName
}
//加載模塊
var myModule = require('./myModule.js');
myModule.printName();

一、AMD規範

想象一下若是把這段代碼放到瀏覽器端 ,math.add(2, 3),在第一行require('math')以後運行,所以必須等math.js加載完成。也就是說,若是加載時間很長,整個應用就會停在那裏等

var math = require('math');
  math.add(2, 3);

這對服務器端不是一個問題,由於全部的模塊都存放在本地硬盤,能夠同步加載完成,等待時間就是硬盤的讀取時間。可是,對於瀏覽器,這倒是一個大問題,由於模塊都放在服務器端,等待時間取決於網速的快慢,可能要等很長時間,瀏覽器處於"假死"狀態

所以,瀏覽器端的模塊,不能採用"同步加載"(synchronous),只能採用"異步加載"(asynchronous)

AMD也採用require()語句加載模塊,可是不一樣於CommonJS,它要求兩個參數

require([module], callback);

上面代碼改寫成AMD形式就是這樣

require(['math'], function (math) {

    math.add(2, 3);

  });

目前實現AMD規範的有RequireJS和curl.js

RequireJS模塊例子:

// 定義模塊 myModule.js
define('myModule', ['dependency'], function(){
    var name = 'Byron';
    function printName(){
        console.log(name);
    }
    return {
        printName: printName
    };
});

// 加載模塊
require(['myModule'], function (my){
  my.printName();
});

二、CMD規範 CMD: Common Module Definition通用模塊定義, 由國內發展出來, SeaJS是其典型表明, 即SeaJS是經過瀏覽器對CMD的具體實現

SeaJS模塊例子:

// 定義模塊  myModule.js
define(function(require, exports, module) {
  var $ = require('jquery.js');
  var foo = require('foo');
  var out = foo.bar();
  $('div').addClass('active');
  module.exports = out;
});

// 加載模塊
seajs.use(['myModule.js'], function(my){

});

三、CommonJS、AMD和CMD區別

  • CommonJS是同步的, 主要用於服務器
  • AMD和CMD是異步的, 二者的模塊定義和加載機制稍有不一樣, 主要用於瀏覽器
  • AMD推崇依賴前置,在定義模塊的時候就要聲明其依賴的模塊,CMD`推崇就近依賴,只有在用到某個模塊的時候再去require 兩個都是定義的全局define函數來定義模塊, define接收函數function(require, exports, module)保持一致
  • CMD是懶加載, 僅在require時纔會加載模塊; - AMD是預加載, 在定義模塊時就提早加載好全部依賴
  • CMD保留了CommonJS風格

3、ES6的使用

ES6在語言標準的層面上, 實現了模塊功能, 並且實現得至關簡單, 徹底能夠取代CommonJS和AMD規範, 是瀏覽器和服務器通用的模塊解決方案

ES6模塊例子:

//模塊定義 myModule.js
const name = 'Byron';
function printName(){
    console.log(name);
}
function printFullName(firstName){
    console.log(firstName + name);
}
const myModule = {
    printName: printName,
    printFullName: printFullName
};
export myModule;

//加載模塊
import myModule, { printFullName } from './myModule.js';
myModule.printName();
printFullName('Michael');

4、最後再說說browserify和webpack

說到 browserify / webpack ,那還要說到 seajs / requirejs 。這四個都是JS模塊化的方案。其中seajs / require 是一種類型,browserify / webpack 是另外一種類型。

一、seajs / require : 是一種在線"編譯"模塊的方案,至關於在頁面上加載一個 CMD/AMD 解釋器。這樣瀏覽器就認識了 define、exports、module 這些東西。也就實現了模塊化。

二、browserify / webpack : 是一個預編譯模塊的方案,相比於上面 ,這個方案更加智能。沒用過browserify,這裏以webpack爲例。首先,它是預編譯的,不須要在瀏覽器中加載解釋器。另外,你在本地直接寫JS,不論是 AMD / CMD / ES6 風格的模塊化,它都能認識,而且編譯成瀏覽器認識的JS。

參考閱讀:

http://www.ruanyifeng.com/blog/2012/10/javascript_module.html
https://www.jianshu.com/p/5226bd9644b6
http://es6.ruanyifeng.com/#docs/module

原文出處:https://www.cnblogs.com/fozero/p/10328556.html

相關文章
相關標籤/搜索