js中执行到一个if就停止的代码_Node 中如何引入一个模块及其细节
在 node 環(huán)境中,有兩個(gè)內(nèi)置的全局變量無需引入即可直接使用,并且無處不見,它們構(gòu)成了 nodejs 的模塊體系: module 與 require。以下是一個(gè)簡單的示例
const fs = require('fs')const add = (x, y) => x + ymodule.exports = add雖然它們在平常使用中僅僅是引入與導(dǎo)出模塊,但稍稍深入,便可見乾坤之大。在業(yè)界可用它們做一些比較 trick 的事情,雖然我不大建議使用這些黑科技,但稍微了解還是很有必要。
module wrapper
當(dāng)我們使用 node 中寫一個(gè)模塊時(shí),實(shí)際上該模塊被一個(gè)函數(shù)包裹,如下所示:
(function(exports, require, module, __filename, __dirname) {// 所有的模塊代碼都被包裹在這個(gè)函數(shù)中const fs = require('fs')const add = (x, y) => x + ymodule.exports = add });因此在一個(gè)模塊中自動(dòng)會(huì)注入以下變量:
- exports
- require
- module
- __filename
- __dirname
module
調(diào)試最好的辦法就是打印,我們想知道 module 是何方神圣,那就把它打印出來!
const fs = require('fs')const add = (x, y) => x + ymodule.exports = addconsole.log(module)- module.id: 如果是 . 代表是入口模塊,否則是模塊所在的文件名,可見如下的 koa
- module.exports: 模塊的導(dǎo)出
module.exports 與 exports
? module.exports 與 exports 有什么關(guān)系??
從以下源碼中可以看到 module wrapper 的調(diào)用方 module._compile 是如何注入內(nèi)置變量的,因此根據(jù)源碼很容易理解一個(gè)模塊中的變量:
- exports: 實(shí)際上是 module.exports 的引用
- require: 大多情況下是 Module.prototype.require
- module
- __filename
- __dirname: path.dirname(__filename)
require
通過 node 的 REPL 控制臺,或者在 VSCode 中輸出 require 進(jìn)行調(diào)試,可以發(fā)現(xiàn) require 是一個(gè)極其復(fù)雜的對象
從以上 module wrapper 的源碼中也可以看出 require 由 makeRequireFunction 函數(shù)生成,如下
// <node_internals>/internal/modules/cjs/helpers.js:33function makeRequireFunction(mod, redirects) {const Module = mod.constructor;let require;if (redirects) {// ...} else {// require 實(shí)際上是 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; } ? 關(guān)于 require 更詳細(xì)的信息可以去參考官方文檔: Node API: require?
require(id)
require 函數(shù)被用作引入一個(gè)模塊,也是平常最常見最常用到的函數(shù)
// <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 引入一個(gè)模塊時(shí),實(shí)際上通過 Module._load 載入,大致的總結(jié)如下:
require.cache
「當(dāng)代碼執(zhí)行 require(lib) 時(shí),會(huì)執(zhí)行 lib 模塊中的內(nèi)容,并作為一份緩存,下次引用時(shí)不再執(zhí)行模塊中內(nèi)容」。
這里的緩存指的就是 require.cache,也就是上一段指的 Module._cache
// <node_internals>/internal/modules/cjs/loader.js:899require.cache = Module._cache;這里有個(gè)小測試:
? 有兩個(gè)文件: index.js 與 utils.js。utils.js 中有一個(gè)打印操作,當(dāng) index.js 引用 utils.js 多次時(shí),utils.js 中的打印操作會(huì)執(zhí)行幾次。代碼示例如下?
「index.js」
// index.js// 此處引用兩次 require('./utils') require('./utils')「utils.js」
// utils.js console.log('被執(zhí)行了一次')「答案是只執(zhí)行了一次」,因此 require.cache,在 index.js 末尾打印 require,此時(shí)會(huì)發(fā)現(xiàn)一個(gè)模塊緩存
// index.jsrequire('./utils') require('./utils')console.log(require)那回到本章剛開始的問題:
? 如何不重啟應(yīng)用熱加載模塊呢??
答:「刪掉 Module._cache」,但同時(shí)會(huì)引發(fā)問題,如這種 一行 delete require.cache 引發(fā)的內(nèi)存泄漏血案
所以說嘛,這種黑魔法大幅修改核心代碼的東西開發(fā)環(huán)境玩一玩就可以了,千萬不要跑到生產(chǎn)環(huán)境中去,畢竟黑魔法是不可控的。
總結(jié)
關(guān)注我
? 本文收錄于 GitHub 山月行博客: shfshanyue/blog,內(nèi)含我在實(shí)際工作中碰到的問題、關(guān)于業(yè)務(wù)的思考及在全棧方向上的學(xué)習(xí)- 前端工程化系列
- Node進(jìn)階系列
?
總結(jié)
以上是生活随笔為你收集整理的js中执行到一个if就停止的代码_Node 中如何引入一个模块及其细节的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新人创业适合开的店有哪些 推荐一些投
- 下一篇: mysql 101_MySQL 调优/优