欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺析node.js的模塊加載機(jī)制

 更新時(shí)間:2018年05月25日 11:45:55   作者:tusi  
這篇文章主要介紹了淺析node.js的模塊加載機(jī)制,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

在node.js中,模塊使用CommonJS規(guī)范,一個(gè)文件是一個(gè)模塊

node.js中的模塊可分為三類

  1. 內(nèi)部模塊 - node.js提供的模塊如 fs,http,path等
  2. 自定模塊 - 我們自己寫的模塊
  3. 第三方模塊 - 通過npm安裝的模塊

node.js提供了大量的模塊供我們使用,比如 想解析一個(gè)文件的路徑,可以使用path模塊下的相應(yīng)方法實(shí)現(xiàn):

const path = require('path');
//返回目標(biāo)文件的絕對(duì)路徑
console.log(path.resolve('./1.txt'));

運(yùn)行結(jié)果:

/Users/cuiyue/workspace/test/1.txt

使用require引入相應(yīng)的模塊,即可使用。

__dirname和__filename

node.js的每個(gè)模塊都有這兩個(gè)參數(shù),它們都是一個(gè)絕對(duì)路徑的地址,區(qū)別是__filename存放了從根目錄到當(dāng)前文件名的路徑,__dirname只存放從根目錄到模塊的所在目錄:

console.log(__dirname);
console.log(__filename);

運(yùn)行結(jié)果:

/Users/cuiyue/workspace/test
/Users/cuiyue/workspace/test/module.js

vm模塊

vm模塊是node.js提供在V8虛擬機(jī)中編譯和運(yùn)行的工具,node.js中的模塊內(nèi)部實(shí)現(xiàn)就是通過此模塊完成。

說說vm的基本用法。

在js環(huán)境中有一個(gè)eval函數(shù),它可以運(yùn)行js的代碼字符串,比如:

eval('console.log("Hello javascript.")'); //輸出Hello javascript.

可以看到,eval函數(shù)的參數(shù)是一段字符串,它可以運(yùn)行字符串形式的js代碼,但它可以使用上下文環(huán)境中的變量:

var num=100;
eval('console.log(num)'); //輸出100

以上是可以正確訪問num的值。

vm模塊提供了方法創(chuàng)建一個(gè)安全的沙箱,在指定的上下文環(huán)境中運(yùn)行代碼,不受外界干擾。

const vm = require('vm');
var num = 100;
vm.runInThisContext('console.log(num)');

運(yùn)行結(jié)果:

console.log(num)
            ^
ReferenceError: num is not defined

可以看到代碼報(bào)錯(cuò)了,說明在vm創(chuàng)建了指定的上下文環(huán)境中,拿不到外界的參量。

CommonJS規(guī)范

在以前,由于javascript的歷史原因?qū)е滤哪K機(jī)制很差,由于這些缺點(diǎn)使得javascript不太善于開發(fā)大型應(yīng)用,于是提出了CommonJS規(guī)范以彌補(bǔ)javascript的不足。

CommonJS規(guī)范主要分為三塊內(nèi)容:模塊導(dǎo)入導(dǎo)出、模塊定義、模塊標(biāo)識(shí)。

模塊導(dǎo)入導(dǎo)出

CommonJS中使用require()函數(shù)進(jìn)行模塊的引入。

const mymodule = require('mymodule');

使用exports導(dǎo)出模塊

module.exports = {
  name: 'Tom'
};

引用的名稱可以不帶路徑,若不帶路徑表示引入的是node提供的模塊或是npm安裝的第三方模塊(node_modules)

模塊定義

module對(duì)象:在每一個(gè)模塊中,module對(duì)象代表該模塊自身。

export屬性:module對(duì)象的一個(gè)屬性,它向外提供接口。

模塊標(biāo)識(shí)

模塊標(biāo)識(shí)指的是傳遞給require方法的參數(shù),必須是符合小駝峰命名的字符串,或者以 .、..、開頭的相對(duì)路徑,或者絕對(duì)路徑。

node中模塊解析流程

  1. 首先接收參數(shù),把傳入的模塊名稱解析成絕對(duì)路徑
  2. 若沒有后綴名稱,依次拼接.js .json .node嘗試加載,仍到不到模塊則報(bào)錯(cuò)
  3. 取得正確的路徑后判斷緩存中是否存在此模塊,若有則取出
  4. 若緩存中不存在則加載此文件,在外包裹一層閉包并執(zhí)行它

以上為大致流程,下面嘗試著寫一下模塊。

代碼的基本結(jié)構(gòu):

/**
 * Module類,用于處理模塊加載
 */
function Module() {}

//模塊的緩存
Module._cacheModule = {};

//不同擴(kuò)展名的加載策略
Module._extensions = {};

//根據(jù)moduleId解析絕對(duì)路徑,
Module._resolveFileName = function(moduleId) {};

//入口函數(shù)
function req(moduleId) {}

附上全部代碼:

const path = require('path');
const fs = require('fs');
const vm = require('vm');

/**
 * Module類,用于處理模塊加載
 */
function Module(file) {
 this.id = file; //當(dāng)前模塊的id,它使用完整的絕對(duì)路徑標(biāo)識(shí),因此是唯一的
 this.exports = {}; //導(dǎo)出
 this.loaded = false; //模塊是否已加載完畢
}

//模塊的緩存
Module._cacheModule = {};

Module._wrapper = ['(function(exports,require,module,__dirname,__filename){', '});'];

//不同擴(kuò)展名的加載策略
Module._extensions = {
 '.js': function(currentModule) {
  let js = fs.readFileSync(currentModule.id, 'utf8'); //讀取出js文件內(nèi)容
  let fn = Module._wrapper[0] + js + Module._wrapper[1];
  vm.runInThisContext(fn).call(
   currentModule.exports,
   currentModule.exports,
   req,
   currentModule,
   path.dirname(currentModule.id),
   currentModule.id);
  return currentModule.exports;
 },
 '.json': function(currentModule) {
  let json = fs.readFileSync(currentModule.id, 'utf8');
  return JSON.parse(json); //轉(zhuǎn)換為JSON對(duì)象返回
 },
 '.node': ''
};

//加載模塊(實(shí)例方法)
Module.prototype.load = function(file) {
 let extname = path.extname(file); //獲取后綴名
 return Module._extensions[extname](this);
};

//根據(jù)moduleId解析絕對(duì)路徑,
Module._resolveFileName = function(moduleId) {
 let p = path.resolve(moduleId);

 if (!path.extname(moduleId)) { //傳入的模塊沒有后綴
  let arr = Object.keys(Module._extensions);

  //循環(huán)讀取不同擴(kuò)展名的文件
  for (var i = 0; i < arr.length; i++) {
   let file = p + arr[i]; //拼接上后綴名成為一個(gè)完整的路徑
   try {
    fs.accessSync(file);
    return file; //若此文件存在返回它
   } catch (e) {
    console.log(e);
   }
  }
 } else {
  return p;
 }
};

function req(moduleId) {
 let file = Module._resolveFileName(moduleId);

 if (Module._cacheModule[file]) { //若緩存中存在此模塊
  return Module._cacheModule[file];
 } else {
  let module = new Module(file);
  module.exports = module.load(file);
  return module.exports;
 }
}

console.log(req('./a.js')());

a.js的文件內(nèi)容:

module.exports = function() {
 console.log('This message from a.js');
 console.log(__dirname);
 console.log(__filename);
}

最終運(yùn)行結(jié)果:

This message from a.js
/Users/cuiyue/workspace/test
/Users/cuiyue/workspace/test/a.js

重要代碼說明

_resolveFileName

_resolveFileName方法的主要作用是把傳入的模塊解析成絕對(duì)路徑,這樣才可以進(jìn)行下一步,根據(jù)完整的路徑加載模塊。

因此要進(jìn)行判斷,如果傳入的模塊不存在,則要報(bào)錯(cuò);如果傳入的模塊已經(jīng)有擴(kuò)展名了,就不要拼接了;若沒有擴(kuò)展名,依次以.js .json .node的順序拼接成完成的模塊進(jìn)行加載。

_extensions

此對(duì)象中封裝了加載不同類型模塊的處理方法,其中若是.json類型則使用fs讀取文件直接轉(zhuǎn)換成JSON對(duì)象并返回。

若是.js文件則讀取后,拼接閉包,將exports,require,module,__dirname,__filename五大參數(shù)拼接好,使用vm模塊的沙箱機(jī)制運(yùn)行,得到的結(jié)果放入module.exports返回。

總結(jié)

以上就是node.js的模塊加載的簡(jiǎn)單邏輯,實(shí)際上node.js的源碼遠(yuǎn)遠(yuǎn)比上面的代碼復(fù)雜,光是處理模塊路徑、判斷合法等操作就寫了N行。而且我這里沒有寫緩存以及其它的復(fù)雜邏輯,但核心差不多就是這些,核心的核心就是用fs.readFileSync讀取js文件,把內(nèi)容拼接到一個(gè)大大的閉包中,這也解釋了為什么我們自己寫的所有node模塊中都會(huì)有require方法,exports導(dǎo)出,以及__dirname和__filename參數(shù)。

了解了node.js的模塊加載邏輯,在以后寫node.js就更可避免一些誤解,寫出精細(xì)的代碼。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • NodeJS配置CORS實(shí)現(xiàn)過程詳解

    NodeJS配置CORS實(shí)現(xiàn)過程詳解

    這篇文章主要介紹了NodeJS配置CORS實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Nodejs回調(diào)加超時(shí)限制兩種實(shí)現(xiàn)方法

    Nodejs回調(diào)加超時(shí)限制兩種實(shí)現(xiàn)方法

    這篇文章主要介紹了Nodejs回調(diào)加超時(shí)限制兩種實(shí)現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • Nodejs中Express 常用中間件 body-parser 實(shí)現(xiàn)解析

    Nodejs中Express 常用中間件 body-parser 實(shí)現(xiàn)解析

    這篇文章主要介紹了Nodejs中Express 常用中間件 body-parser 實(shí)現(xiàn)解析,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • Node.js基礎(chǔ)入門之使用方式及模塊化詳解

    Node.js基礎(chǔ)入門之使用方式及模塊化詳解

    Node.js是一個(gè)基于 Chrome V8 引擎的 JavaScript 運(yùn)行時(shí)。類似于Java中的JRE,.Net中的CLR。本文將詳細(xì)為大家介紹一些Node.js的基礎(chǔ)知識(shí)和使用方式以及其模塊化,需要的可以參考一下
    2022-03-03
  • nodejs個(gè)人博客開發(fā)第四步 數(shù)據(jù)模型

    nodejs個(gè)人博客開發(fā)第四步 數(shù)據(jù)模型

    這篇文章主要為大家詳細(xì)介紹了nodejs個(gè)人博客開發(fā)的數(shù)據(jù)模型,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • 配置nodejs環(huán)境的方法

    配置nodejs環(huán)境的方法

    本篇文章主要介紹了配置nodejs環(huán)境變量的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • 究竟什么是Node.js?Node.js有什么好處?

    究竟什么是Node.js?Node.js有什么好處?

    這篇文章主要介紹了究竟什么是Node.js?Node.js有什么好處?,為試圖解釋什么是 Node.js,本文將簡(jiǎn)要介紹一些背景信息:它要解決的問題,它如何工作,如何運(yùn)行一個(gè)簡(jiǎn)單應(yīng)用程序,最后,Node 在什么情況下是一個(gè)好的解決方案,需要的朋友可以參考下
    2015-05-05
  • Nodejs開發(fā)grpc的實(shí)例代碼

    Nodejs開發(fā)grpc的實(shí)例代碼

    Nodejs開發(fā)grpc包含靜態(tài)和動(dòng)態(tài)兩種代碼生成方式,靜態(tài)代碼生成需要提前通過.proto文件編譯生成JS源碼,而動(dòng)態(tài)代碼生成則是在運(yùn)行時(shí)指定IDL文件位置,實(shí)時(shí)生成源碼,兩者各有優(yōu)缺點(diǎn),本文給大家介紹Nodejs開發(fā)grpc的實(shí)例代碼,感興趣的朋友一起看看吧
    2024-10-10
  • nodejs開發(fā)一個(gè)最簡(jiǎn)單的web服務(wù)器實(shí)例講解

    nodejs開發(fā)一個(gè)最簡(jiǎn)單的web服務(wù)器實(shí)例講解

    在本篇文章里小編給大家整理的是關(guān)于nodejs開發(fā)一個(gè)最簡(jiǎn)單的web服務(wù)器實(shí)例內(nèi)容,有需要的朋友們可以參考下。
    2020-01-01
  • Nodejs 中文分詞常用模塊用法分析

    Nodejs 中文分詞常用模塊用法分析

    這篇文章主要介紹了Nodejs 中文分詞常用模塊用法,結(jié)合具體案例形式分析了node.js常用分詞模塊的基本功能、用法、效率與相關(guān)使用特點(diǎn),需要的朋友可以參考下
    2023-05-05

最新評(píng)論