摘要:應(yīng)用日益復(fù)雜,模塊化已經(jīng)成為一個(gè)迫切需求。異步模塊加載機(jī)制。引用的資源列表太長(zhǎng),懶得回調(diào)函數(shù)中寫(xiě)一一對(duì)應(yīng)的相關(guān)參數(shù)假定這里引用的資源有數(shù)十個(gè),回調(diào)函數(shù)的參數(shù)必定非常多這就是傳說(shuō)中的
簡(jiǎn)述 緣起
模塊通常是指編程語(yǔ)言所提供的代碼組織機(jī)制,利用此機(jī)制可將程序拆解為獨(dú)立且通用的代碼單元。
模塊化主要是解決代碼分割、作用域隔離、模塊之間的依賴(lài)管理以及發(fā)布到生產(chǎn)環(huán)境時(shí)的自動(dòng)化打包與處理等多個(gè)方面。
javascript應(yīng)用日益復(fù)雜,模塊化已經(jīng)成為一個(gè)迫切需求。但是作為一個(gè)模塊化方案,它至少要解決如下問(wèn)題:
可維護(hù)性
避免作用域污染
代碼重用
依賴(lài)管理
script最原始的方式就是,每個(gè)文件就是一個(gè)模塊,然后使用script的方式進(jìn)行引入。
但是此方式有以下問(wèn)題:
全局作用域污染
無(wú)依賴(lài)管理,執(zhí)行順序依賴(lài)script標(biāo)簽的順序,如果采用異步加載,那會(huì)亂套,先下載的先執(zhí)行
IIFE 和 模塊對(duì)象為了解決作用域污染的問(wèn)題,就產(chǎn)生了立即執(zhí)行函數(shù) + 模塊對(duì)象模式:
// app1.js var app = {};
// app2.js (function(){ app.a = function(a, b) { // code } })();
// app3.js (function(app){ var temp = [ 1, 2]; var a = app.a(temp) })(app);
具體的可以查閱阮一峰老師的博客Javascript模塊化編程(一):模塊的寫(xiě)法
在ES6之前,js沒(méi)有塊級(jí)作用域,所以采用此方式建立一個(gè)函數(shù)作用域。但是在ES6之后,可以使用塊級(jí)作用域。
由于使用了IIFE,所以減少了全局作用域污染,但并不是徹底消除,因?yàn)檫€定義了一個(gè)appa模塊對(duì)象呢。
所以這也僅僅只是減少了作用域污染,還是會(huì)有其他缺點(diǎn)。
CommonJS后來(lái),有人試圖將javascript引入服務(wù)端,由于服務(wù)端編程相對(duì)比較復(fù)雜,就急需一種模塊化的方案,所以就誕生了commonjs,有require + module.exports實(shí)現(xiàn)模塊的加載和導(dǎo)出。
CommonJS采用同步的方式加載模塊,主要使用場(chǎng)景為服務(wù)端編程。因?yàn)榉?wù)器一般都是本地加載,速度較快。
AMD 和 CMD后來(lái),隨著前端業(yè)務(wù)的日漸復(fù)雜,瀏覽器端也需要模塊化,但是commonjs是同步加載的,這意味著加載模塊時(shí),瀏覽器會(huì)凍結(jié),什么都干不了,這在瀏覽器肯定是不行的,這就誕生了AMD和CMD規(guī)范,分別以requirejs和seajs為代表。
這兩貨都采用異步方式加載模塊。
AMDAMD(Asynchronous Module Defination)異步模塊加載機(jī)制。
define( [module_id,] // 模塊名字,如果缺省則為匿名模塊 [dependenciesArray,] // 模塊依賴(lài) definition function | object // 模塊內(nèi)容,可以為函數(shù)或者對(duì)象 );CMD
CMD(Common Module Defination)通用模塊加載機(jī)制
// 方式一 define(function(require, exports, module) { // 模塊代碼 var a = require("a") }); // 方式二 define( "module", ["module1", "module2"], function( require, exports, module ){ // 模塊代碼 } );區(qū)別
對(duì)于依賴(lài)的模塊,AMD 是提前執(zhí)行( RequireJS 從 2.0 開(kāi)始,也改成可以延遲執(zhí)行),CMD 是延遲執(zhí)行
CMD 推崇依賴(lài)就近,AMD 推崇依賴(lài)前置
AMD 的 API 默認(rèn)是一個(gè)當(dāng)多個(gè)用,CMD 的 API 嚴(yán)格區(qū)分,推崇職責(zé)單一
不完美盡管以上方案解決了上面說(shuō)的問(wèn)題,但是也帶來(lái)了一些新問(wèn)題:
語(yǔ)法冗余,所有東西都要封裝在define中
AMD中的依賴(lài)列表必須與函數(shù)的參數(shù)列表匹配,易錯(cuò)且修改苦難
Browserify由于上述這些原因,有些人想在瀏覽器使用 CommonJS 規(guī)范,但 CommonJS 語(yǔ)法主要是針對(duì)服務(wù)端且是同步的,所以就產(chǎn)生了Browserify,它是一個(gè) 模塊打包器(module bundler),可以打包c(diǎn)ommonjs規(guī)范的模塊到瀏覽器中使用。
UMDUMD(Universal Module Definition) 統(tǒng)一模塊定義。
AMD 與 CommonJS 雖然師出同源,但還是分道揚(yáng)鑣,關(guān)注于代碼異步加載與最小化入口模塊的開(kāi)發(fā)者將目光投注于 AMD;而隨著 Node.js 以及 Browserify 的流行,越來(lái)越多的開(kāi)發(fā)者也接受了 CommonJS 規(guī)范。令人扼腕嘆息的是,符合 AMD 規(guī)范的模塊并不能直接運(yùn)行于 CommonJS 模塊規(guī)范的環(huán)境中,符合 CommonJS 規(guī)范的模塊也不能由 AMD 進(jìn)行異步加載。
而且有這么多種規(guī)范,如果我們要發(fā)布一個(gè)模塊供其他人用,我們不可能為每種規(guī)范發(fā)布一個(gè)版本,就算你蛋疼這樣做了,別人使用的時(shí)候還得下載對(duì)應(yīng)版本,所以現(xiàn)在需要一種方案來(lái)兼容這些規(guī)范。
實(shí)現(xiàn)的方式就是在代碼前面做下判斷,根據(jù)不同的規(guī)范使用對(duì)應(yīng)的加載方式。
// 以vue為例 (function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : (global = global || self, global.Vue = factory()); }(this, function () { // vue code ... })
由于目前ES6瀏覽器支持還不夠好,所以很多第三方庫(kù)都采用了這種方式。
ESModuleES6引入了ESModule規(guī)范,主要通過(guò)export + import來(lái)實(shí)現(xiàn),最終一統(tǒng)江湖??墒乾F(xiàn)實(shí)很骨感,一些瀏覽器并不支持(IE,說(shuō)的就是你),所以還不能直接在瀏覽器中直接使用。
常用的兩種方案在線編譯:requirejs/seajs/sytemjs
在頁(yè)面上加載一個(gè)AMD/CMD模塊格式解釋器。這樣瀏覽器就認(rèn)識(shí)了define, exports,module這些東西,也就實(shí)現(xiàn)了模塊化。
SystemJS 是一個(gè)通用的模塊加載器,它能在瀏覽器或者 NodeJS 上動(dòng)態(tài)加載模塊,并且支持 CommonJS、AMD、全局模塊對(duì)象和 ES6 模塊。通過(guò)使用插件,它不僅可以加載 JavaScript,還可以加載 CoffeeScript 和 TypeScript。配合jspm也是不錯(cuò)的搭配。
預(yù)編譯:browserify/webpack
相比于第一種方案,這個(gè)方案更加智能。由于是預(yù)編譯的,不需要在瀏覽器中加載解釋器。你在本地直接寫(xiě)JS,不管是AMD/CMD/ES6風(fēng)格的模塊化,它都能認(rèn)識(shí),并且編譯成瀏覽器認(rèn)識(shí)的JS。
注意: browerify只支持Commonjs模塊,如需兼容AMD模塊,則需要plugin轉(zhuǎn)換
CommonJS前身為ServerJS。
我們可以理解為代碼會(huì)被如下內(nèi)建輔助函數(shù)包裹:
(function (exports, require, module, __filename, __dirname) { // ... // Your code // ... });加載
通過(guò)require加載模塊。
const a = require("a")導(dǎo)出
通過(guò)exports和module.exports進(jìn)行模塊導(dǎo)出。
exports:exports是module.exports的一個(gè)引用,一個(gè)模塊可以使用多次,但是不能直接對(duì)exports重新賦值,只能通過(guò)如下方式使用
exports.a = function(){ // code... }
module.exports:一個(gè)模塊只能使用一次
module.exports = function(){ // code... }特點(diǎn)
同步加載,定位于服務(wù)端,不適合瀏覽器
requirejs 特點(diǎn)支持同步和異步兩種方式
大多數(shù)第三方庫(kù)都有兼容AMD,即時(shí)不兼容也可以通過(guò)配置使其可用
異步加載,不阻塞瀏覽器
依賴(lài)前置,可以很清楚看到當(dāng)前模塊的依賴(lài)
引入在引入requirejs的script標(biāo)簽上添加data-main屬性定義入口文件,該文件會(huì)在requirejs加載完后立即執(zhí)行。
如果baseUrl未多帶帶配置,則默認(rèn)為引入require的文件的路徑。
configrequirejs.config({ // 為模塊加上query參數(shù),解決瀏覽器緩存,只在開(kāi)發(fā)環(huán)境使用 urlArgs: "yn-course=" + (new Date()).getTime(), // 配置所有模塊加載的初始路徑,所有模塊都是基于此路徑加載 baseUrl: "./", // 映射一些快捷路徑,相當(dāng)于別名 paths: { "~": "assets", "@": "components", "vue": "assets/lib/vue/vue", "vueRouter": "assets/lib/vue-router/vue-router", "jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery"] }, // 對(duì)于匹配的模塊前綴,使用一個(gè)不同的模塊ID來(lái)加載該模塊 map: { "layer": { "jquery": "http://libs.baidu.com/jquery/2.0.3/jquery" } }, // 從CommonJS包(package)中加載模塊 packages:{}, // 加載上下文 context:{}, // 超時(shí),默認(rèn)7S waitSeconds: 7, // 定義應(yīng)用依賴(lài)的模塊,在啟動(dòng)后會(huì)加載此數(shù)組中的模塊 deps: [], // 在deps加載完畢后執(zhí)行的函數(shù) callback:function(){}, // 用來(lái)加載非AMD規(guī)范的模塊,以瀏覽器全局變量注入,此處僅作映射,需要在需要時(shí)手動(dòng)載入 shim: { // "backbone": { // deps: ["underscore", "jquery"], // 模塊依賴(lài) // exports: "Backbone" // 導(dǎo)出的名稱(chēng) // } }, // 全局配置信息,可在模塊中通過(guò)module.config()訪問(wèn) config:{ color:"red" }, // 如果設(shè)置為true,則當(dāng)一個(gè)腳本不是通過(guò)define()定義且不具備可供檢查的shim導(dǎo)出字串值時(shí),就會(huì)拋出錯(cuò)誤 enforceDefine:false, // 如果設(shè)置為true,則使用document.createElementNS()去創(chuàng)建script元素 xhtm: false, //指定RequireJS將script標(biāo)簽插入document時(shí)所用的type=""值 scriptType:"text/javascript" });
默認(rèn)requirejs會(huì)根據(jù)baseUrl+paths配置去查找模塊,但是如下情況例外:
路徑以.js結(jié)尾,比如lib/hello.js、hello.js
以“/”開(kāi)始
包含url協(xié)議:如"http:"、"https"
設(shè)置baseURl的方式有如下三種:
requirejs.config指定;
如指data-main,則baseUrl為data-main所對(duì)應(yīng)的js的目錄
如果上述均未指定,則baseUrl為運(yùn)行RequireJS的HTML文件所在目錄
map配置對(duì)于大型項(xiàng)目很重要:如有兩類(lèi)模塊需要使用不同版本的"foo",但它們之間仍需要一定的協(xié)同。
在那些基于上下文的多版本實(shí)現(xiàn)中很難做到這一點(diǎn)。而且,paths配置僅用于為模塊ID設(shè)置root paths,而不是為了將一個(gè)模塊ID映射到另一個(gè)。
requirejs.config({ map: { "some/newmodule": { "foo": "foo1.2" }, "some/oldmodule": { "foo": "foo1.0" } } });define
通過(guò)define來(lái)定義模塊,推薦依賴(lài)前置原則,當(dāng)然也可以使用require動(dòng)態(tài)按需加載。
define( [module_id,] // 模塊名字,如果缺省則為匿名模塊 [dependencies,] // 模塊依賴(lài) definition function | object // 模塊內(nèi)容,可以為函數(shù)或者對(duì)象 );
// 如果僅僅返回一個(gè)鍵值對(duì),可以采用如下格式,類(lèi)似JSONP define({ color: "black", size: "unisize" }) //如果沒(méi)有依賴(lài) define(function () { return { color: "black", size: "unisize" } }) // 有依賴(lài) define(["./a", "./b"], function(a, b) { }) // 具名模塊 define("name", ["c", "d"], function(cart, inventory) { //此處定義foo/title object } )
如要在define()內(nèi)部使用諸如require("./a/b")相對(duì)路徑,記得將"require"本身作為一個(gè)依賴(lài)注入到模塊中:
define(["require", "./a/b"], function(require) { var mod = require("./a/b"); });
或者使用如下方式:
define(function(require) { var mod = require("./a/b"); })標(biāo)識(shí)
require加載的所有模塊都是單例的,每個(gè)模塊都有一個(gè)唯一的標(biāo)識(shí),這個(gè)標(biāo)識(shí)是模塊的名字或者模塊的相對(duì)路徑(如匿名模塊)。
模塊的唯一性與它們的訪問(wèn)路徑無(wú)關(guān),即使是地址完全相同的一份JS文件,如果引用的方式與模塊的配置方式不一致,依舊會(huì)產(chǎn)生多個(gè)模塊。
// User.js define([], function() { return { username : "yiifaa", age : 20 }; });
require(["user/User"], function(user) { // 修改了User模塊的內(nèi)容 user.username = "yiifee"; // em/User以baseUrl定義的模塊進(jìn)行訪問(wèn) // "user/User"以path定義的模塊進(jìn)行訪問(wèn) require(["em/User", "user/User"], function(u1, u2) { // 輸出的結(jié)果完全不相同,u1為yiifaa,u2為修改后的內(nèi)容yiifee console.log(u1, u2); }) })依賴(lài)
requirejs推薦依賴(lài)前置,在define或者require模塊的時(shí)候,可以將需要依賴(lài)的模塊作為第一個(gè)參數(shù),以數(shù)組的方式聲明,然后在回調(diào)函數(shù)中,依賴(lài)會(huì)以參數(shù)的形式注入到該函數(shù)上,參數(shù)列表需要和依賴(lài)數(shù)組中位置一一對(duì)應(yīng)。
define(["./a", "./b"], function(a, b) { })導(dǎo)出
在requirejs中,有3中方式進(jìn)行模塊導(dǎo)出:
通過(guò)return方式導(dǎo)出,優(yōu)先級(jí)最高(推薦);
define(function(require, exports, module) { return { a : "a" } });
通關(guān)module.exports對(duì)象賦值導(dǎo)出,優(yōu)先級(jí)次之;
define(function(require, exports, module) { module.exports = { a : "a" } });
通過(guò)exports對(duì)象賦值導(dǎo)出,優(yōu)先級(jí)最低;
define(function(require, exports, module) { exports.a = "a" });require
requirejs提供了兩個(gè)全局變量require、requirejs供我們加載模塊,這二者是完全等價(jià)的。
// 此處require 和 define 函數(shù)僅僅是一個(gè)參數(shù)(模塊標(biāo)識(shí))的差異, // 一般require用于沒(méi)有返回的模塊,如應(yīng)用頂層模塊 require( [dependencies,] // 模塊依賴(lài) definition function // 模塊內(nèi)容 );
require是內(nèi)置模塊,不用在配置中定義,直接進(jìn)行引用即可。
define(["require"], function(require) { var $ = require("jquery"); })
requirejs支持異步(require([module]))和同步(require(module))兩種方式加載,即require參數(shù)為數(shù)組即為異步加載,反之為同步。
同步加載在requirejs中,執(zhí)行同步加載必須滿足兩點(diǎn)要求:
必須在定義模塊時(shí)使用,亦即define函數(shù)中;
引用的資源必須是之前異步加載過(guò)的(不必在同一個(gè)模塊),換句話說(shuō),同步載入的模塊是不會(huì)發(fā)網(wǎng)絡(luò)請(qǐng)求的,只會(huì)調(diào)取之前緩存的模塊;
define(function(require, exports, module) { })中可以同步加載模塊
應(yīng)用場(chǎng)景明確知道模塊的先后順序,確認(rèn)此模塊已經(jīng)被加載過(guò),例如系統(tǒng)通用模塊,在載入完成后,之后的模塊就可以進(jìn)行同步的引用,或者在Vue等前端技術(shù)框架中,在應(yīng)用模塊同步加載vue模塊。
引用的資源列表太長(zhǎng),懶得回調(diào)函數(shù)中寫(xiě)一一對(duì)應(yīng)的相關(guān)參數(shù)
// 假定這里引用的資源有數(shù)十個(gè),回調(diào)函數(shù)的參數(shù)必定非常多 define(["jquery"], function() { return function(el) { // 這就是傳說(shuō)中的同步調(diào)用 var $ = require("jquery"); $(el).html("Hello, World!"); } })
可以減少命名或者命名空間沖突,例如prototype與jquery的沖突問(wèn)題
define(["jquery", "prototype"], function() { var export = {}; export.jquery = function(el) { // 這就是傳說(shuō)中的同步調(diào)用 var $ = require("jquery"); $(el).html("Hello, World!"); } export.proto = function(el) { // 這就是傳說(shuō)中的同步調(diào)用 var $ = require("prototype"); $(el).html("Hello, World!"); } return export; })
處女座專(zhuān)用,代碼顯得更整潔漂亮了,硬是把回調(diào)函數(shù)寫(xiě)出了同步的感覺(jué)
異步加載define([],function()):依賴(lài)數(shù)組中的模塊會(huì)異步加載,所有模塊加載完成后混執(zhí)行回調(diào)函數(shù)
require([]):傳入數(shù)組格式即表示需要異步加載
require === requirejs //=> truerequire.toUrl("./a.css"): 獲取模塊url 模塊卸載
只要頁(yè)面不刷新,被requirejs加載的模塊只會(huì)執(zhí)行一次,后面會(huì)一直緩存在內(nèi)存中,即時(shí)重新引入模塊也不會(huì)再進(jìn)行初始化。
我們可以通過(guò)undef卸載已加載的模塊。
require.undef("moduleName") // moduleName是模塊標(biāo)識(shí)其他 插件
css:加載css
text:加載HTML及其他文本
domReady
模塊加載錯(cuò)誤Module name has not been loaded yet for context: _:
此錯(cuò)誤表示執(zhí)行時(shí)模塊還未加載成功,一般為異步加載所致,改成同步加載即可。
借助類(lèi)解決模塊間的相互干擾//C模塊 define([],function(){ // 定義一個(gè)類(lèi) function DemoClass() { var count = 0; this.say = function(){ count++; return count; }; } return function(){ //每次都返回一個(gè)新對(duì)象 return new DemoClass(); }; }); // A模塊 require(["C"], function(module) { cosole.log(module().say());//1 }); // B模塊 require(["C"], function(module) { cosole.log(module().say());//1 });
文檔:官方文檔,
[中文版](https://blog.csdn.net/wangzhanzheng/article/details/79050033)seajs
Sea.js 追求簡(jiǎn)單、自然的代碼書(shū)寫(xiě)和組織方式,具有以下核心特性:
簡(jiǎn)單友好的模塊定義規(guī)范:Sea.js 遵循 CMD 規(guī)范,可以像 Node.js 一般書(shū)寫(xiě)模塊代碼。
自然直觀的代碼組織方式:依賴(lài)的自動(dòng)加載、配置的簡(jiǎn)潔清晰,可以讓我們更多地享受編碼的樂(lè)趣。
通過(guò)exports + require實(shí)現(xiàn)模塊的加載與導(dǎo)出。
引入config
//seajs配置 seajs.config({ //1.頂級(jí)標(biāo)識(shí)始終相對(duì) base 基礎(chǔ)路徑解析。 //2.絕對(duì)路徑和根路徑始終相對(duì)當(dāng)前頁(yè)面解析。 //3.require 和 require.async 中的相對(duì)路徑相對(duì)當(dāng)前模塊路徑來(lái)解析。 //4.seajs.use 中的相對(duì)路徑始終相對(duì)當(dāng)前頁(yè)面來(lái)解析。 // Sea.js 的基礎(chǔ)路徑 在解析頂級(jí)標(biāo)識(shí)時(shí),會(huì)相對(duì) base 路徑來(lái)解析 base 的默認(rèn)值為 sea.js 的訪問(wèn)路徑的父級(jí) base: "./", // 路徑配置 當(dāng)目錄比較深,或需要跨目錄調(diào)用模塊時(shí),可以使用 paths 來(lái)簡(jiǎn)化書(shū)寫(xiě) paths: { gallery: "https://a.alipayobjects.com/gallery" /* var underscore = require("gallery/underscore"); //=> 加載的是 https://a.alipayobjects.com/gallery/underscore.js */ }, // 別名配置 當(dāng)模塊標(biāo)識(shí)很長(zhǎng)時(shí),可以使用 alias 來(lái)簡(jiǎn)化(相當(dāng)于 base 設(shè)置的目錄為基礎(chǔ)) //Sea.js 在解析模塊標(biāo)識(shí)時(shí), 除非在路徑中有問(wèn)號(hào)(?)或最后一個(gè)字符是井號(hào)(#),否則都會(huì)自動(dòng)添加 JS 擴(kuò)展名(.js)。如果不想自動(dòng)添加擴(kuò)展名,可以在路徑末尾加上井號(hào)(#)。 alias: { "seajs-css": "~/lib/seajs/plugins/seajs-css", "seajs-text": "~/lib/seajs/plugins/seajs-text", "$": "~/lib/zepto/zepto" }, // 變量配置 有些場(chǎng)景下,模塊路徑在運(yùn)行時(shí)才能確定,這時(shí)可以使用 vars 變量來(lái)配置 vars: { //locale: "zh-cn" /* var lang = require("./i18n/{locale}.js"); //=> 加載的是 path/to/i18n/zh-cn.js */ }, // 映射配置 該配置可對(duì)模塊路徑進(jìn)行映射修改,可用于路徑轉(zhuǎn)換、在線調(diào)試等 map: [ //[".js", "-debug.js"] /* var a = require("./a"); //=> 加載的是 ./js/a-debug.js */ ], // 預(yù)加載項(xiàng) 在普通模塊加載前,提前加載并初始化好指定模塊 preload 中的配置,需要等到 use 時(shí)才加載 preload: ["seajs-css","seajs-text"], // 調(diào)試模式 值為 true 時(shí),加載器不會(huì)刪除動(dòng)態(tài)插入的 script 標(biāo)簽。插件也可以根據(jù) debug 配置,來(lái)決策 log 等信息的輸出 debug: true, // 文件編碼 獲取模塊文件時(shí), 一樣,會(huì)相對(duì)當(dāng)前頁(yè)面解析 use用來(lái)在頁(yè)面中加載一個(gè)或多個(gè)模塊。seajs.use 理論上只用于加載啟動(dòng),不應(yīng)該出現(xiàn)在 define 中的模塊代碼里。在模塊代碼里需要異步加載其他模塊時(shí),推薦使用 require.async 方法。
// 加載一個(gè)模塊 seajs.use("./a"); // 加載一個(gè)模塊,在加載完成時(shí),執(zhí)行回調(diào) seajs.use("./a", function(a) { a.doSomething(); }); // 加載多個(gè)模塊,在加載完成時(shí),執(zhí)行回調(diào) seajs.use(["./a", "./b"], function(a, b) { a.doSomething(); b.doSomething(); });define// 方式一 define(function(require, exports, module) { // 模塊代碼 var a = require("a") }); // 方式二,此方法嚴(yán)格來(lái)說(shuō)不屬于CMD規(guī)范 define( "module", ["module1", "module2"], function( require, exports, module ){ // 模塊代碼 }); // 如果模塊內(nèi)容僅是對(duì)象或者字符串 define({ "foo": "bar" }); define("I am a template. My name is {{name}}.");requirerequire 是一個(gè)方法,接受 模塊標(biāo)識(shí)作為唯一參數(shù),用來(lái)獲取其他模塊提供的接口。
同步執(zhí)行此方式,require 的參數(shù)值 必須 是字符串直接量。
var a = require("./a");異步回調(diào)執(zhí)行require.async 方法用來(lái)在模塊內(nèi)部異步加載模塊,并在加載完成后執(zhí)行指定回調(diào)。callback 參數(shù)可選。
此時(shí),參數(shù)值可以是動(dòng)態(tài)的,以實(shí)現(xiàn)動(dòng)態(tài)加載。define(function(require, exports, module) { // 異步加載一個(gè)模塊,在加載完成時(shí),執(zhí)行回調(diào) require.async("./b", function(b) { b.doSomething(); }); // 異步加載多個(gè)模塊,在加載完成時(shí),執(zhí)行回調(diào) require.async(["./c", "./d"], function(c, d) { c.doSomething(); d.doSomething(); }); });require.resolve 使用模塊系統(tǒng)內(nèi)部的路徑解析機(jī)制來(lái)解析并返回模塊絕對(duì)路徑。
define(function(require, exports) { console.log(require.resolve("./b")); // ==> http://example.com/path/to/b.js });exportsexports 是一個(gè)對(duì)象,用來(lái)向外提供模塊接口,也可以使用return或者module.exports來(lái)進(jìn)行導(dǎo)出
define(function(require, exports) { // 對(duì)外提供 foo 屬性 exports.foo = "bar"; // return return { foo: "bar", doSomething: function() {} }; // module.exports module.exports = { foo: "bar", doSomething: function() {} }; });modulemodule 是一個(gè)對(duì)象,上面存儲(chǔ)了與當(dāng)前模塊相關(guān)聯(lián)的一些屬性和方法。
module.id 模塊的唯一標(biāo)識(shí)
module.uri 根據(jù)模塊系統(tǒng)的路徑解析規(guī)則得到的模塊絕對(duì)路徑
module.dependencies 表示當(dāng)前模塊的依賴(lài)
module.exports 當(dāng)前模塊對(duì)外提供的接口
其他 插件seajs-css
seajs-preload
seajs-text
seajs-style
seajs-combo
seajs-flush
seajs-debug
seajs-log
seajs-health
文檔:官方文檔
ESModule 簡(jiǎn)介在 ES6 之前,社區(qū)制定了一些模塊加載方案,最主要的有 CommonJS 和 AMD 兩種。前者用于服務(wù)器,后者用于瀏覽器。ES6 在語(yǔ)言標(biāo)準(zhǔn)的層面上,實(shí)現(xiàn)了模塊功能,而且實(shí)現(xiàn)得相當(dāng)簡(jiǎn)單,完全可以取代 CommonJS 和 AMD 規(guī)范,成為瀏覽器和服務(wù)器通用的模塊解決方案。
嚴(yán)格模式ES6 的模塊自動(dòng)采用嚴(yán)格模式,不管你有沒(méi)有在模塊頭部加上"use strict";。
嚴(yán)格模式主要有以下限制:
變量必須聲明后再使用
函數(shù)的參數(shù)不能有同名屬性,否則報(bào)錯(cuò)
不能使用with語(yǔ)句
不能對(duì)只讀屬性賦值,否則報(bào)錯(cuò)
不能使用前綴 0 表示八進(jìn)制數(shù),否則報(bào)錯(cuò)
不能刪除不可刪除的屬性,否則報(bào)錯(cuò)
不能刪除變量delete prop,會(huì)報(bào)錯(cuò),只能刪除屬性delete global[prop]
eval不會(huì)在它的外層作用域引入變量
eval和arguments不能被重新賦值
arguments不會(huì)自動(dòng)反映函數(shù)參數(shù)的變化
不能使用arguments.callee
不能使用arguments.caller
禁止this指向全局對(duì)象
不能使用fn.caller和fn.arguments獲取函數(shù)調(diào)用的堆棧
增加了保留字(比如protected、static和interface)
export 命令定義模塊的對(duì)外接口。
一個(gè)模塊就是一個(gè)獨(dú)立的文件。該文件內(nèi)部的所有變量,外部無(wú)法獲取。如果你希望外部能夠讀取模塊內(nèi)部的某個(gè)變量,就必須使用export關(guān)鍵字輸出該變量。
以下是幾種用法://------輸出變量------ export var firstName = "Michael"; export var lastName = "Jackson"; //等價(jià)于 var firstName = "Michael"; export {firstName}; //推薦,能清除知道輸出了哪些變量 //------輸出函數(shù)或類(lèi)------ export function multiply(x, y) { return x * y; }; //------輸出并as重命名------ var v1 = "Michael"; function v2() { ... } export { v1 as streamV1, v2 as streamV2 }; //------輸出default------ export default function () { ... }注意:export default在一個(gè)模塊中只能有一個(gè)。
import 命令使用export命令定義了模塊的對(duì)外接口以后,其他 JS 文件就可以通過(guò)import命令加載這個(gè)模塊。
以下是幾種用法,必須和上面的export對(duì)應(yīng)://------加載變量、函數(shù)或類(lèi)------ import {firstName, lastName} from "./profile.js"; //------加載并as重命名------ import { lastName as surname } from "./profile.js"; //------加載有default輸出的模塊------ import v1 from "./profile.js"; //------執(zhí)行所加載的模塊------ import "lodash"; //------加載模塊所有輸出------ import * as surname from "./profile.js";復(fù)合寫(xiě)法如果在一個(gè)模塊之中,先輸入后輸出同一個(gè)模塊,import語(yǔ)句可以與export語(yǔ)句寫(xiě)在一起。
export { foo, bar } from "my_module"; // 等同于 import { foo, bar } from "my_module"; export { foo, bar };不完美只能出現(xiàn)在模塊頂層,不能在其他語(yǔ)句中
無(wú)法動(dòng)態(tài)加載,其實(shí)這點(diǎn)主要是為了保證靜態(tài)分析,所有的模塊都要在解析階段確定它的依賴(lài)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105832.html
摘要:隨著前端的發(fā)展,異步這個(gè)詞真是越來(lái)越常見(jiàn)了。真正帶來(lái)革命性改變的是規(guī)范。借助,我們可以這樣完成異步任務(wù)好棒寫(xiě)起來(lái)像同步處理的函數(shù)一樣別著急,少年??偨Y(jié)以上就是筆者總結(jié)的幾種異步編程模式。 隨著前端的發(fā)展,異步這個(gè)詞真是越來(lái)越常見(jiàn)了。假設(shè)我們現(xiàn)在有這么一個(gè)異步任務(wù): 向服務(wù)器發(fā)起數(shù)次請(qǐng)求,每次請(qǐng)求的結(jié)果作為下次請(qǐng)求的參數(shù)。 來(lái)看看我們都有哪些處理方法: Callbacks ...
摘要:大潮來(lái)襲前端開(kāi)發(fā)能做些什么去年谷歌和火狐針對(duì)提出了的標(biāo)準(zhǔn),顧名思義,即的體驗(yàn)方式,我們可以戴著頭顯享受沉浸式的網(wǎng)頁(yè),新的標(biāo)準(zhǔn)讓我們可以使用語(yǔ)言來(lái)開(kāi)發(fā)。 VR 大潮來(lái)襲 --- 前端開(kāi)發(fā)能做些什么 去年谷歌和火狐針對(duì) WebVR 提出了 WebVR API 的標(biāo)準(zhǔn),顧名思義,WebVR 即 web + VR 的體驗(yàn)方式,我們可以戴著頭顯享受沉浸式的網(wǎng)頁(yè),新的 API 標(biāo)準(zhǔn)讓我們可以使用 ...
摘要:不同于其它靜態(tài)編程語(yǔ)言,實(shí)現(xiàn)組合模式的難點(diǎn)是保持樹(shù)對(duì)象與葉對(duì)象之間接口保持統(tǒng)一,可借助定制接口規(guī)范,實(shí)現(xiàn)類(lèi)型約束。誤區(qū)規(guī)避組合不是繼承,樹(shù)葉對(duì)象并不是父子對(duì)象組合模式的樹(shù)型結(jié)構(gòu)是一種聚合的關(guān)系,而不是。 showImg(https://segmentfault.com/img/bVbu79V?w=800&h=600); 組合模式:又叫 部分整體 模式,將對(duì)象組合成樹(shù)形結(jié)構(gòu),以表示 部分...
摘要:權(quán)威指南第六版關(guān)于閉包的說(shuō)明采用詞法作用域,也就是說(shuō)函數(shù)的執(zhí)行依賴(lài)于變量的作用域,這個(gè)作用域是在函數(shù)定義時(shí)決定的,而不是函數(shù)調(diào)用時(shí)決定的。閉包這個(gè)術(shù)語(yǔ)的來(lái)源指函數(shù)變量可以被隱藏于作用域鏈之內(nèi),因此看起來(lái)是函數(shù)將變量包裹了起來(lái)。 最近打算換工作,所以參加了幾次面試(國(guó)內(nèi)比較知名的幾家互聯(lián)網(wǎng)公司)。在面試的過(guò)程中每當(dāng)被問(wèn)起閉包,我都會(huì)說(shuō)閉包是作用域的問(wèn)題?令人驚訝的是幾乎無(wú)一例外的當(dāng)我提到...
摘要:原型繼承基本模式這種是最簡(jiǎn)單實(shí)現(xiàn)原型繼承的方法,直接把父類(lèi)的對(duì)象賦值給子類(lèi)構(gòu)造函數(shù)的原型,這樣子類(lèi)的對(duì)象就可以訪問(wèn)到父類(lèi)以及父類(lèi)構(gòu)造函數(shù)的中的屬性。 真正意義上來(lái)說(shuō)Javascript并不是一門(mén)面向?qū)ο蟮恼Z(yǔ)言,沒(méi)有提供傳統(tǒng)的繼承方式,但是它提供了一種原型繼承的方式,利用自身提供的原型屬性來(lái)實(shí)現(xiàn)繼承。Javascript原型繼承是一個(gè)被說(shuō)爛掉了的話題,但是自己對(duì)于這個(gè)問(wèn)題一直沒(méi)有徹底理解...
閱讀 1386·2021-10-13 09:39
閱讀 1342·2021-09-23 11:22
閱讀 2252·2019-08-30 14:05
閱讀 1069·2019-08-29 17:03
閱讀 785·2019-08-29 16:24
閱讀 2235·2019-08-29 13:51
閱讀 663·2019-08-29 13:00
閱讀 1316·2019-08-29 11:24