最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺析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)文章

最新評(píng)論

美姑县| 澄江县| 九台市| 阿勒泰市| 建昌县| 湄潭县| 三河市| 临沭县| 平遥县| 吉水县| 理塘县| 石台县| 基隆市| 沿河| 乐东| 天气| 灵寿县| 西丰县| 兰西县| 衡山县| 马关县| 佛坪县| 安泽县| 台湾省| 城口县| 长岛县| 玉环县| 石狮市| 建昌县| 根河市| 宜君县| 宁南县| 宿迁市| 陕西省| 昌邑市| 庆安县| 东莞市| 阿拉善右旗| 长海县| 遵义县| 宣武区|