亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

讀懂CommonJS的模塊加載

2024-05-06 16:50:09
字體:
來源:轉載
供稿:網友

叨叨一會CommonJS

Common這個英文單詞的意思,相信大家都認識,我記得有一個詞組common knowledge是常識的意思,那么CommonJS是不是也是類似于常識性的,大家都理解的意思呢?很明顯不是,這個常識一點都不常識。我最初認為commonJS是一個開源的JS庫,就是那種非常方便用的庫,里面都是一些常用的前端方法,然而我錯得離譜,CommonJS不僅不是一個庫,還是一個看不見摸不著的東西,他只是一個規范!就像校紀校規一樣,用來規范JS編程,束縛住前端們。就和Promise一樣是一個規范,雖然有許多實現這些規范的開源庫,但是這個規范也是可以依靠我們的JS能力實現的。

CommonJs規范

那么CommonJS規范了些什么呢?要解釋這個規范,就要從JS的特性說起了。JS是一種直譯式腳本語言,也就是一邊編譯一邊運行,所以沒有模塊的概念。因此CommonJS是為了完善JS在這方面的缺失而存在的一種規范。

CommonJS定義了兩個主要概念:

  1. require函數,用于導入模塊
  2. module.exports變量,用于導出模塊

然而這兩個關鍵字,瀏覽器都不支持,所以我認為這是為什么瀏覽器不支持CommonJS的原因。如果一定腰在瀏覽器上使用CommonJs,那么就需要一些編譯庫,比如browserify來幫助哦我們將CommonJs編譯成瀏覽器支持的語法,其實就是實現require和exports。

那么CommonJS可以用于那些方面呢?雖然CommonJS不能再瀏覽器中直接使用,但是nodejs可以基于CommonJS規范而實現的,親兒子的感覺。在nodejs中我們就可以直接使用require和exports這兩個關鍵詞來實現模塊的導入和導出。

Nodejs中CommomJS模塊的實現

require

導入,代碼很簡單,let {count,addCount}=require("./utils")就可以了。那么在導入的時候發生了些什么呢??首先肯定是解析路徑,系統給我們解析出一個絕對路徑,我們寫的相對對路徑是給我們看的,絕對路徑是給系統看的,畢竟絕對路徑辣么長,看著很費力,尤其是當我們的的項目在N個文件夾之下的時候。所以require第一件事就是解析路徑。我們可以寫的很簡潔,只需要寫出相對路徑和文件名即可,連后綴都可以省略,讓require幫我們去匹配去尋找。也就是說require的第一步是解析路徑獲取到模塊內容:

如果是核心模塊,比如fs,就直接返回模塊

如果是帶有路徑的如/,./等等,則拼接出一個絕對路徑,然后先讀取緩存require.cache再讀取文件。如果沒有加后綴,則自動加后綴然后一一識別。

  1. .js 解析為JavaScript 文本文件
  2. .json解析JSON對象
  3. .node解析為二進制插件模塊

首次加載后的模塊會緩存在require.cache之中,所以多次加載require,得到的對象是同一個。

在執行模塊代碼的時候,會將模塊包裝成如下模式,以便于作用域在模塊范圍之內。

(function(exports, require, module, __filename, __dirname) {// 模塊的代碼實際上在這里});

nodejs官方給出的解釋,大家可以參考下

module

說完了require做了些什么事,那么require觸發的module做了些什么呢?我們看看用法,先寫一個簡單的導出模塊,寫好了模塊之后,只需要把需要導出的參數,加入module.exports就可以了。

 

let count=0function addCount(){  count++}module.exports={count,addCount}

然后根據require執行代碼時需要加上的,那么實際上我們的代碼長成這樣:

(function(exports, require, module, __filename, __dirname) {  let count=0  function addCount(){    count++  }  module.exports={count,addCount}});

require的時候究竟module發生了什么,我們可以在vscode打斷點:

CommonJS,模塊加載

根據這個斷點,我們可以整理出:

黃色圈出來的時require,也就是我們調用的方法

紅色圈出來的時Module的工作內容

Module._compileModule.extesions..jsModule.loadtryMouduleLoadModule._loadModule.runMain

藍色圈出來的是nodejs干的事,也就是NativeModule,用于執行module對象的。

我們都知道在JS中,函數的調用時棧stack的方式,也就是先近后出,也就是說require這個函數觸發之后,圖中的運行時從下到上運行的。也就是藍色框最先運行。我把他的部分代碼扒出來,研究研究。

NativeModule原生代碼關鍵代碼,這一塊用于封裝模塊的。

NativeModule.wrap = function(script) {  return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];};NativeModule.wrapper = [  '(function (exports, require, module, __filename, __dirname) { ',  '/n});'];

等NativeModule觸發Module.runMain之后,我們的模塊加載開始了,我們按照從下至上的順序來解讀吧。

Module._load,就是新建一個module對象,然后將這個新對象放入Module緩存之中。

var module = new Module(filename, parent);Module._cache[filename] = module;

tryMouduleLoad,然后就是新建的module對象開始解析導入的模塊內容

 module.load(filename);

新建的module對象繼承了Module.load,這個方法就是解析文件的類型,然后分門別類地執行

Module.extesions..js這就干了兩件事,讀取文件,然后準備編譯

Module._compile終于到了編譯的環節,那么JS怎么運行文本?將文本變成可執行對象,js有3種方法:

eval方法eval("console.log('aaa')")

new Function() 模板引擎

let str="console.log(a)"new Function("aaa",str)

node執行字符串,我們用高級的vm

let vm=require("vm")let a='console.log("a")'vm.runInThisContext(a)

這里Module用vm的方式編譯,首先是封裝一下,然后再執行,最后返回給require,我們就可以獲得執行的結果了。

var wrapper = Module.wrap(content);var compiledWrapper = vm.runInThisContext(wrapper, {  filename: filename,  lineOffset: 0,  displayErrors: true});

因為所有的模塊都是封裝之后再執行的,也就說導入的這個模塊,我們只能根據module.exports這一個對外接口來訪問內容。

總結一下

這些代碼看的人真的很暈,其實主要流程就是require之后解析路徑,然后觸發Module這一個類,然后Module的_load的方法就是在當前模塊中創建一個新module的緩存,以保證下一次再require的時候可以直接返回而不用再次執行。然后就是這個新module的load方法載入并通過VM執行代碼返回對象給require。

正因為是這樣編譯運行之后賦值給的緩存,所以如果export的值是一個參數,而不是函數,那么如果當前參數的數值改變并不會引起export的改變,因為這個賦予export的參數是靜態的,并不會引起二次運行。

CommonJs模塊和ES6模塊的區別

使用場景

CommonJS因為關鍵字的局限性,因此大多用于服務器端。而ES6的模塊加載,已經有瀏覽器支持了這個特性,因此ES6可以用于瀏覽器,如果遇到不支持ES6語法的瀏覽器,可以選擇轉譯成ES5。

語法差異

ES6也是一種JavaScript的規范,它和CommonJs模塊的區別,顯而易見,首先代碼就不一樣,ES6的導入導出很直觀import和export。

 

  commonJS ES6
支持的關鍵字 arguments,require,module,exports,__filename,__dirname import,export
導入 const path=require("path") import path from "path"
導出 module.exports = APP; export default APP
導入的對象 隨意修改 不能隨意修改
導入次數 可以隨意require,但是除了第一次,之后都是從模塊緩存中取得 在頭部導入

 

** 大家注意了!劃重點!nodejs是CommonJS的親兒子,所以有些ES6的特性并不支持,比如ES6對于模塊的關鍵字import和export,如果大家在nodejs環境下運行,就等著大紅的報錯吧~**

加載差異

除了語法上的差異,他們引用的模塊性質是不一樣的。雖然都是模塊,但是這模塊的結構差異很大。

在ES6中,如果大家想要在瀏覽器中測試,可以用以下代碼:

//utils.jsconst x = 1;export default x
<script type="module">  import x from './utils.js';  console.log(x);  export default x</script>

首先要給script一個type="module"表明這里面是ES6的模塊,而且這個標簽默認是異步加載,也就是頁面全部加載完成之后再執行,沒有這個標簽的話代碼不然無法運行哦。然后就可以直接寫import和export了。

ES6模塊導入的幾個問題:

  1. 相同的模塊只能引入一次,比如x已經導入了,就不能再從utils中導入x
  2. 不同的模塊引入相同的模塊,這個模塊只會在首次import中執行。
  3. 引入的模塊就是一個值的引用,并且是動態的,改變之后其他的相關值也會變化
  4. 引入的對象不可隨意斬斷鏈接,比如我引入的count我就不能修改他的值,因為這個是導入進來的,想要修改只能在count所在的模塊修改。但是如果count是一個對象,那么可以改變對象的屬性,比如count.one=1,但是不可以count={one:1}。

大家可以看這個例子,我寫了一個改變object值的小測試,大家會發現utils.js中的count初始值應該是0,但是運行了addCount所以count的值動態變化了,因此count的值變成了2。

let count=0function addCount(){  count=count+2}export {count,addCount}
<script type="module">  import {count,addCount} from './utils.js';  //count=4//不可修改,會報錯  addCount()  console.log(count);</script>

與之對比的是commonJS的模塊引用,他的特性是:

上一節已經解釋了,模塊導出的固定值就是固定值,不會因為后期的修改而改變,除非不導出靜態值,而改成函數,每次調用都去動態調用,那么每次值都是最新的了。
導入的對象可以隨意修改,相當于只是導入模塊中的一個副本。

如果想要深入研究,大家可以參考下阮老師的ES6入門——Module 的加載實現。

CommonJS模塊總結

CommonJS模塊只能運行再支持此規范的環境之中,nodejs是基于CommonJS規范開發的,因此可以很完美地運行CommonJS模塊,然后nodejs不支持ES6的模塊規范,所以nodejs的服務器開發大家一般使用CommonJS規范來寫。

CommonJS模塊導入用require,導出用module.exports。導出的對象需注意,如果是靜態值,而且非常量,后期可能會有所改動的,請使用函數動態獲取,否則無法獲取修改值。導入的參數,是可以隨意改動的,所以大家使用時要小心。

以上所述是小編給大家介紹的CommonJS的模塊加載詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對VeVb武林網網站的支持!


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美日韩在线第一页| 成人午夜激情网| 亚洲欧洲国产伦综合| 亚洲精品免费一区二区三区| 精品久久久久久中文字幕大豆网| 日韩精品在线观看网站| 国产盗摄xxxx视频xxx69| 国产热re99久久6国产精品| 国产精品成人国产乱一区| 国产日韩在线看片| 亚洲精品国产精品国自产观看浪潮| 18久久久久久| 欧美一级淫片aaaaaaa视频| 色妞一区二区三区| 久久久久久久激情视频| 日韩中文字幕欧美| 国产女人18毛片水18精品| 成人97在线观看视频| 国产精品亚洲第一区| 久久99亚洲热视| 日韩欧美精品网站| 亚洲精品一区中文字幕乱码| 亚洲性69xxxbbb| 欧美精品在线视频观看| 国产亚洲视频在线观看| 国产精品久久久久久影视| 中文字幕精品www乱入免费视频| 亚洲欧美国产精品| 91在线观看免费观看| 日韩中文字幕免费看| 久久久久久久久久亚洲| 自拍偷拍亚洲在线| 久久久日本电影| 亚洲乱码国产乱码精品精天堂| 亚洲自拍偷拍色图| 日韩精品一区二区三区第95| 日韩精品福利在线| 欧美精品18videos性欧美| 久久6免费高清热精品| 亚洲精品国产精品国自产在线| 久久久久久久久久久人体| 色婷婷**av毛片一区| 日韩成人在线电影网| 国产极品精品在线观看| 亚洲天堂av在线免费观看| 一道本无吗dⅴd在线播放一区| 国色天香2019中文字幕在线观看| 日韩国产精品一区| 国产mv久久久| 亚洲国产成人久久综合| 国产综合久久久久| 久久综合电影一区| 精品动漫一区二区三区| 国产97在线|日韩| 国产精品久久久av| 川上优av一区二区线观看| 色妞一区二区三区| 国产中文字幕91| 久久久精品一区二区三区| 久久精品中文字幕一区| 韩国19禁主播vip福利视频| 久久亚洲精品视频| 国产精品久久久久久久电影| 91免费看视频.| 久久久久久国产精品美女| 成人精品一区二区三区| 92福利视频午夜1000合集在线观看| 欧美限制级电影在线观看| 国产精品成人免费电影| 亚洲第一网站男人都懂| 午夜精品三级视频福利| 欧美日韩中文字幕在线视频| 久久久视频免费观看| 日韩暖暖在线视频| 国产精品91久久久久久| 日韩美女免费观看| 欧美精品video| 欧美性xxxxx极品| 这里只有精品丝袜| 日韩av网站电影| 久久久久五月天| 亚洲深夜福利在线| 亚洲美女在线观看| 欧美一级片久久久久久久| 亚洲国产中文字幕久久网| 久久久国产视频| 成人久久久久久久| 69视频在线免费观看| 亚洲视频第一页| 亚洲色图欧美制服丝袜另类第一页| 国产精品第三页| 美女福利视频一区| 久久精品国产91精品亚洲| 亚洲另类xxxx| 亚洲人高潮女人毛茸茸| 国产一区二区三区三区在线观看| 亚洲精品国偷自产在线99热| 清纯唯美亚洲综合| 精品国产一区二区三区久久狼5月| 日本aⅴ大伊香蕉精品视频| 亚洲精品美女在线观看播放| 国产精品视频免费观看www| 91天堂在线视频| 国产日韩在线播放| 色偷偷偷综合中文字幕;dd| 国产视频丨精品|在线观看| 国内精品模特av私拍在线观看| 国内精品美女av在线播放| 欧美黄色片在线观看| 亚洲欧美一区二区三区四区| 亚洲天堂av电影| 亚洲国内精品在线| 在线观看免费高清视频97| 亚洲人线精品午夜| 国产精品老女人精品视频| 亚洲欧美另类人妖| 国产精品久久一区主播| 日韩美女中文字幕| 国产欧美一区二区三区在线| 成人写真福利网| 精品在线小视频| 欧美一级淫片videoshd| 亚洲japanese制服美女| 亚洲激情视频在线观看| zzijzzij亚洲日本成熟少妇| 欧美伦理91i| 九九热视频这里只有精品| 亚洲sss综合天堂久久| 538国产精品一区二区在线| 91欧美激情另类亚洲| 精品一区二区三区电影| 久久6免费高清热精品| 日韩免费av一区二区| 中文国产成人精品| 欧美精品一区二区三区国产精品| 黄色一区二区三区| 亚洲国产日韩欧美在线动漫| 日韩精品一区二区三区第95| 在线观看91久久久久久| 97精品伊人久久久大香线蕉| 美女啪啪无遮挡免费久久网站| 欧美日韩国产一中文字不卡| 亚洲国产精品va在线看黑人动漫| 亚洲iv一区二区三区| 91在线观看免费网站| 欧美尤物巨大精品爽| 久久免费福利视频| 成人黄色av网站| 久久久久久久999| 深夜福利91大全| 91av免费观看91av精品在线| 国产精品白丝av嫩草影院| 国产精品露脸自拍| 欧美日韩成人在线视频| 成人免费直播live| 久久久精品视频在线观看| 午夜免费日韩视频| 国产91色在线|免| 国产精品欧美一区二区| 一本色道久久88精品综合| 欧美一区二区三区……| 97免费视频在线| 亚洲成人xxx| 日韩高清av在线|