js 模塊化進程

JS模塊化進程

1、上古時代

第一步javascript

只要把不一樣的函數(以及記錄狀態的變量)簡單地放在一塊兒,就算是一個模塊。Global全局污染,容易命名衝突html

function foo(){
    //...
}
function bar(){
    //...
}

第二步 Namespacejava

雖然減小了Global變量的數量,可是這樣的寫法會暴露全部模塊成員,內部狀態能夠被外部改寫。jquery

var name = {
    foo: function(){
        
    },
    bar: function(){
        
    }
}

// 使用
name.foo();

第三步 IIFE模式webpack

閉包模式,經過當即執行函數寫法,能夠達到不暴露私有變量的目的。且外部沒法訪問內部的值。es6

var module = (function(){
    var _private = 'sss';
    var foo = function(){
        
    }   
    return {
        foo: foo
    }
})();

module.foo();
module._private; // undefined

引入依賴web

// 改進一些
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();
// 依賴其餘
var module1 = (function (mod){

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

  return mod;

})(module1);
// 其餘擴展
var MODULE = (function (my) {
  // add capabilities...
  return my;
}(MODULE || {}));
// 能夠傳入空對象
var MODULE = (function (my) {
  var old_moduleMethod = my.moduleMethod;

  my.moduleMethod = function () {
    // 方法重載
    // 可經過 old_moduleMethod 調用之前的方法...
  };

  return my;
}(MODULE));
// 有時咱們要求在擴展時調用之前已被定義的方法,這也有可能被用於覆蓋已有的方法。這時,對模塊的定義順序是有要求的。
  • 這就是模塊模式,也就是現代模塊實現的基石

2、遠古時代

Script Loader
經過script標籤引入一堆的依賴,按序執行。一樣難以維護,依賴過多。編程

body
    script(src="zepto.js")
    script(src="jhash.js")
    script(src="fastClick.js")
    script(src="iScroll.js")
    script(src="underscore.js")
    script(src="handlebar.js")
    script(src="datacenter.js")
    script(src="deferred.js")
    script(src="util/wxbridge.js")
    script(src="util/login.js")
    script(src="util/base.js")
    script(src="util/city.js")
    script(src="util/date.js")
    script(src="util/cookie.js")
    script(src="app.js")
這樣的寫法有很大的缺點。首先,加載的時候,瀏覽器會中止網頁渲染,加載文件越多,網頁失去響應的時間就會越長;其次,因爲js文件之間存在依賴關係,所以必須嚴格保證加載順序(好比上例的1.js要在2.js的前面),依賴性最大的模塊必定要放到最後加載,當依賴關係很複雜的時候,代碼的編寫和維護都會變得困難。

3、中古時代

CommonJS 征服世界的第一步是跳出瀏覽器。segmentfault

CommonJS規範的提出,主要時爲了彌補js沒有標準的缺陷,使得經過CommonJS Api編寫的應用能夠在不一樣的宿主環境中執行。瀏覽器

由於老實說,在瀏覽器環境下,沒有模塊也不是特別大的問題,畢竟網頁程序的複雜性有限;可是在服務器端,必定要有模塊,與操做系統和其餘應用程序互動,不然根本無法編程。所以Node就在Commonjs的基礎上應運而生了。

NodeJSCommonJS規範的實現,webpack 也是以CommonJS的形式來書寫】

3、近代奇兵

AMD/CMD 瀏覽器環境模塊化方案

AMD(Asynchronous Module Definition:異步模塊定義)RequireJS 在推廣過程當中對模塊定義的規範化產出。

CMD(Common Module Definition:公共模塊定義)SeaJS 在推廣過程當中對模塊定義的規範化產出。

1. AMD

有了服務器端模塊之後,很天然地,你們就想要客戶端模塊。並且最好二者可以兼容,一個模塊不用修改,在服務器和瀏覽器均可以運行。

可是,因爲一個重大的侷限,使得CommonJS規範不適用於瀏覽器環境。下面的代碼,若是在瀏覽器中運行,會有一個很大的問題,你能看出來嗎?

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

第二行math.add(2, 3),在第一行require('math')以後運行,所以必須等math.js加載完成。也就是說,若是加載時間很長,整個應用就會停在那裏等。

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

所以,在瀏覽器就須要"異步加載"(asynchronous)。這就是AMD規範誕生的背景。

目前,主要有兩個Javascript庫實現了AMD規範:require.jscurl.js


Require.JS

Require.JS 的基本功能"模塊化加載"。

實例1

// math1.js
/*
    語法結構:
    1. define({函數方法})
*/
// 一個沒有依賴性的模塊能夠直接定義對象

define({
  name: '測試',
  add: function(num1, num2) {
    return num1+num2;
  }
})
// test1.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <script src="./lib/require.js" charset="utf-8"></script>
    <script type="text/javascript">
      require(['math1'], function(math) {
        console.log(math.name);
        console.log(math.add(1,3));
      })
    </script>
  </body>
</html>

實例二

// math2.js
/*
    語法結構:
    2. define([引入其餘模塊地址],回調函數(引入模塊別名));
    別名能夠在函數裏面去調用其餘模塊提供的方法
*/
// 一個返回對象的匿名模塊
define(['math1'], function(math) {
  var sub = function(num1,num2) {
    return num1 - num2;
  }

  return {
    add: math.add,
    sub: sub
  }
})
// test2.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <script src="./lib/require.js" charset="utf-8"></script>
    <script type="text/javascript">
      require(['math2'], function(math) {
        console.log(math.sub(4,2));
      })
    </script>
  </body>
</html>

實例三

// math3.js
// 定義一個命名模塊

define('mymath', ['math2'], function(math) {
  var multiplication = function(num1, num2) {
    return num1 * num2;
  }
  return {
    add: math.add,
    sub: math.sub,
    mult: multiplication
  }
})
// test3.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <script src="./lib/require.js" charset="utf-8"></script>
    <script type="text/javascript">
    //須要配置一下引入的模塊的地址
      require.config({
        paths:{
          'mymath': 'math3'
        }
      });
      require(['mymath'], function(math){
        console.log(math.mult(3,5));
      })
    </script>
  </body>
</html>

下圖有一些注意事項,可是截圖不是本實例的截圖

實例四

// math4.js
// 一個使用了簡單CommonJS轉換的模塊定義
define(function(require,exports,module){
    // 引入其餘模塊
    var math = require('js/1_math');
    console.log(math);

    // 導出(暴露方法:2種方式)
    // 第一種
    // exports.a = math.add;
    // 第二種
    module.exports = {
        a : math.add
    }
});
// test4.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    
</body>
<script type="text/javascript" src="js/require.js"></script>
<script type="text/javascript">
require(['js/4_math'],function(mytool){
    console.log(mytool.a(11,22));//33
});
</script>
</html>


一個小demo
  • 目錄結構

圖片描述

  • 代碼
// index.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
      <h1>My Sample Project</h1>
      <script src="./script/require.js" data-main="./script/main" charset="utf-8"></script>
  </body>

</html>
// main.js
require.config({
  baseUrl: 'script',
  paths: {
    math: './lib/math1',
    utils: './utils/utils',
    jquery: './lib/jquery.min'
  }
});

require(['math','utils','jquery'], function(math, utils, $) {
  $(function(){
    console.log(math.add(1,2));
    console.log(utils.utils.sub(4,2));
    alert('在加載完math、utils、jquery以後,我執行了');
  })
})
//math.js 
define({
  add:function(num1, num2) {
    return num1+num2;
  }
});
// utils.js
define(function(require, exports, module) {
  var utils = {
    sub:function(num1, num2) {
      return num1-num2;
    }
  };

  module.exports = {
    utils: utils
  }
})

圖片描述


2.CMD

SeaJS 是一個適用於 Web 瀏覽器端的模塊加載器。這裏就先不寫了~

你們能夠參考文檔學習

SeaJs中文


4、展望將來

面向將來的ES6模塊標準

2015年6月,ECMAScript2015也就是ES6發佈了,JavaScript終於在語言標準的層面上,實現了模塊功能,使得在編譯時就能肯定模塊的依賴關係,以及其輸入和輸出的變量,不像 CommonJS、AMD之類的須要在運行時才能肯定(例如FIS這樣的工具只能預處理依賴關係,本質上仍是運行時解析),成爲瀏覽器和服務器通用的模塊解決方案。

更多關於ES6 Modules的資料,能夠看一下ES6阮一峯


感謝您的支持!!
相關文章
相關標籤/搜索