亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

如何使node也支持從url加載一個(gè)module詳解

 更新時(shí)間:2018年06月05日 11:22:08   作者:zhangzhao  
這篇文章主要給大家介紹了關(guān)于如何使node也支持從url加載一個(gè)module的相關(guān)資料,文中通過(guò)示例代碼將實(shí)現(xiàn)的方法介紹非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧

前言

最近兩天 ry 大神的 deno 火了一把。作為 node 項(xiàng)目的發(fā)起人,現(xiàn)在又基于 go 重新寫了一個(gè)類似 node 的項(xiàng)目命名為 deno,引發(fā)了大家的強(qiáng)烈關(guān)注。

在 deno 項(xiàng)目 readme 的開始就列舉出了這個(gè)項(xiàng)目的優(yōu)勢(shì)和需要解決的問(wèn)題,里面最讓我矚目的就是模塊原生支持 ts ,同時(shí)也能也必須從 url 加載模塊,這也是與現(xiàn)有的 CommonJS 最大的不同。

仔細(xì)思考一下,deno 的模塊化與 CommonJS 相比,更多的是一些 runtime 的能力?,F(xiàn)有的 CommonJS 底層實(shí)現(xiàn)過(guò)程并不是靜態(tài)化,考慮了很多的動(dòng)態(tài)配置,所以基于現(xiàn)有到 CommonJS 改造起來(lái)還是比較容易的,支持 url 加載或者 ts 模塊也并不復(fù)雜,主要難點(diǎn)在于與系統(tǒng)調(diào)用的耦合度上。所以周六在家準(zhǔn)備擼個(gè)小項(xiàng)目,從上層入手,算是仿照 deno 的這幾個(gè)特性使得一個(gè)仿原生 node 的 CommonJS 模塊語(yǔ)法也能支持這些特性。

CommonJS 的執(zhí)行過(guò)程

想要讓 CommonJS 支持 url 訪問(wèn)或者原生加載 ts 模塊,必須從 CommonJS 的執(zhí)行過(guò)程中入手,在中間階段將模塊注入進(jìn)去。而 CommonJS 的執(zhí)行過(guò)程其實(shí)總結(jié)起來(lái)很簡(jiǎn)單,大概分為以下幾點(diǎn):

  • 處理路徑依賴

處理路徑依賴應(yīng)該也是所有模塊化加載規(guī)范的第一步,換言之就是根據(jù)路徑找到文件的位置。無(wú)論是 CommonJS 的 require 還是 ESModule 的 import,無(wú)論是相對(duì)路徑還是絕對(duì)路徑,都必須首先在內(nèi)部對(duì)這個(gè)路徑進(jìn)行處理,找到合適的文件地址。

模塊路徑有可能是絕對(duì)路徑,有可能是相對(duì)路徑,有可能省略了后綴(js、node、json),有可能省略了文件名(index),甚至是動(dòng)態(tài)路徑(運(yùn)行時(shí)基于變量的動(dòng)態(tài)拼接)等等。

首先就是遵守約定,同時(shí)按照一定的策略找到這個(gè)文件的真實(shí)位置,中間的過(guò)程就是補(bǔ)齊上面模塊化省略的東西。一般都是根據(jù) CommonJS 的這張流程圖

  • 加載文件

確認(rèn)了路徑并且確保了文件存在之后,加載文件這一步就簡(jiǎn)單粗暴的多。最簡(jiǎn)單的方式就是直接讀取硬盤上的文件,將純文本的模塊源代碼讀取至內(nèi)存。

  • 拼接函數(shù)

在上一步中獲取到的只是代碼的文本形式源文件,并不具有執(zhí)行能力。在接下來(lái)的步驟中需要將它變?yōu)橐粋€(gè)可執(zhí)行的代碼段。

如果有同學(xué)看過(guò) webpack 打包出來(lái)的結(jié)果,可以發(fā)現(xiàn)有這么一個(gè)現(xiàn)象,所有模塊化的內(nèi)容都處在一個(gè)函數(shù)的閉包中,內(nèi)部所有的模塊加載函數(shù)都替換成了 __webpack_require__ 這類的 webpack 內(nèi)部變量。

還有一個(gè)問(wèn)題,在 CommonJS 模塊化規(guī)范中我們或多或少在每個(gè)文件中會(huì)寫 module, module.exports require 等等這樣的「字眼」,因?yàn)檫@里的 module 和 require 講道理并不能稱為關(guān)鍵字,JS 中關(guān)于模塊加載方面的關(guān)鍵字只有 ESModule 中 import 和 export 等等相關(guān)的內(nèi)容,他們是真真正正的關(guān)鍵字。而這里 CommonJS 里面帶來(lái)的 module 和 require 則完全算是自己實(shí)現(xiàn)的一種 hack,在日常的 CommonJS 模塊書寫過(guò)程中,module 對(duì)象和 require 函數(shù)完全是 node 在包解析時(shí)注入進(jìn)去的(類似上面的 __webpack_require__)

這也就給了我們極大的想象空間,我們也完全可以將上面拿到的 module 進(jìn)行包裹然后注入我們傳遞的每一個(gè)變量。簡(jiǎn)單的例子:

// 純文本代碼 無(wú)法執(zhí)行
var str = 1;
console.log(str);

將函數(shù)進(jìn)行拼接,結(jié)果依舊是一個(gè)純文本代碼。但是已經(jīng)可以給這個(gè)文件內(nèi)部注入 require module 等變量,只需后續(xù)將它變?yōu)榭蓤?zhí)行文件并執(zhí)行,就能把模塊取出來(lái)。

function(require, module, exports, __dirname, __filename) {
 // 純文本代碼
 var str = 1;
 console.log(str);
}
  • 轉(zhuǎn)化為可執(zhí)行代碼

拼接完成之后我們拿到的是還是純字符串的代碼,接下來(lái)就需要將這個(gè)字符串變成真正的代碼,也就是將字符串變?yōu)榭蓤?zhí)行代碼片段,這種操作在 JS 的歷史上一直是危險(xiǎn)的代名詞…一直以來(lái)也有多種方法可以使用,eval、new Function(str) 等等。而在 node 環(huán)境中可以直接使用原生提供的 vm 模塊,內(nèi)部的沙盒環(huán)境支持我們手動(dòng)注入一些變量,相對(duì)來(lái)說(shuō)安全性還有所保證。

var txt = "function(require, module, exports, __dirname, __filename) {
 module.exports = 1;
}"

var vm = require('vm');
var script = new vm.Script(txt);
var func = script.runInThisContext();

上面這個(gè)示例中,func 就已經(jīng)是經(jīng)過(guò) vm 從字符串變?yōu)榭蓤?zhí)行代碼段的結(jié)果,我們的 txt 給定的是一個(gè)函數(shù),所以此時(shí)我們需要調(diào)用這個(gè)函數(shù)來(lái)最后完成模塊的導(dǎo)出。

var m = {
 exports: {}
};
func(null, m, m.exports);

這樣的話,內(nèi)部導(dǎo)出的內(nèi)容就會(huì)被外面全局對(duì)象 m 所截獲,將每一個(gè)模塊導(dǎo)出的結(jié)果緩存到全局的 m 對(duì)象上面來(lái)。

而對(duì)于 require 函數(shù)來(lái)講,注入時(shí)我們需要考慮的就是走完上面的幾個(gè)步驟,require 接受一個(gè)字符串變量路徑,然后依次通過(guò)路徑找到文件,獲取文件,拼接函數(shù),變?yōu)榭蓤?zhí)行代碼段并執(zhí)行,之后仍給全局的緩存對(duì)象,這就是 「require」需要做的內(nèi)容。

過(guò)程中的切面

  • 最終形態(tài)是什么

對(duì)于最終的形態(tài),本質(zhì)上我們是要提供一個(gè) require 函數(shù),它的目標(biāo)就是在 runtime 能夠從遠(yuǎn)端 url 加載 js 模塊,能夠加載 ts 模塊甚至類似 babel 提供 preset 加載各種各樣的模塊。

但是我們的 require 無(wú)法注入到 node bootstrap 階段,所以最終結(jié)果一定得是 bootsrap 文件使用 CommonJS 模塊加載,通過(guò)我們自定義的 require 加載的所有文件都能實(shí)現(xiàn)功能。

  • 生命周期的設(shè)計(jì)

就如上面的第二部分介紹的那樣,對(duì)于 require 函數(shù)我們要依次做這些事情,完全可以把每個(gè)階段看做一個(gè)切面,任何一個(gè)階段只關(guān)注輸入和輸出而不關(guān)注上個(gè)階段是如何產(chǎn)出的。

經(jīng)過(guò)仔細(xì)的思考,最終設(shè)置了兩個(gè)核心的過(guò)程,包裹模塊內(nèi)容 和 編譯文件結(jié)果。

包裹模塊內(nèi)容就是將字符串的文件結(jié)果包裹一下函數(shù),專注于處理字符串結(jié)果,將普通文件的文本進(jìn)行包裹。

編譯文件結(jié)果這一步就是將代碼結(jié)果編譯成 node 能夠直接識(shí)別的 js 而使得下一步沙盒環(huán)境進(jìn)行執(zhí)行,每次通過(guò)文件結(jié)果動(dòng)態(tài)在內(nèi)存進(jìn)行編譯,從而使得下一步 js 的執(zhí)行。

  • 同步還是異步?

這個(gè)問(wèn)題其實(shí)困擾了很久。最大的問(wèn)題就是里面涉及了部分異步加載的問(wèn)題,按照傳統(tǒng)前端的做法,這里一般都是使用 callback 或者 promise(async/await) 的方式,但這樣就會(huì)帶來(lái)一個(gè)很大的問(wèn)題。

如果是 callback 的方式,那么意味著最終我的 require 可能得這樣調(diào)用:

var r = require("nedo");
var moduleA = r("./moduleA");
var moduleB = r("./moduleB");

function log(module) {
 // 所有執(zhí)行過(guò)程作為 callback
 // 這里拿到 module 的結(jié)果
 console.log(module);
}

moduleA(log); // 傳入 callback,moduleA 加載結(jié)束執(zhí)行回調(diào)
moduleB(log); // 傳入 callback,moduleB 加載結(jié)束執(zhí)行回調(diào)

這樣就顯得很愚蠢,即使改成 AMD 那樣的 callback 調(diào)用也感覺是在開歷史的倒車。

如果是 promise(async/await) 這樣的異步方式,那么意味著最終我的 require 可能得這樣調(diào)用:

var r = require("nedo");
var moduleA = r("./moduleA");

moduleA.then(module => {
 // 這里拿到 module 結(jié)果
});

(async function() {
 var moduleB = await r("./moduleB");
 // 這里拿到 module 的結(jié)果
})();

說(shuō)實(shí)話這種方式也顯得很愚蠢。不過(guò)中間我想了個(gè)方法,包裹函數(shù)時(shí)多包一層,包一個(gè) IIFE 然后自執(zhí)行一個(gè) async 的 wrapper,不過(guò)這樣的話 bootstrap 文件就必須還得手動(dòng)包裹在 async 的函數(shù)中,子函數(shù)的問(wèn)題解決了但是上層沒有解決,不夠完美。

其實(shí)后來(lái)仔細(xì)的思考了一下,造成這樣的問(wèn)題的原因究其根本是因?yàn)?request 是 async 的,這就導(dǎo)致了后續(xù)的代碼必須以 async 的方式出現(xiàn)。如果我們想要從硬盤讀取一個(gè)文件,那么我們可以使用 promise 包裹的 fs.readFile,當(dāng)然我們也可以使用 fs.readFileSync 。前者的方法會(huì)讓后續(xù)的所有調(diào)用都變成異步,而后者的代碼還是同步,雖然性能很差但是完全符合直覺。

所以就必須找到一個(gè) sync 的 request 的形式,才能讓最終調(diào)用變的完美,最終的想法結(jié)果應(yīng)該如下:

var r = require("nedo");
var moduleA = r("./moduleA");
// moduleA 結(jié)果

var moduleB = r("https://baidu.com");
// moduleB 結(jié)果,同步阻塞

思考了半天不知道 sync 的 request 應(yīng)該怎么寫,后來(lái)只得求助萬(wàn)能的 npmjs,結(jié)果真的發(fā)現(xiàn)了一個(gè) sync-request 的包,仔細(xì)研究了一下代碼發(fā)現(xiàn)核心是借助了 sync-rpc 這個(gè)包,雖然這個(gè)包 github 只有 5 個(gè) star,下載量也不大。但是感覺卻是非常的厲害,能夠?qū)⑷魏萎惒降拇a轉(zhuǎn)化為同步調(diào)用的形式,戰(zhàn)略性 star,日后可能大有所為…

  • runtime 編譯

解決了 request async 的問(wèn)題之后其他問(wèn)題都變的非常簡(jiǎn)單,ts 使用 babel + ts preset 在內(nèi)存中完成了編譯,如果想要增加任何文件的支持,只需要在 lib/compile 下加入對(duì)應(yīng)的文件后綴即可,在內(nèi)存中只要能夠完成編譯就能夠最終保證代碼結(jié)果。

  • top level await

在之前的過(guò)程中我們只是包了一層注入?yún)?shù)的函數(shù)進(jìn)去,當(dāng)然也可以上層包裹一層 async 函數(shù),這樣就可以在使用 nedo require 的包內(nèi)部直接使用頂層 await,不需要再使用 async 進(jìn)行包裹

最終結(jié)果

最后經(jīng)過(guò)幾個(gè)小時(shí)的不懈努力,最終能夠?qū)?hello world 跑起來(lái)了,代碼還處于 pre-pre-pre-prototype 的階段。倉(cāng)庫(kù)地址 nedo ,希望大家多幫忙 review,提供更多建設(shè)性的意見…

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • express中創(chuàng)建 websocket 接口及問(wèn)題解答

    express中創(chuàng)建 websocket 接口及問(wèn)題解答

    本文主要介紹了express中創(chuàng)建 websocket 接口及問(wèn)題解答,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • Node?文件查找優(yōu)先級(jí)及?Require?方法文件查找策略

    Node?文件查找優(yōu)先級(jí)及?Require?方法文件查找策略

    這篇文章主要介紹了Node文件查找優(yōu)先級(jí)及Require方法文件查找策略。文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • 簡(jiǎn)單兩步使用node發(fā)送qq郵件的方法

    簡(jiǎn)單兩步使用node發(fā)送qq郵件的方法

    這篇文章主要介紹了簡(jiǎn)單兩步使用node發(fā)送qq郵件的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-03-03
  • node.js [superAgent] 請(qǐng)求使用示例

    node.js [superAgent] 請(qǐng)求使用示例

    這篇文章主要介紹了node.js [superAgent] 請(qǐng)求使用示例,分別給大家匯總了post請(qǐng)求、get請(qǐng)求、delete請(qǐng)求和put請(qǐng)求的示例,推薦給大家,希望大家能夠喜歡。
    2015-03-03
  • nodejs和npm版本不匹配報(bào)錯(cuò)的解決方法

    nodejs和npm版本不匹配報(bào)錯(cuò)的解決方法

    當(dāng)公司要求使用固定nodejs的版本時(shí),自己不小心更新了npm,就會(huì)導(dǎo)致npm和nodejs不匹配,下面這篇文章主要給大家介紹了關(guān)于nodejs和npm版本不匹配報(bào)錯(cuò)的解決方法,需要的朋友可以參考下
    2023-04-04
  • node版本過(guò)高該如何將node版本降低

    node版本過(guò)高該如何將node版本降低

    我們常使用nvm來(lái)管理node.js的版本,這樣就可以根據(jù)自己的需要來(lái)回切換node.js版本,下面這篇文章主要給大家介紹了關(guān)于node版本過(guò)高該如何將node版本降低的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • node通過(guò)express搭建自己的服務(wù)器

    node通過(guò)express搭建自己的服務(wù)器

    本篇文章主要介紹了node通過(guò)express搭建自己的服務(wù)器 ,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • nodejs入門教程四:URL相關(guān)模塊用法分析

    nodejs入門教程四:URL相關(guān)模塊用法分析

    這篇文章主要介紹了nodejs入門教程四之URL相關(guān)模塊用法,較為詳細(xì)的分析了URL相關(guān)模塊功能、方法與使用技巧,需要的朋友可以參考下
    2017-04-04
  • 利用yarn代替npm管理前端項(xiàng)目模塊依賴的方法詳解

    利用yarn代替npm管理前端項(xiàng)目模塊依賴的方法詳解

    這篇文章主要給大家介紹了關(guān)于利用yarn代替npm管理前端項(xiàng)目模塊依賴的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-09-09
  • Node.js assert斷言原理與用法分析

    Node.js assert斷言原理與用法分析

    這篇文章主要介紹了Node.js assert斷言原理與用法,結(jié)合實(shí)例形式分析了assert模塊斷言函數(shù)與使用技巧,需要的朋友可以參考下
    2019-01-01

最新評(píng)論