1. 程式人生 > >前端模塊化小總結—commonJs,AMD,CMD, ES6 的Module

前端模塊化小總結—commonJs,AMD,CMD, ES6 的Module

嚴格 ole 說過 定義 塊級作用域 解決 個數 數據 得到

隨著前端快速發展,需要使用javascript處理越來越多的事情,不在局限頁面的交互,項目的需求越來越多,更多的邏輯需要在前端完成,這時需要一種新的模式 --模塊化編程

模塊化的理解:模塊化是一種處理復雜系統分解為更好的可管理模塊的方式。簡單來說就是解耦,簡化開發,一個模塊就是實現特定功能的文件,可以更方便地使用別人的代碼,想要什麽功能,就加載什麽模塊。模塊開發需要遵循一定的規範

CommonJS規範

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

特點:1. 模塊輸出的是一個值的拷貝, 模塊是運行時加載,同步加載

   2.CommonJS 模塊的頂層this指向當前模塊

有兩個API :

require : 加載所要依賴的其他模塊
module.exports 或者exports :對外暴露的接口

示例:新建兩個模塊文件A.js 和 B.js

A.js
   //寫法一
module.exports = {
     a:1,
     b:2
}
    //寫法二
// module.exports.a=1;
// module.exports.b = 2;


//寫法三 // exports.a=1; // exports.b = 2

// 三種寫法結果是一樣,對外暴露的接口的結果是一致的
B.js 

console.log(require(‘./B.js‘));//{a:1,b:2}

註意

(1). exports module.exports 的區別:exports 是對 module.exports 的引用,不能直接給exports 賦值,直接賦值無效,結果是一個空對象,module.exports 可以直接賦值:如示例

// module.exports = 123; //123
// module.exports = function () { //[Function] // console.log(123) // }
// exports = 123; //{} // exports = function(){
//{} // console.log(123) // }

(2). 一個文件不能寫多個module.exports ,如果寫多個,對外暴露的接口是最後一個module.exports

(3). 模塊如果沒有指定使用module.exports 或者exports 對外暴露接口時,在其他文件就引用該模塊,得到的是一個空對象{}

AMD規範

AMD Asynchronous Module Definition,中文名是異步模塊定義的意思。它是一個在瀏覽器端模塊化開發的規範AMD RequireJS 在推廣過程中對模塊定義的規範化產出所以AMD規範的實現,就是的require.js

特點 異步加載,不阻塞頁面的加載,能並行加載多個模塊,但是不能按需加載,必須提前加載所需依賴

Amd 的規範中定義了兩個重要的api

define(id?,[]?,callbakc): //定義聲明模塊,參數id 模塊id標識(可選),參數二是一個數組(可選),依賴其他模塊,最後是回調函數

require([module],callback):// 加載模塊,參數一,是數組,指定加載的模塊,參數二回調函數,模塊加載完成後執行

還有一個配置屬性API

require.config({

baseUrl: //基本路徑

paths:// 對象,對外加載的模塊名稱  : 鍵值關系,鍵:自定義模塊名稱,值 :文件名或者文件路徑(不要寫文件後綴.js),可以是字符串,數組(如果第一個加載失敗,會加載第二個)

shim://對象,配置非AMD 模式的文件,每個模塊要定義(1)exports:值(指在js文件暴露出來的全局變量,如:window.a)(2)deps: 數組,表明該模塊的依賴性

})

註意:paths 的設置加載優化與shim 中配置(AMD模式優先非AMD模式)

以下是例子,加載不同情況的(AMD 模式,AMD 模式)的例子

示例1 :加載AMD模式模塊

//首先,引入requiresJS
 //requirejs.html
   <!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <!--data-main :設置data-main屬性,設置加載主文件路徑,默認設置該目錄為根目錄 如:../js/index.js ,默認把 ../js 作為模塊文件的根模塊 -->
        <script src="../js/require.js" data-main="../js/index.js"  defer async="async" type="text/javascript"></script>
    </head>
</html>

//Math.js
// 定義了AMD 模式的模塊,並且引入了jquery 模塊 (這裏的jquery屬於AMD模式)
define([‘jquery‘],function(_){
    //define([‘moudel1‘,‘moudel2‘],callback())
    //如果一個模塊不依賴其他模塊,直接使用define()函數寫,
    //如果一個模塊依賴其他模塊,define()第一個參數是數組
    //回調函數的參數對應第一個參數數組的值,按順序
    console.log(_);//ƒ (a,b){return new m.fn.init(a,b)}
  return { 
        add:function(x,y){
            return x+y;
        },
    }
})

// index.js (主入口)
//配置好加載路徑
 require.config({
    baseUrl:"../js",
    paths:{    
        "index":"index", // 主入口文件 index: 
     "jquery":"jquery.min",// jquery 庫,符合AMD模式(1.7 版本以上符合AMD)
        "maths":"Math", //自定義AMD 模式的模塊
    }
})

//加載maths.js
   require(["maths"],function(_math){
    console.log( _math); // {add: ƒ }
})

示例2:加載非AMD 模式的模塊,依賴非AMD模塊 ,第三方插件(如jquery .lazyload 插件)

這裏用到了shim屬性配置(加載非AMD 模式)

//index.js (主入口)
//配置好加載路徑
 require.config({
    baseUrl:"../js",
    paths:{    
        "index":"index", // 主入口文件 index: 
        "jquery":"jquery.min",//jquery 庫(1.7 版本以上符合AMD)
        "jquery.lazyload":[ // 非AMD 模式 依賴jquery 庫的第三方插件
       "http://apps.bdimg.com/libs/jquery-lazyload/1.9.5/jquery.lazyload.min",
      "jquery.lazyload"
      
]
     //這裏加載了百度靜態資源cdn和本地的資源(如果第一個加載失敗,會加載本地)},   shim:{ "jquery.lazyload":{ deps:["jquery"]//配置 jquery 依賴 } } })   //加載jquery 和jquery.lazyload require(["jquery","jquery.lazyload"],function($){ $("img.lazy").lazyload({effect: "fadeIn"}); })

示例3 加載非AMD 模式 (閉包形式)的模塊,有依賴(非AMD模式),自定義(與示例2,差不多)

指定了config 配置的 shim exports屬性,有返回值

//util.js,
//非閉包,暴露了兩個全局變量 utils ,obj
var utils = {};  
var obj = {};
utils.add = function(v1,v2){  
    return v1+v2;  
}; 
obj.reduce = function(x,y){
    return x-y;
}
//test1.js
//非AMD 模式的 (閉包形式),一般插件寫法,使用wondow 對外暴露了objTest 變量
(function(window,$,obj,utils){
    console.log($);//ƒ (a,b){return new m.fn.init(a,b)}
    console.log(utils);//{add: ƒ}
    console.log(obj);//{reduce: ƒ}
    window.objTest = {};//window等於對外暴露接口
    
})(window,jQuery,obj,utils)//  只能寫接口$或者 jQuery(不能寫AMD 的模塊名稱jquery,會報錯undefined)

//index.js (主入口)
//配置好加載路徑
 require.config({
    baseUrl:"../js",
    paths:{    
        "index":"index", // 主入口文件 index: 
        "jquery":"jquery.min",//jquery 庫(1.7 版本以上符合AMD)
    },
  shim:{
      "test1":{// 非AMD 模式的(閉包形式)
            deps:["util"],//設置依賴util:實際是,加載 了 "../js/util.js" 依賴該js 中的所有全局變量
            exports:"objTest" , //在test1.js 文件中,使用了window.objTest 對外暴露了接口
        },
      "util":{ // 非AMD 模式  (非閉包)
            deps:["jquery"],//設置依賴
        }
    }
})
//加載 test1.js
require(["test1"],function(_){
    console.log(_);//返回的是一個對象 {},因為在配置中設置了exports
    console.log("load finshing...")
});
//加載 util.js
require(["util"],function(_){
    console.log(_);//返回的是一個undefind,沒有配置exports
    console.log("load finshing...")
});


分析示例3:在 test1.js 中,閉包函數,一共傳了四個值,分別是 window,jQuery,obj,utils ,

window 就不用說了,jQuery 從哪裏傳來呢?index.js 主文件中的shim 中test1 模塊的依賴只有util (等於加載了util.js 文件,依賴了該文件中的所有全局變量,obj 和utils); 回歸jQuery 從哪裏加載進來,別忘paths 的配置加載優先shim 配置,所以先加載了paths 中的jquery 模塊,即使jquery 也是支持AMD ,但是也是暴露了window.jQuery 全局變量,所以後於jquery 模塊的加載,無論是AMD 還是非AMD 都可以得到該全局變量jQuery

註意 :AMD 模塊中依賴AMD 模塊,是不可以直接在deps屬性中設置AMD的模塊名作為依賴,這樣是直接報錯的

CMD規範

CMD規範是阿裏的玉伯提出來的,實現js庫為sea.js 它和requirejs非常類似,即一個js文件就是一個模塊,但是CMD的加載方式更加優秀,是通過按需加載的方式,而不是必須在模塊開始就加載所有的依賴。

玉伯說過能夠親眼看到seajs死掉也是一種幸福(說明了什麽,你懂的)

seajs.config({
//設置別名,方便調用
alias: { jquery:  http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js }
});
define(function(require, exports, module) {
    //引用jQuery模塊
    var $ = require(jquery);
});
// 加載多個模塊,在加載完成時,執行回調
seajs.use([./a, ./b], function(a, b) {
  a.doSomething();
  b.doSomething();
});

es6 的module規範

ES6 在語言標準的層面上,實現了模塊功能,而且非常簡單,ES6到來,完全可以取代 CommonJS AMD規範,成為瀏覽器和服務器通用的模塊解決方案。vue,Angular React這些mvvm 模式的框架發展,讓前端的編程變得模塊化,組件化。

特點:1. ES6 模塊之中,頂層的this指向undefined,即不應該在頂層代碼使用this

   2. 自動采用嚴格模式"use strict"。須遵循嚴格模式的要求

   3. ES6 模塊的設計思想是盡量的靜態化,編譯時加載”或者靜態加載,編譯時輸出接口

   4. ES6 模塊exportimport命令可以出現在模塊的任何位置,但是必須處於模塊頂層。如果處於塊級作用域內,就會報錯

   5.ES6 模塊輸出的是值的引用

模塊功能主要由兩個命令構成:export 和 import

export用於規定模塊的對外接口,

import用於輸入其他模塊提供的功能。

1.export 命令

export命令除了輸出變量,還可以輸出函數或類(class),不同的數據類型

export.js

//變量
export var m = 1; //函數 export function fn(x, y) { return x * y; }; //類class export class class1{} //export輸出的變量就是本來的名字,但是可以使用as關鍵字重命名 function v1() { ... } function v2() { ... } export { v1 as streamV1, v2 as streamV2, v2 as streamV3 };

2.import命令

使用export命令定義了模塊的對外接口以後,其他 JS 文件就可以通過import命令加載這個模塊。

main.js

//靜態加載,只加載export.js 文件中三個變量,其他不加載
import {m, fn, streamV1} from ./export.js;

//import命令要使用as關鍵字,將輸入的變量重命名。
import {fn as fn1} from ./export.js;

//整體加載模塊
improt * as all from ./export.js

3.export default 命令

本質上,export default就是輸出一個叫做default的變量或方法

// export-default.js
export default function foo() {
  console.log(foo);
}

// 或者寫成

function foo() {
  console.log(foo);
}
//foo函數的函數名foo,在模塊外部是無效的。加載的時候,視同匿名函數加載 export default foo;
//import-default.js import myfoo from ./export-default.js;

比較一下默認輸出和正常輸出

// 第一組
export default function crc32() { // 輸出
  // ...
}

import crc32 from crc32; // 輸入

// 第二組
export function crc32() { // 輸出
  // ...
};

import {crc32} from crc32; // 輸入

分析:上面代碼的兩組寫法,

第一組是使用export default時,對應的import語句不需要使用大括號;

第二組是不使用export default時,對應的import語句需要使用大括號。

以下的寫法是有效的

// modules.js
function add(x, y) {
  return x * y;
}
export {add as default};
// 等同於
// export default add;

// app.js
import { default as foo } from modules;
// 等同於
// import foo from ‘modules‘;

瀏覽器加載規則:

1、瀏覽器加載 ES6 模塊,也使用<script>標簽,但是要加入type="module"屬性。

瀏覽器對於帶有type="module"<script>,都是異步加載,不會造成堵塞瀏覽器,

<script type="module" src="./foo.js"></script>

2.ES6 模塊也允許內嵌在網頁中,語法行為與加載外部腳本完全一致。

<script type="module">
  import utils from "./utils.js";

  // other code
</script>

es6模塊推薦參考:http://es6.ruanyifeng.com/#docs/module

完結。。。謝謝

前端模塊化小總結—commonJs,AMD,CMD, ES6 的Module