JavaScript模塊化編程探索

隨着網站逐漸變成"互聯網應用程序",嵌入網頁的Javascript代碼愈來愈龐大,愈來愈複雜。網頁愈來愈像桌面程序,須要一個團隊分工協做、進度管理、單元測試等等......開發者不得不使用軟件工程的方法,管理網頁的業務邏輯。
Javascript模塊化編程,已經成爲一個迫切的需求。javascript

從CommonJS提及

CommonJS團隊定義了module格式來解決JavaScript做用域問題,這樣確保了每個module都在本身的命名空間下執行。html

根據CommonJS的規範,每一個文件就是一個模塊,有本身的做用域。在一個文件裏面定義的變量、函數、類,都是私有的,對其餘文件不可見。前端

CommonJS規範規定,每一個模塊內部,module變量表明當前模塊。這個變量是一個對象,它的exports屬性(即module.exports)是對外的接口。加載某個模塊,實際上是加載該模塊的module.exports屬性。java

CommonJS給出2個工具來實現模塊之間的依賴:jquery

  1. require() 用於在當前做用域引入已有的模塊git

  2. module object 用於從當前做用域導出一些東東github

那就先搞一個Hello world的小栗子來試下吧!編程

編寫簡單的JavaScript模塊

新建一個項目文件夾吧,雖然項目很小。。。起名commonjs,在裏邊新建2個JavaScript文件,分別命名爲world.js和salute.js,代碼以下:數組

// salute.js 打招呼
var MySalute = "Hello";
module.exports = MySalute;

/*注意上下是分別寫在2個文件js文件裏哦*/

// world.js
var MySalute = require("./salute");
var Result = MySalute + " world!";
console.log(Result);

而後無知的我有新建了一個demo.html,(想要在瀏覽器裏打開看看是什麼樣子)內容以下:瀏覽器

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script type="text/javascript" src="world.js"></script>
</head>
<body>
    
</body>
</html>

結果在瀏覽器中打開,查看控制檯大失所望,報了一個錯誤
world.js:2 Uncaught ReferenceError: require is not defined
發現瀏覽器不兼容CommonJS的根本緣由,在於缺乏四個Node.js環境的變量:

  • module

  • exports

  • require

  • global
    只要可以提供這四個變量,瀏覽器就能加載 CommonJS 模塊,問題是能夠解決的,可是好像並不怎麼好玩,有興趣的朋友能夠去阮老師博客裏逛逛啊傳送門

如今我決定要去Node.js裏邊玩一下了

Node.js環境裏玩一把

打開命令行工具cd到項目目錄:
圖片描述

結果順利打印出了Hello world!果真頗有搞頭啊,呵呵.

那麼寫到爲止簡單實現了模塊之間的引用,到底這個CommonJS規範下還能夠作些什麼呢?到CommonJS官網看了一下,發現以下內容:

JavaScript是強調大面向對象語言,並且帶有最快的解釋器,並且以前的JavaScript定義的APIs僅僅用於構建瀏覽器端的應用,然而呢有了這個CommonJS就能夠構建更寬範圍的應用了,具體點就是能夠用JavaScript來寫:

  • 服務器端應用Server-side JavaScript applications

  • 命令行工具Command line tools

  • 基於GUI的桌面應用Desktop GUI-based applications

  • Hybrid applications (Titanium, Adobe AIR)(這個是什麼?雖然我如今還不知道,但感受它很牛逼)

AMD是用來幹甚麼的?

AMD (Asynchronous Module Definition)膚淺的理解異步模塊定義。。。
一開始你們可能覺得CommonJS的天性就是同步,它的模塊系統並不適用於瀏覽器,而這個AMD就是指定了一個標準,證實給別人看模塊化的JavaScript能夠異步加載依賴,解決同步加載出現的問題。

定義

define函數是AMD定義模塊的方法:
define(id?: String, dependencies?: String[], factory: Function|Object);
id:指定模塊名字
dependencies:指明依賴
factory:是定義模塊的,能夠是function或object,若是是function那麼函數的返回值就是module 導出的值。

examples

define('myModule', ['jquery'], function($) {
    // $ is the export of the jquery module.
    $('body').text('hello world');
});
// and use it
require(['myModule'], function(myModule) {});

RequireJS

隨着網站功能逐漸豐富,網頁中的js也變得愈來愈複雜和臃腫,原有經過script標籤來導入一個個的js文件這種方式已經不能知足如今互聯網開發模式,咱們須要團隊協做、模塊複用、單元測試等等一系列複雜的需求。
RequireJS是一個很是小巧的JavaScript模塊載入框架,是AMD規範最好的實現者之一。最新版本的RequireJS壓縮後只有14K,堪稱很是輕量。它還同時能夠和其餘的框架協同工做,使用RequireJS必將使您的前端代碼質量得以提高。此段出處

對比js模塊的同步加載和異步加載

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

瀏覽器同步加載js模塊

新建a.js

(function(){
    function test(){
        alert("it works");
    }

    test();
})()

新建demo1.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script type="text/javascript" src="a.js"></script>
</head>
<body>
    <span>body</span>
</body>
</html>

在瀏覽器中運行demo1.html,alert執行的時候,html內容是一片空白的,即<span>body</span>並未被顯示,當點擊肯定後,纔出現,這就是JS阻塞瀏覽器渲染致使的結果。

RequireJS異步加載js模塊

把a.js改寫以下:

define(function(){
    function test(){
      alert("it works");
    }

    test();
})

到githug下載require.jsRequireJS download修改demo1.html以下:

!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script type="text/javascript" src="require.js"></script>
    <script type="text/javascript">
        require(["a"]);
    </script>
</head>
<body>
    <span>body</span>
</body>
</html>

瀏覽器提示了"it works",說明運行正確,可是有一點不同,此次瀏覽器並非一片空白,body已經出如今頁面中,目前爲止能夠知道requirejs具備以下優勢:

  1. 防止js加載阻塞頁面渲染

  2. 管理模塊之間的依賴性,便於代碼的編寫和維護,使得代碼更加優雅。

CMD規範

CMD(Common Module Definition) 模塊定義規範。該規範明確了模塊的基本書寫格式和基本交互規則。在 CMD 規範中,一個模塊就是一個文件。代碼的書寫格式以下:

define(factory);

define 是一個全局函數,用來定義模塊。define 接受 factory 參數,factory 能夠是一個函數,也能夠是一個對象或字符串。

factory 爲對象、字符串時,表示模塊的接口就是該對象、字符串。好比能夠以下定義一個 JSON 數據模塊:

define({ "foo": "bar" });

require 是一個方法,接受 模塊標識 做爲惟一參數,用來獲取其餘模塊提供的接口。

define(function(require, exports) {

  // 獲取模塊 a 的接口
  var a = require('./a');

  // 調用模塊 a 的方法
  a.doSomething();

});

require.async 方法用來在模塊內部異步加載模塊,並在加載完成後執行指定回調。callback 參數可選。

define(function(require, exports, module) {

  // 異步加載一個模塊,在加載完成時,執行回調
  require.async('./b', function(b) {
    b.doSomething();
  });

  // 異步加載多個模塊,在加載完成時,執行回調
  require.async(['./c', './d'], function(c, d) {
    c.doSomething();
    d.doSomething();
  });

});

require 是同步往下執行,require.async 則是異步回調執行。require.async 通常用來加載可延遲異步加載的模塊。
更詳細內容請查看CMD 模塊定義規範

sea.js

RequireJS 和 Sea.js 都是模塊加載器,倡導模塊化開發理念,核心價值是讓 JavaScript 的模塊化開發變得簡單天然。
在 SeaJS 中,全部 JavaScript 文件都應該用模塊的形式來書寫,而且一個文件只包含一個模塊。
使用全局函數 define 來定義模塊:

define(id?, dependencies?, factory);

id
當前模塊的惟一標識。該參數可選。若是沒有指定,默認爲模塊所在文件的訪問路徑。若是指定的話, 必須是頂級或絕對標識(不能是相對標識)。
dependencies
當前模塊所依賴的模塊,是一個由模塊標識組成的數組。該參數可選。若是沒有指定,模塊加載器會從 factory.toString() 中解析出該數組。
factory
模塊的工廠函數。模塊初始化時,會調用且僅調用一次該工廠函數。factory 能夠是函數, 也能夠是對象、字符串等任意值,這時 module.exports 會直接設置爲 factory 值。
factory 函數在調用時,會始終傳入三個參數: require、exports 和 module, 這三個參數在全部模塊代碼裏可用。

define(function(require, exports, module) {

  // The module code goes here
  
});

more

相關文章
相關標籤/搜索