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