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

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

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

 

CommonJS規範前端

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

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

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

有兩個API : jquery

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

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

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}

注意web

 (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';

 

瀏覽器加載規則:

 一、瀏覽器加載 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

 

完結。。。謝謝

相關文章
相關標籤/搜索