摘要:重要的模塊化規(guī)范有幾個,模塊機(jī)制,,。模塊化的目的在于營造安全封閉的作用域且具有易于引用接口,按我的理解可分為模塊定義模塊引入兩部分。它的定義如下模塊標(biāo)識符模塊對外輸出的值調(diào)用該模塊的模塊。在中,有一部分模塊由提供,稱之為核心模塊。
重要的模塊化規(guī)范有幾個:commonjs,ES6模塊機(jī)制,AMD,CMD。由于業(yè)務(wù)中一直接觸的都是Vue+webpack+babel架構(gòu)的項目,在封裝代碼時用的比較的多還是ES6規(guī)范,對其他模塊化規(guī)范不熟悉,因此在這里記錄一下學(xué)習(xí)過的模塊化知識。
CommonJS模塊化的目的在于營造安全封閉的作用域、且具有易于引用接口,按我的理解可分為模塊定義、模塊引入兩部分。
在模塊中存在著一個module對象,它代表著模塊本身,將需要導(dǎo)出的api掛載于其中的exports屬性上即可以定義導(dǎo)出的接口;CommonJS規(guī)范中存在require()方法,用于接受模塊標(biāo)識,引入某個模塊到當(dāng)前的上下文。
1. 模塊定義要理解模塊如何定義,那必須要先理解module對象。在Node中,每一個文件模塊都是一個對象,即module對象。它的定義如下:
function Module(id, parent){ this.id = id //模塊標(biāo)識符 this.exports = {} //模塊對外輸出的值 this.parent = parent //調(diào)用該模塊的模塊。parent為null時意味著模塊為入口模塊 if(parent && parent.children){ parent.children.push(this) } this.filename = filename //文件名 this.loaded = false //是否已加載 this.children = [] //表示該模塊調(diào)用的其他模塊 }
定義模塊的目的其實在于定義輸出的值。寫法非常簡單,舉個?
function sayHello(){ console.log("hello") } module.exports = sayHello //或exports.sayHello = sayHello
為了方便導(dǎo)出接口,Node還定義了一個exports變量,但有個容易踩的坑是,exports只是一個引用,本來指向module.exports,假如只是給exports變量賦值則exports變量會失去對module.exports的指向。說到底,必須對module.exports定義接口才能真正導(dǎo)出值。
先說解決方法,常見的寫法為:
exports = module.exports = sayHello //或嚴(yán)格地只給exports變量添加屬性 exports.sayHello = sayHello
再舉個例子說一下犯錯的具體場景:
//a.js exports.name = "kent" exports.sayHi = function(){ console.log("hi") } console.log(module)// { exports: { name: "kent", sayHi: function(){ console.log("hi") } } } //假如給exports重新賦值 =_= exports = { name: "nicolas", sayBye: function(){ console.log("bye") } } //module中的exports屬性不會有任何變化 console.log(module)// { exports: { name: "kent", sayHi: function(){ console.log("hi") } } } console.log(exports)// { name: "nicolas", sayBye: function(){ console.log("bye") } }
//b.js //因此require的時候讀取的name仍然為kent var person = require("a.js") console.log(person.name)//kent
具體的原因也可以從模塊機(jī)制中看出來
function require(...) { var module = { exports: {} }; ((module, exports) => { // Your module code here. In this example, define a function. function some_func() {}; exports = some_func; // At this point, exports is no longer a shortcut to module.exports, and // this module will still export an empty default object. module.exports = some_func; // At this point, the module will now export some_func, instead of the // default object. })(module, module.exports); return module.exports; }2. 模塊引入
模塊引入的語法也非常簡單。上一節(jié)也簡單提過。這里再舉個?
//book.js exports.name = "javascript" exports.logName = function(){ console.log("javascript") }
//main.js var book = require("./book.js")//require的參數(shù)即模塊標(biāo)識符 console.log(book.name)//"javascript" book.logName()//"javascript"
下面詳情談?wù)勀K引入經(jīng)歷哪些步驟。但在此之前需要先了解兩個概念:核心模塊與文件模塊。
在Node中,有一部分模塊由Node提供,稱之為核心模塊。在Node進(jìn)程啟動的時候,核心模塊就直接加載至內(nèi)存中。因此引入核心模塊只需要走路徑分析一個步驟,其加載速度最快。
另一部分則是運行時動態(tài)加載,常見的有用戶定義帶路徑標(biāo)識符的模塊,或自定義模塊(如三方提供的包)。這類模塊需要完整地走完以下三個步驟:路徑分析、文件定位與編譯執(zhí)行。
①路徑分析:
路徑分析可以理解為模塊標(biāo)識符的分析。模塊標(biāo)識符在Node中主要有:
·核心模塊,如:http, fs等等; ·以"./"或"../"開頭的相對路徑模塊,相對于當(dāng)前的目錄位置; ·以"/"開頭的絕對路徑模塊; ·非路徑形式的文件模塊,與核心模塊的標(biāo)識符類似。Node會搜索各級的node_modules目錄。
· 核心模塊:核心模塊經(jīng)過路徑分析之后會直接加載。需要注意的是,自定義的文件模塊不能與核心模塊標(biāo)識符相同,要不更換不同的標(biāo)識符要么使用相對路徑或絕對路徑標(biāo)識符。
· 路徑形式的文件模塊:在分析文件模塊的時候,require方法將會把路徑轉(zhuǎn)換為真實路徑并以此為索引編譯模塊并存放到緩存中(緩存加載將在下文介紹)。
· 非路徑形式的文件模塊(自定義模塊):自定義模塊的路徑分析在我們引用三方庫的時候經(jīng)常會碰到。這類非路徑形式的文件模塊加載時將會以模塊路徑為線索逐級搜索。舉個? :
//在"/Users/zhazheng/Documents/www"下新建一個module_path.js //module_path.js console.log(module.paths) //再執(zhí)行module_path.js node module_path //得出以下log [ "/Users/zhazheng/Documents/www/node_modules", "/Users/zhazheng/Documents/node_modules", "/Users/zhazheng/node_modules", "/Users/node_modules", "/node_modules" ]
可見,這類模塊會從當(dāng)前文件目錄往上逐級遞歸直到根目錄下的node_modules目錄。因此這類模塊的路徑分析是最費時的。
②文件定位:文件定位主要包括文件擴(kuò)展名分析、目錄和包的處理。
·文件擴(kuò)展名分析:分析標(biāo)識符的過程中出現(xiàn)不包含文件擴(kuò)展名的情況非常常見。在標(biāo)識符不包含文件擴(kuò)展名的情況下,Node會依次嘗試以下三種擴(kuò)展名:.js、.json、.node。由于嘗試解析的過程是同步阻塞進(jìn)行的,因此大量的分析文件擴(kuò)展名會產(chǎn)生性能問題,這種情況下可以嘗試添加擴(kuò)展名或充分利用緩存加載的優(yōu)勢。
·目錄分析與包的處理: 假如分析完擴(kuò)展名后仍然沒有找到對應(yīng)的文件而只得出一個目錄,那么Node會將此目錄當(dāng)做一個包來處理。首先會查找當(dāng)前目錄下是否有package.json文件,假如有則檢查是否具有main屬性(main屬性即指向入口文件)。假如沒有package.json文件或package.json中不具備main屬性,那么Node則按index為默認(rèn)的文件名,最后再重復(fù)“文件擴(kuò)展名分析”這個步驟。
3.緩存加載事實上Node的模塊,無論是核心模塊還是文件模塊,第一次加載之后都會被緩存。require()方法將會對二次加載的模塊進(jìn)行緩存。因此假如有多次加載模塊的需求,那么就需要記得先從緩存中刪除模塊。
緩存均保存在require.cache對象中,需要刪除單個模塊或全部模塊的緩存可以這樣寫:
//刪除單個模塊緩存 delete require.cache[moduleName] //刪除全部模塊緩存 Object.getOwnPropertyNames(require.cache).forEach(key => { delete require.cache[key] })
當(dāng)然,一般情況下緩存是可以帶來性能優(yōu)勢的。對于路徑套得非常深的自定義文件模塊來說尤甚。
4.循環(huán)加載循環(huán)加載是避免不了的問題。在Node中需要了解一下循環(huán)加載的表現(xiàn)。首先要理解的是,require是一個同步加載的過程,讀取的接口僅僅是指向exports對象中的屬性,舉個? :(以下三個模塊均在同一目錄下)
//a.js exports.name = "a1" console.log(`a.js, ${require("./b.js").name}`) exports.name = "a2"
//b.js exports.name = "b1" console.log(`b.js, ${require("./a.js").name}`) exports.name = "b2"
//main.js console.log(`main.js, ${require("./a.js").name}`) console.log(`main.js, ${require("./b.js").name}`)
nvm run node然后.load main.js得出以下的結(jié)果
b.js, a1 a.js, b2//這兩行結(jié)果應(yīng)該大致可以理解兩個模塊的require方法發(fā)生了什么 main.js a2 main.js b2
再次執(zhí)行.load main.js會讀取緩存結(jié)果
main.js a2 main.js b2
循環(huán)加載示例代碼可到我的github查看
AMD...未完待續(xù)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/91938.html
摘要:容器自動完成裝載,默認(rèn)的方式是這部分重點在常用模塊的使用以及的底層實現(xiàn)原理。 對于那些想面試高級 Java 崗位的同學(xué)來說,除了算法屬于比較「天方夜譚」的題目外,剩下針對實際工作的題目就屬于真正的本事了,熱門技術(shù)的細(xì)節(jié)和難點成為了主要考察的內(nèi)容。 這里說「天方夜譚」并不是說算法沒用,不切實際,而是想說算法平時其實很少用到,甚至面試官都對自己出的算法題一知半解。 這里總結(jié)打磨了 70 道...
摘要:原文地址游客前言金三銀四,很多同學(xué)心里大概都準(zhǔn)備著年后找工作或者跳槽。最近有很多同學(xué)都在交流群里求大廠面試題。 最近整理了一波面試題,包括安卓JAVA方面的,目前大廠還是以安卓源碼,算法,以及數(shù)據(jù)結(jié)構(gòu)為主,有一些中小型公司也會問到混合開發(fā)的知識,至于我為什么傾向于混合開發(fā),我的一句話就是走上編程之路,將來你要學(xué)不僅僅是這些,豐富自己方能與世接軌,做好全棧的裝備。 原文地址:游客kutd...
摘要:當(dāng)你真正到公司里面從事了幾年開發(fā)之后,你就會同意我的說法利用找工作,需要的就是項目經(jīng)驗,項目經(jīng)驗就是理解項目開發(fā)的基本過程,理解項目的分析方法,理解項目的設(shè)計思 Java就是用來做項目的!Java的主要應(yīng)用領(lǐng)域就是企業(yè)級的項目開發(fā)!要想從事企業(yè)級的項目開發(fā),你必須掌握如下要點: 1、掌握項目開發(fā)的基本步驟 2、具備極強(qiáng)的面向?qū)ο蟮姆治雠c設(shè)計技巧 3、掌握用例驅(qū)動、以架構(gòu)為核心的主流開發(fā)...
摘要:中的有多重意義。它可能是一個構(gòu)造器,承擔(dān)起對象模板的作用可能是對象的方法,負(fù)責(zé)向?qū)ο蟀l(fā)送消息。語義匿名函數(shù)處理某些特殊效果如處理一些數(shù)據(jù)又不想暴露過多的變量判斷版本的方式最終只要一個結(jié)果,匿名函數(shù)內(nèi)部用到了一些局部變量全部可以隔離開。 JavaScript 中的 function 有多重意義。它可能是一個構(gòu)造器(constructor),承擔(dān)起對象模板的作用; 可能是對象的方法(met...
閱讀 3782·2021-09-22 15:49
閱讀 3321·2021-09-08 09:35
閱讀 1431·2019-08-30 15:55
閱讀 2333·2019-08-30 15:44
閱讀 726·2019-08-29 16:59
閱讀 1610·2019-08-29 16:16
閱讀 492·2019-08-28 18:06
閱讀 904·2019-08-27 10:55