日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

js中执行到一个if就停止的代码_Node 中如何引入一个模块及其细节

發布時間:2025/3/8 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 js中执行到一个if就停止的代码_Node 中如何引入一个模块及其细节 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在 node 環境中,有兩個內置的全局變量無需引入即可直接使用,并且無處不見,它們構成了 nodejs 的模塊體系: module 與 require。以下是一個簡單的示例

const fs = require('fs')const add = (x, y) => x + ymodule.exports = add

雖然它們在平常使用中僅僅是引入與導出模塊,但稍稍深入,便可見乾坤之大。在業界可用它們做一些比較 trick 的事情,雖然我不大建議使用這些黑科技,但稍微了解還是很有必要。

  • 如何在不重啟應用時熱加載模塊?如 require 一個 json 文件時會產生緩存,但是重寫文件時如何 watch
  • 如何通過不侵入代碼進行打印日志
  • 循環引用會產生什么問題?
  • module wrapper

    當我們使用 node 中寫一個模塊時,實際上該模塊被一個函數包裹,如下所示:

    (function(exports, require, module, __filename, __dirname) {// 所有的模塊代碼都被包裹在這個函數中const fs = require('fs')const add = (x, y) => x + ymodule.exports = add });

    因此在一個模塊中自動會注入以下變量:

    • exports
    • require
    • module
    • __filename
    • __dirname

    module

    調試最好的辦法就是打印,我們想知道 module 是何方神圣,那就把它打印出來!

    const fs = require('fs')const add = (x, y) => x + ymodule.exports = addconsole.log(module)

    • module.id: 如果是 . 代表是入口模塊,否則是模塊所在的文件名,可見如下的 koa
    • module.exports: 模塊的導出

    module.exports 與 exports

    ? module.exports 與 exports 有什么關系?
    ?

    從以下源碼中可以看到 module wrapper 的調用方 module._compile 是如何注入內置變量的,因此根據源碼很容易理解一個模塊中的變量:

    • exports: 實際上是 module.exports 的引用
    • require: 大多情況下是 Module.prototype.require
    • module
    • __filename
    • __dirname: path.dirname(__filename)
    // <node_internals>/internal/modules/cjs/loader.js:1138Module.prototype._compile = function(content, filename) {// ...const dirname = path.dirname(filename);const require = makeRequireFunction(this, redirects);let result;// 從中可以看出:exports = module.exportsconst exports = this.exports;const thisValue = exports;const module = this;if (requireDepth === 0) statCache = new Map();if (inspectorWrapper) {result = inspectorWrapper(compiledWrapper, thisValue, exports,require, module, filename, dirname);} else {result = compiledWrapper.call(thisValue, exports, require, module,filename, dirname);}// ... }

    require

    通過 node 的 REPL 控制臺,或者在 VSCode 中輸出 require 進行調試,可以發現 require 是一個極其復雜的對象

    從以上 module wrapper 的源碼中也可以看出 require 由 makeRequireFunction 函數生成,如下

    // <node_internals>/internal/modules/cjs/helpers.js:33function makeRequireFunction(mod, redirects) {const Module = mod.constructor;let require;if (redirects) {// ...} else {// require 實際上是 Module.prototype.requirerequire = function require(path) {return mod.require(path);};}function resolve(request, options) { // ... }require.resolve = resolve;function paths(request) {validateString(request, 'request');return Module._resolveLookupPaths(request, mod);}resolve.paths = paths;require.main = process.mainModule;// Enable support to add extra extension types.require.extensions = Module._extensions;require.cache = Module._cache;return require; } ? 關于 require 更詳細的信息可以去參考官方文檔: Node API: require
    ?

    require(id)

    require 函數被用作引入一個模塊,也是平常最常見最常用到的函數

    // <node_internals>/internal/modules/cjs/loader.js:1019Module.prototype.require = function(id) {validateString(id, 'id');if (id === '') {throw new ERR_INVALID_ARG_VALUE('id', id,'must be a non-empty string');}requireDepth++;try {return Module._load(id, this, /* isMain */ false);} finally {requireDepth--;} }

    而 require 引入一個模塊時,實際上通過 Module._load 載入,大致的總結如下:

  • 如果 Module._cache 命中模塊緩存,則直接取出 module.exports,加載結束
  • 如果是 NativeModule,則 loadNativeModule 加載模塊,如 fs、http、path 等模塊,加載結束
  • 否則,使用 Module.load 加載模塊,當然這個步驟也很長,下一章節再細講
  • // <node_internals>/internal/modules/cjs/loader.js:879Module._load = function(request, parent, isMain) {let relResolveCacheIdentifier;if (parent) {// ...}const filename = Module._resolveFilename(request, parent, isMain);const cachedModule = Module._cache[filename];// 如果命中緩存,直接取緩存if (cachedModule !== undefined) {updateChildren(parent, cachedModule, true);return cachedModule.exports;}// 如果是 NativeModule,加載它const mod = loadNativeModule(filename, request);if (mod && mod.canBeRequiredByUsers) return mod.exports;// Don't call updateChildren(), Module constructor already does.const module = new Module(filename, parent);if (isMain) {process.mainModule = module;module.id = '.';}Module._cache[filename] = module;if (parent !== undefined) { // ... }let threw = true;try {if (enableSourceMaps) {try {// 如果不是 NativeModule,加載它module.load(filename);} catch (err) {rekeySourceMap(Module._cache[filename], err);throw err; /* node-do-not-add-exception-line */}} else {module.load(filename);}threw = false;} finally {// ...}return module.exports; };

    require.cache

    「當代碼執行 require(lib) 時,會執行 lib 模塊中的內容,并作為一份緩存,下次引用時不再執行模塊中內容」。

    這里的緩存指的就是 require.cache,也就是上一段指的 Module._cache

    // <node_internals>/internal/modules/cjs/loader.js:899require.cache = Module._cache;

    這里有個小測試:

    ? 有兩個文件: index.js 與 utils.js。utils.js 中有一個打印操作,當 index.js 引用 utils.js 多次時,utils.js 中的打印操作會執行幾次。代碼示例如下
    ?

    「index.js」

    // index.js// 此處引用兩次 require('./utils') require('./utils')

    「utils.js」

    // utils.js console.log('被執行了一次')

    「答案是只執行了一次」,因此 require.cache,在 index.js 末尾打印 require,此時會發現一個模塊緩存

    // index.jsrequire('./utils') require('./utils')console.log(require)

    那回到本章剛開始的問題:

    ? 如何不重啟應用熱加載模塊呢?
    ?

    答:「刪掉 Module._cache」,但同時會引發問題,如這種 一行 delete require.cache 引發的內存泄漏血案

    所以說嘛,這種黑魔法大幅修改核心代碼的東西開發環境玩一玩就可以了,千萬不要跑到生產環境中去,畢竟黑魔法是不可控的。

    總結

  • 模塊中執行時會被 module wrapper 包裹,并注入全局變量 require 及 module 等
  • module.exports 與 exports 的關系實際上是 exports = module.exports
  • require 實際上是 module.require
  • require.cache 會保證模塊不會被執行多次
  • 不要使用 delete require.cache 這種黑魔法
  • 關注我

    ? 本文收錄于 GitHub 山月行博客: shfshanyue/blog,內含我在實際工作中碰到的問題、關于業務的思考及在全棧方向上的學習
    • 前端工程化系列
    • Node進階系列

    ?

    總結

    以上是生活随笔為你收集整理的js中执行到一个if就停止的代码_Node 中如何引入一个模块及其细节的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。