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

淺析node.js的模塊加載機制

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

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

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

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

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

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

運行結(jié)果:

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

使用require引入相應的模塊,即可使用。

__dirname和__filename

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

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

運行結(jié)果:

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

vm模塊

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

說說vm的基本用法。

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

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

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

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

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

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

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

運行結(jié)果:

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

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

CommonJS規(guī)范

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

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

模塊導入導出

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

const mymodule = require('mymodule');

使用exports導出模塊

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

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

模塊定義

module對象:在每一個模塊中,module對象代表該模塊自身。

export屬性:module對象的一個屬性,它向外提供接口。

模塊標識

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

node中模塊解析流程

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

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

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

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

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

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

//根據(jù)moduleId解析絕對路徑,
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; //當前模塊的id,它使用完整的絕對路徑標識,因此是唯一的
 this.exports = {}; //導出
 this.loaded = false; //模塊是否已加載完畢
}

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

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

//不同擴展名的加載策略
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對象返回
 },
 '.node': ''
};

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

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

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

  //循環(huán)讀取不同擴展名的文件
  for (var i = 0; i < arr.length; i++) {
   let file = p + arr[i]; //拼接上后綴名成為一個完整的路徑
   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);
}

最終運行結(jié)果:

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

重要代碼說明

_resolveFileName

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

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

_extensions

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

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

總結(jié)

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

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

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    nodejs開發(fā)一個最簡單的web服務器實例講解

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

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

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

最新評論