摘要:若不存在則模塊標(biāo)識(shí)應(yīng)該默認(rèn)定義為在加載器中被請(qǐng)求腳本的標(biāo)識(shí)。這也是目前很多插件頭部的寫(xiě)法,就是用來(lái)兼容各種不同模塊化的寫(xiě)法。語(yǔ)句輸出的值是動(dòng)態(tài)綁定的,綁定其所在的模塊。
前言
歷史上,js沒(méi)有模塊化的概念,不能把一個(gè)大工程分解成很多小模塊。這對(duì)于多人開(kāi)發(fā)大型,復(fù)雜的項(xiàng)目形成了巨大的障礙,明顯降低了開(kāi)發(fā)效率,java,Python有import,甚至連css都有@import,但是令人費(fèi)解的是js居然沒(méi)有這方面的支持。es6出現(xiàn)之后才解決了這個(gè)問(wèn)題,在這之前,各大社區(qū)也都出現(xiàn)了很多解決方法,比較出色的被大家廣為流傳的就有AMD,CMD,commonjs,UMD,今天我們就來(lái)分析這幾個(gè)模塊化的解決方案。
模塊加載上面提到的幾種模塊化的方案的模塊加載有何異同呢?
先來(lái)說(shuō)下es6模塊,es6模塊的設(shè)計(jì)思想是盡量靜態(tài)化,使得編譯時(shí)就能確定依賴(lài)關(guān)系,被稱(chēng)為編譯時(shí)加載。其余的都只能在運(yùn)行時(shí)確定依賴(lài)關(guān)系,這種被稱(chēng)為運(yùn)行時(shí)加載。下面來(lái)看下例子就明白了,比如下面這段代碼
let {a,b,c} = require("util");//會(huì)加載util里的所有方法,使用時(shí)只用到3個(gè)方法 import {a,b,c} from "util";//從util中加載3個(gè)方法,其余不加載模塊化的幾種方案
下面簡(jiǎn)單介紹一下AMD,CMD,commonjs,UMD這幾種模塊化方案。
commonjscommonjs是服務(wù)端模塊化采用的規(guī)范,nodejs就采用了這個(gè)規(guī)范。
根據(jù)commonjs的規(guī)范,一個(gè)多帶帶的文件就是一個(gè)模塊,加載模塊使用require方法,該方法讀取文件并執(zhí)行,返回export對(duì)象。
// foobar.js //私有變量 var test = 123; //公有方法 function foobar () { this.foo = function () { // do someing ... } this.bar = function () { //do someing ... } } //exports對(duì)象上的方法和變量是公有的 var foobar = new foobar(); exports.foobar = foobar; //讀取 var test = require("./foobar").foobar; test.bar();
CommonJS 加載模塊是同步的,所以只有加載完成才能執(zhí)行后面的操作。像Node.js主要用于服務(wù)器的編程,加載的模塊文件一般都已經(jīng)存在本地硬盤(pán),所以加載起來(lái)比較快,不用考慮異步加載的方式,所以CommonJS規(guī)范比較適用。但如果是瀏覽器環(huán)境,要從服務(wù)器加載模塊,這是就必須采用異步模式。所以就有了 AMD CMD 解決方案。
AMDAMD是"Asynchronous Module Definition"的縮寫(xiě),意思就是"異步模塊定義"
AMD設(shè)計(jì)出一個(gè)簡(jiǎn)潔的寫(xiě)模塊API:
define(id?, dependencies?, factory);
第一個(gè)參數(shù) id 為字符串類(lèi)型,表示了模塊標(biāo)識(shí),為可選參數(shù)。若不存在則模塊標(biāo)識(shí)應(yīng)該默認(rèn)定義為在加載器中被請(qǐng)求腳本的標(biāo)識(shí)。如果存在,那么模塊標(biāo)識(shí)必須為頂層的或者一個(gè)絕對(duì)的標(biāo)識(shí)。
第二個(gè)參數(shù),dependencies ,是一個(gè)當(dāng)前模塊依賴(lài)的,已被模塊定義的模塊標(biāo)識(shí)的數(shù)組字面量。
第三個(gè)參數(shù),factory,是一個(gè)需要進(jìn)行實(shí)例化的函數(shù)或者一個(gè)對(duì)象。
看下下面的例子就明白了
define("alpha", [ "require", "exports", "beta" ], function( require, exports, beta ){ export.verb = function(){ return beta.verb(); // or: return require("beta").verb(); } });
提到AMD就不得不提requirejs。
RequireJS 是一個(gè)前端的模塊化管理的工具庫(kù),遵循AMD規(guī)范,它的作者就是AMD規(guī)范的創(chuàng)始人 James Burke。
AMD的基本思想就是先加載需要的模塊,然后返回一個(gè)新的函數(shù),所有的操作都在這個(gè)函數(shù)內(nèi)部操作,之前加載的模塊在這個(gè)函數(shù)里是可以調(diào)用的。
CMD是seajs在推廣的過(guò)程中對(duì)模塊的定義的規(guī)范化產(chǎn)出
和AMD提前執(zhí)行不同的是,CMD是延遲執(zhí)行,不過(guò)requirejs從2.0開(kāi)始也開(kāi)始支持延遲執(zhí)行了,這取決于寫(xiě)法。
AMD推薦的是依賴(lài)前置,CMD推薦的是依賴(lài)就近。
看下AMD和CMD的代碼
//AMD define(["./a","./b"], function (a, b) { //依賴(lài)一開(kāi)始就寫(xiě)好 a.test(); b.test(); }); //CMD define(function (requie, exports, module) { //依賴(lài)可以就近書(shū)寫(xiě) var a = require("./a"); a.test(); ... //軟依賴(lài) if (status) { var b = requie("./b"); b.test(); } });UMD
UMD是AMD和commonjs的結(jié)合
AMD適用瀏覽器,commonjs適用服務(wù)端,如果結(jié)合了兩者就達(dá)到了跨平臺(tái)的解決方案。
UMD先判斷是否支持AMD(define是否存在),存在用AMD模塊的方式加載模塊,再判斷是否支持nodejs的模塊(exports是否存在),存在用nodejs模塊的方式,否則掛在window上,當(dāng)全局變量使用。
這也是目前很多插件頭部的寫(xiě)法,就是用來(lái)兼容各種不同模塊化的寫(xiě)法。
(function(window, factory) { //amd if (typeof define === "function" && define.amd) { define(factory); } else if (typeof exports === "object") { //umd module.exports = factory(); } else { window.jeDate = factory(); } })(this, function() { ...module..code... })ES6
es6的模塊自動(dòng)采用嚴(yán)格模式,不管有沒(méi)有在頭部加上"use strict"
模塊是由export和import兩個(gè)命令構(gòu)成。
export命令可以出現(xiàn)在模塊的任何位置,只要處于模塊的頂層(不在塊級(jí)作用域內(nèi))即可。如果處于塊級(jí)作用域內(nèi),會(huì)報(bào)錯(cuò)。
export語(yǔ)句輸出的值是動(dòng)態(tài)綁定的,綁定其所在的模塊。
export default命令//a.js export default function(){ console.log("aaa"); } //b.js import aaa from "a.js";
1.使用export default的時(shí)候,對(duì)應(yīng)的import不需要使用大括號(hào),import命令可以為default指定任意的名字。
2.不適用export default的時(shí)候,對(duì)應(yīng)的import是需要使用大括號(hào)的
3.一個(gè)export default只能使用一次
import命令具有提升效果,會(huì)提升到整個(gè)模塊的頭部首先執(zhí)行,所以建議直接寫(xiě)在頭部,這樣也方便查看和管理。
import語(yǔ)句會(huì)執(zhí)行所加載的模塊,因?yàn)橛幸韵碌膶?xiě)法
import "lodash;
上面的代碼僅僅執(zhí)行了lodash模塊,沒(méi)有輸入任何值
整體加載整體加載有兩種方式
//import import * as circle from "./circle" //module //module后面跟一個(gè)變量,表示輸入的模塊定義在該變量上 module circle from "./circle"循環(huán)加載
在講循環(huán)加載前,先了解下commonjs和es6模塊加載的原理
commonjs模塊加載的原理commonjs的一個(gè)模塊就是一個(gè)腳本文件,require命令第一次加載腳本的時(shí)候就會(huì)執(zhí)行整個(gè)腳本,然后在內(nèi)存中生成一個(gè)對(duì)象
{ id:"...", exports: {...}, loaded: true, ... }
上面的對(duì)象中,id是模塊名,exports是模塊輸出的各個(gè)接口,loaded是一個(gè)布爾值,表示該模塊的腳本是否執(zhí)行完畢.
之后要用到這個(gè)模塊時(shí),就會(huì)到exports上取值,即使再次執(zhí)行require命令,也不會(huì)執(zhí)行該模塊,而是到緩存中取值
commonjs模塊輸入的是被輸出值的拷貝,也就是說(shuō)一旦輸出一個(gè)值,模塊內(nèi)部的變化就影響不到這個(gè)值
es6的運(yùn)行機(jī)制和commonjs不一樣,它遇到模塊加載命令import不會(huì)去執(zhí)行模塊,只會(huì)生成一個(gè)動(dòng)態(tài)的只讀引用,等到真正要用的時(shí)候,再到模塊中去取值,由于es6輸入的模塊變量只是一個(gè)‘符號(hào)鏈接’,所以這個(gè)變量是只讀的,對(duì)他進(jìn)行重新賦值會(huì)報(bào)錯(cuò)。
import {obj} from "a.js"; obj.a = "qqq";//ok obj = {}//typeError
分析完兩者的加載原理,來(lái)看下兩者的循環(huán)加載
commonjs的循環(huán)加載commonjs模塊的重要特性是加載時(shí)執(zhí)行,即代碼在require的時(shí)候就會(huì)執(zhí)行,commonjs的做法是一旦出現(xiàn)循環(huán)加載,就只輸出已經(jīng)執(zhí)行的部分,還未執(zhí)行的部分不會(huì)輸出.
下面來(lái)看下commonjs中的循環(huán)加載的代碼
//a.js exports.done = false; var b = require("./b.js"); console.log("在a.js中,b.done=",b.done); exports.done = true; console.log("a.js執(zhí)行完畢") //b.js exports.done = false; var a = require("./a.js"); console.log("在b.js中,a.done=",a.done); exports.done = true; console.log("b.js執(zhí)行完畢") //main.js var a = require("./a.js"); var b = require("./b.js"); console.log("在main.js中,a.done=",a.done,",b.done=",b.done);
上面的代碼中,執(zhí)行a.js的時(shí)候,a.js先輸出done變量,然后加載另一個(gè)腳本b.js,此時(shí)a的代碼就停在這里,等待b.js執(zhí)行完畢,再往下執(zhí)行。然后看下b.js的代碼,b.js也是先輸出done變量,然后加載a.js,這時(shí)發(fā)生了循環(huán)加載,按照commonjs的機(jī)制,系統(tǒng)會(huì)去a.js中的exports上取值,可是其實(shí)a.js是沒(méi)有執(zhí)行完的,只能輸出已經(jīng)執(zhí)行的部分done=false,然后b.js繼續(xù)執(zhí)行,執(zhí)行完畢后將執(zhí)行權(quán)返回給a.js,于是a.js繼續(xù)執(zhí)行,直到執(zhí)行完畢。
所以執(zhí)行main.js,結(jié)果為
在b.js中,a.done=false
b.js執(zhí)行完畢
在a.js中,b=done=true
a.js執(zhí)行完畢
在main.js中,a.done=true,b.done=true
上面這個(gè)例子說(shuō)了兩點(diǎn)
在b.js中a.js沒(méi)有執(zhí)行完,只執(zhí)行了第一行
2.main.js中執(zhí)行到第二行不會(huì)再次執(zhí)行b.js,而是輸出緩存的b.js的執(zhí)行結(jié)果,即第4行
es6的循環(huán)加載es6處理循環(huán)加載和commonjs不同,es6是動(dòng)態(tài)引用,遇到模塊加載命令import時(shí)不會(huì)去執(zhí)行模塊,只會(huì)生成一個(gè)指向模塊的引用,需要開(kāi)發(fā)者自己保證能取到輸出的值
看下面的例子
//a.js import {odd} from "b.js"; export counter = 0; export function even(n){ counter++; return n==0 || odd(n-1); } //b.js import {even} from "a.js"; export function odd(n){ return n!=0 && even(n-1); } //main.js import {event,counter } from "./a.js"; event(10) counter //6
執(zhí)行main.js,按照commonjs的規(guī)范,上面的代碼是無(wú)法執(zhí)行的,因?yàn)閍先加載b,b又加載a,但是a又沒(méi)有輸出值,b的even(n-1)會(huì)報(bào)錯(cuò)
但是es6可以執(zhí)行,結(jié)果是6
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/99618.html
摘要:常見(jiàn)模塊化方案是由社區(qū)提出的模塊化方案中的一種,遵循了這套方案。是模塊化規(guī)范中的一種,遵循了這套規(guī)范。中的模塊化能力由兩個(gè)命令構(gòu)成和,命令用于規(guī)定模塊的對(duì)外接口,命令用于輸入其他模塊提供的功能。 為什么需要模塊化 在ES6出現(xiàn)之前,JS語(yǔ)言本身并沒(méi)有提供模塊化能力,這為開(kāi)發(fā)帶來(lái)了一些問(wèn)題,其中最重要的兩個(gè)問(wèn)題應(yīng)當(dāng)是全局污染和依賴(lài)管理混亂。 // file a.js var name =...
摘要:即盡早地執(zhí)行依賴(lài)模塊。阮一峰輸出值的引用模塊是動(dòng)態(tài)關(guān)聯(lián)模塊中的值,輸出的是值得引用。的加載實(shí)現(xiàn)阮一峰運(yùn)行時(shí)加載靜態(tài)編譯模塊是運(yùn)行時(shí)加載,模塊是編譯時(shí)輸出接口。 模塊化開(kāi)發(fā) 優(yōu)點(diǎn) 模塊化開(kāi)發(fā)中,通常一個(gè)文件就是一個(gè)模塊,有自己的作用域,只向外暴露特定的變量和函數(shù),并且可以按需加載。 依賴(lài)自動(dòng)加載,按需加載。 提高代碼復(fù)用率,方便進(jìn)行代碼的管理,使得代碼管理更加清晰、規(guī)范。 減少了命名沖...
摘要:要想讓模塊再次運(yùn)行,必須清除緩存。模塊加載會(huì)阻塞接下來(lái)代碼的執(zhí)行,需要等到模塊加載完成才能繼續(xù)執(zhí)行同步加載。環(huán)境服務(wù)器環(huán)境應(yīng)用的模塊規(guī)范是參照實(shí)現(xiàn)的。這等同在每個(gè)模塊頭部,有一行這樣的命令。 commonJS 特點(diǎn): 1、模塊可以多次加載,但是只會(huì)在第一次加載時(shí)運(yùn)行一次,然后運(yùn)行結(jié)果就被緩存了,以后再加載,就直接讀取緩存結(jié)果。要想讓模塊再次運(yùn)行,必須清除緩存。2、模塊加載會(huì)阻塞接下來(lái)代...
摘要:參考資料前端模塊化詳解完整版入門(mén)近一萬(wàn)字的語(yǔ)法知識(shí)點(diǎn)補(bǔ)充徹底搞清楚中的和和詳解 前言 前端的模塊化之路經(jīng)歷了漫長(zhǎng)的過(guò)程,想詳細(xì)了解的小伙伴可以看浪里行舟大神寫(xiě)的前端模塊化詳解(完整版),這里根據(jù)幾位大佬們寫(xiě)的文章,將模塊化規(guī)范部分做了匯總和整理,希望讀完的小伙伴能有些收獲,也希望覺(jué)得有用的小伙伴可以點(diǎn)個(gè)贊,筆芯。 什么是模塊 將一個(gè)復(fù)雜的程序依據(jù)一定的規(guī)則(規(guī)范)封裝成幾個(gè)塊(文件)...
摘要:也就是說(shuō),外部模塊輸出值變了,當(dāng)前模塊的導(dǎo)入值不會(huì)發(fā)生變化。三規(guī)范的出現(xiàn),使得模塊化在環(huán)境中得到了施展機(jī)會(huì)。模塊化這種加載稱(chēng)為編譯時(shí)加載或者靜態(tài)加載。總結(jié)的模塊化規(guī)范經(jīng)過(guò)了模塊模式的演進(jìn),利用現(xiàn)在常用的打包工具,非常方便我們編寫(xiě)模塊化代碼。 前言 什么是模塊化? 模塊就是實(shí)現(xiàn)特定功能的一組方法,而模塊化是將模塊的代碼創(chuàng)造自己的作用域,只向外部暴露公開(kāi)的方法和變量,而這些方法之間高度解耦...
閱讀 2938·2023-04-26 02:22
閱讀 2292·2021-11-17 09:33
閱讀 3144·2021-09-22 16:06
閱讀 1078·2021-09-22 15:54
閱讀 3541·2019-08-29 13:44
閱讀 1921·2019-08-29 12:37
閱讀 1327·2019-08-26 14:04
閱讀 1920·2019-08-26 11:57