1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢
      選擇下列產(chǎn)品馬上在線溝通
      服務(wù)時(shí)間:8:30-17:00
      你可能遇到了下面的問題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
      淺析node.js的模塊加載機(jī)制

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

      員工經(jīng)過長(zhǎng)期磨合與沉淀,具備了協(xié)作精神,得以通過團(tuán)隊(duì)的力量開發(fā)出優(yōu)質(zhì)的產(chǎn)品。創(chuàng)新互聯(lián)建站堅(jiān)持“專注、創(chuàng)新、易用”的產(chǎn)品理念,因?yàn)椤皩W⑺詫I(yè)、創(chuàng)新互聯(lián)網(wǎng)站所以易用所以簡(jiǎn)單”。公司專注于為企業(yè)提供成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、外貿(mào)網(wǎng)站建設(shè)、微信公眾號(hào)開發(fā)、電商網(wǎng)站開發(fā),小程序定制開發(fā),軟件按需網(wǎng)站開發(fā)等一站式互聯(lián)網(wǎng)企業(yè)服務(wù)。

      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í)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。


      文章題目:淺析node.js的模塊加載機(jī)制
      網(wǎng)頁URL:http://www.ef60e0e.cn/article/pjpcjp.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        彩票| 尉氏县| 武冈市| 英山县| 弥渡县| 道孚县| 台山市| 普宁市| 长治县| 金昌市| 陇南市| 安吉县| 容城县| 珠海市| 乐东| 太原市| 长岭县| 辽阳市| 新河县| 台中县| 招远市| 青阳县| 宁城县| 肇源县| 河北省| 治县。| 兴安盟| 萝北县| 洛扎县| 汝州市| 嘉兴市| 滁州市| 民和| 舟曲县| 兴仁县| 蒙城县| 许昌市| 朝阳市| 尖扎县| 大化| 瑞安市|