1. 程式人生 > >WEB前端模組化基礎知識

WEB前端模組化基礎知識

前段模組化基礎

  • 概念

    前端模組化其實就是將一個完整的單一的功能整合起來形成單獨的一個功能元件,當需要用的的時候只需要載入這個元件,然後便可以通過該元件唯一的名稱去使用其中的內容。

  • 主流模組化框架

    • commonJS
    • AMD
    • CMD
    • UMD
    • ES6規範

commonJS

  • commonJS模組化

    • 定義模組:即一個單獨的檔案就是一個模組,切該檔案中的作用域獨立,當中的變數是無法被其他檔案引用的,如果要使用需要將其定義為global
    • 輸出模組:模組只有一個出口,即使用module.exports物件,將需要輸出的內容放入到該物件中;
    • 載入模組:通過require載入,例如:var module = require('./moduleFile.js');
      該module的值即對應檔案內部的module.exports物件, 然後就可以通過module名稱去引用模組中的變數和函數了;

    例如:

    // 定義模組 module.js
    var data = "hello commonJS !";
    
    function test1() {
        alert("hello test1 !");
    }
    
    function test2() {
        alert("hello test2 !");
    }
    
    // 輸出模組
    module.exports = {
        test1: test1,
        test2: test2
    }    
    
    // 載入模組
    var module
    = require('./module.js'); // 使用模組功能 module.test1(); // "hello test1 !" module.test2(); // "hello test2 !" 總共四個步驟: 定義 -> 輸出 -> 載入 -> 使用

    PS: commonJS是基於伺服器端開發的,由於require是同步載入,所以當在瀏覽器中是無法執行的,所以就有了下面的非同步模組的出現。

AMD(Asynchronous Module Definition),非同步模組定義

  • AMD是一套基於瀏覽器端模組化開發的規範,在進行頁面開發時需要用到該規範的庫函式,即:requireJS

  • requireJS解決了兩個問題

    • 多檔案依賴關係處理,被依賴的需要早一步被載入;
    • 載入js時候頁面會停止渲染,如果載入的檔案越多,頁面失去響應的時間就會越長;

    例如:

    // 模組定義 module.js
    define(
        ['dependModule', '...', ...], // 這個陣列表示該模組所依賴的模組名稱 
        function () {
            var data = "hello AMD !";
            function test1() {
                alert("hello test1 !);
            }
    
            function test2() {
                alert("hello test2 !);
            }
    
    
            return {
                test1: test1,
                test2: test2
            };
    });
    
    
    // 載入模組
    require(['module'], function (myModule) {
        // 載入之後的module模組將會以引數形式:myModule傳入到回撥函式中,供使用
        // 這裡是模組載入完成之後的回撥函式
        myModule.test1(); // "hello test1 !"
        myModule.test2(); // "hello test2 !"
    });
    
    // ---------- AMD語法:
    1. 模組定義:使用全域性函式`define(id, dependencies, factory);`
        1. id: 用來定義模組標識,非必選引數,如果沒提供該引數則會預設使用指令碼檔名(不帶副檔名的);
        2. dependencies:是表示該模組所依賴的模組名稱陣列;
        3. factory:如果是函式則為該模組初始化所執行的函式,如果是物件則為該模組的輸出值。
    4. 模組載入:使用**非同步的**`require(dependencies, function (myModule) {});`函式
        1. `dependencies`: 模組依賴的模組陣列;
        2. 回撥函式:模組載入完成後執行的函式,載入的模組將以引數形式傳入該函式,即:myModule,供使用。

CMD(Common Module Definition)通用模組定義

  • 就近依賴,需要時再進行載入,所以執行順序和書寫順序一致;這點與AMD不同,AMD是在使用模組之前將依賴模組全部載入完成,但由於網路等其他因素可能導致依賴模組下載先後順序不一致這就導致了,執行順序可能跟書寫順序不一致的情況
// 定義模組 module.js
define(function (require, exports, module) {

    // 在該函式裡面通過require實時的載入依賴模組
    var module1 = require('./module1.js');

    module1.test1(); 
});

// 載入模組
seajs.use(['module.js'], function (myModule) {
    // 除了用的是seajs.use其他和AMD的載入方式類似
});

ES6模組化,未來標準,但目前未廣泛使用

  • 優點

    • 類似commonJS,語法更簡潔;
    • 類似AMD,直接支援非同步載入和配置模組載入;
    • 結構可以做靜態分析,靜態檢測;
    • 比commonJS更好的支援迴圈依賴;
  • 語法概述

    • 命名式匯出方式:每個模組可以有多個
    • 定義式匯出方式:每個模組只有一個
ES6命名式匯出方式

一個模組通過export宣告來匯出多個,需要匯出的變數或函式只需要在宣告最前面加上export關鍵詞即可,然後在需要用的地方使用import匯入。這些匯出主要根據變數和函式名字來區分,故稱之為命名式匯出方式。

例如
```
// module.js
export const PI = 3.1415926;
export function double( r ) {
    return 2 * r;
}

export function sum( x, y ) {
    return x + y;
}

// 在main.js中使用
import { PI, double, sum } from 'module';

console.log(PI); // 3.1415926
console.log(double(10)); // 20
console.log(sum(10, 8)); // 18

// 使用屬性命名來使用模組
import { PI, double, sum } as myModule from 'module';
// 這樣就可以通過 myModule.double(10);語法去呼叫模組內變數或函數了
console.log( myModule.PI ); // 3.1415926
console.log( myModule.double(10) ); // 20
console.log( myModule.sum(10, 8) ); // 18
```
上面是ES6標準方式,下面是commonJS形式
```
// module.js
var pi = 3.1415926;
function double( r ) {}
function sum( a, b ) {}

module.exports = {
    pi: pi,
    double: double,
    sum: sum
};

// main.js
var module = require('./module.js');

console.log( module.pi );
module.double(10);
module.sum(10, 8);
```

從上述程式碼中可知命名式匯出方式的使用步驟
1. 在模組檔案中使用export關鍵字將指定的變數或函式匯出;
2. 然後在需要使用模組內函式或變數的地方使用import從該模組檔案中匯入這些變數或函式;

ES6定義式匯出方式
  • 概念:定義式匯出理解上就是一個檔案一個模組,一個模組一個功能,不像命名式一樣可以一個檔案可以匯出多個變數或函式(事實上命名式和定義式可以結合使用,看下面例子)。

  • 定義方式:

    單函式定義:

    // myFunc.js
    export default function () {
        // 這裡麵包含了該模組(或者說該檔案myFunc.js)所有邏輯
        // 這種方式該函式不需要名字,因為前面的default關鍵字意思即匯出時使用的模組名字和檔名一樣即:myFunc
    };
    
    // main.js
    import myFunc from 'myFunc';
    
    // 匯入後就可以直接使用了
    myFunc();

    類的定義

    // MyClass.js(首字母大寫)
    export default class () {
        // 類內容
        // class: 指定該模組匯出的是一個類  
    };
    
    // main.js
    var myclass = new MyClass();
    
    // 建立類之後就可以通過例項myclass去訪問類中的成員和方法了
  • 命名式 + 定義式相結合使用

    命名式和定義式結合使用:

    // module.js
    export default function () {};
    export function test() {};
    
    
    // main.js
    import { module, test } from 'module';
    
    // 另外定義式的匯出還可以直接用'default'代替,甚至可以定義別名,如下:
    import module from 'module';
    import default from 'module';
    import { default as myModule } from 'module';
    三種方式最終結果是一樣的,根據不同需求和習慣選擇。

ES6匯入和匯出

  • ES6提供的幾種匯入方式

    // 1. 定義式匯出和命名式匯出,以及相結合的匯出
    import default from 'module'; // 定義式
    import { name1, name2 } from 'module'; // 命名式
    import defualt, { name1, name2 } from 'module'; // 結合式
    
    // 2. 匯出重新命名,即將模組匯出後重命名為指定名字以供使用
    import { name1 as rename1, name2 } from 'module';// 即將name1重新命名為rename1供使用
    
    // 3. 將整個模組一起匯入,統一用指定屬性名去呼叫模組內的內容
    import * as moduleName from 'module';
    
    // 4. 值載入不匯出模組
    import 'module';
  • ES6提供的幾種匯出方式

// 1. 在模組內部使用export匯出
export var v1 = 10; // 全域性變數
export let v2 = 11; // 區域性變數
export const v3 = 12; // 常量

export function f1() {} // 函式
export class MyClass {} // 類

// 2. 模組內部使用default匯出整個表示式
export default 123;
export default function (x) { return x; }
export default x => x;
export default class {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
};

// 3. 將所有想匯出的列出來放到檔案最後一起匯出
export { v1, v2, v3 };

// 4. 還可以別名匯出
export { v1 as vv1, v2 as vv2, v3 };

// 5. 最後一種:重匯出,即在一個模組裡從另一個模組裡匯出內容
// module.js
export { v1, v2 } from 'otherModule';
// 別名
export { v1 as vv1, v2 } from 'otherModule';

ES6模組元資料,通過this module訪問當前模組資料的方式

// main.js
// 訪問module中的oData
import { oData } from this module;
console.log( oData );

// 如果想訪問當前模組的oData,就需要
import * as currModule from this module;
console.log( currModule.oData );