给大家变个 Node.js 的小魔术

开发 前端
Node.js 的 js 模块加载的流程是 load -> _extensions['.js'] -> _compile,可以通过修改 _extensions['.js'] 来达到 hook 的目的,比如在 _compile 之前做一些代码转换。

[[425926]]

本文转载自微信公众号「神光的编程秘籍」,作者神说要有光。转载本文请联系神光的编程秘籍公众号。

魔术演出

我们准备一个 Node.js 的模块 input.js:

  1. // input.js 
  2. function func() { 
  3.     return '卡颂' 
  4. module.exports = func(); 

这个模块返回的值是啥?

东东:是“卡颂”。

那我在另一个模块 test.js 中引入这个 input.js,然后打印一下:

  1. // test.js 
  2. const data = require('./input.js'); 
  3. console.log(data); 

之后我在 entry.js 里面引入 test.js:

  1. require('./test.js'); 

执行之后打印的是啥?

东东:是“卡颂”。

真的么?那我们跑一下:

打印的是啥:

东东:是 “卡帅”,哇,好神奇,怎么做到的。

我:想不想学?

东东:想。

我:那接下来就进入魔术揭秘时间。

魔术揭秘

Node.js 加载模块的流程是这样的:

模块加载会调用 load 方法, load 会调用对应后缀名的 _extensions 的方法来处理,其中会调用 _compile 来编译并把结果放入 cache,之后返回。

所以呢?我们想改变 js 模块的返回值,只需要改造下 Module._extensions['.js'] 就可以了。

  1. const Module = require('module'); 
  2. const fs = require('fs'); 
  3.  
  4. Module._extensions['.js'] = function (module, filename) { 
  5.     let content = fs.readFileSync(filename, 'utf8'); 
  6.     if (filename.includes('input')) { 
  7.         content = content.replace('卡颂''卡帅'); 
  8.     } 
  9.     module._compile(content, filename); 
  10. }; 

我们对 filename 为 input 的文件,读取内容之后进行了替换,之后再调用 module._compile 来编译,后续流程不变。

模块引入方式不变,但是模块内容已经悄悄的被修改了,这个魔术的名字叫做 require hook。

东东:原来是你藏了一段代码没展示。

我:魔术都是这样的啊。而且你别小看了这个 require hook,它能做到很多强大的功能呢。

东东:哦?比如说

我:比如说 ts-node,它是怎么做到直接 require ts 模块的?就是通过 require hook 偷偷做了编译,其实你执行的是编译后的 js。

比如说 babel-register 它是怎么做到直接执行带有 esnext 新特性的代码的?也是通过 require hook 偷偷做了编译。

还有覆盖率测试,其实是通过函数插桩做到的,也就是你每执行一条语句都会计数。怎么插桩呢?跑单测的时候也没手动插桩啊,就是因为工具内部偷偷通过 require hook 做了插桩,才能得到覆盖率数据。

东东:这个魔术还挺有用的嘛。学会了~

总结

Node.js 的 js 模块加载的流程是 load -> _extensions['.js'] -> _compile,可以通过修改 _extensions['.js'] 来达到 hook 的目的,比如在 _compile 之前做一些代码转换。

这种 hook 在 babel-register、ts-node 还有单测的覆盖率测试中都有应用,能够达到透明的修改代码的目的。

因为开发者不知道代码什么时候被修改的,所以看起来比较神奇。

 

责任编辑:武晓燕 来源: 神光的编程秘籍
相关推荐

2022-03-30 08:36:32

Node.jsPRHTTP

2013-11-01 09:34:56

Node.js技术

2015-03-10 10:59:18

Node.js开发指南基础介绍

2020-05-29 15:33:28

Node.js框架JavaScript

2021-12-25 22:29:57

Node.js 微任务处理事件循环

2012-02-03 09:25:39

Node.js

2011-09-08 13:46:14

node.js

2011-09-02 14:47:48

Node

2011-09-09 14:23:13

Node.js

2011-11-01 10:30:36

Node.js

2012-10-24 14:56:30

IBMdw

2011-11-10 08:55:00

Node.js

2021-01-04 08:09:58

Node.js磁盘接口

2011-11-02 09:04:15

Node.js

2021-09-26 05:06:04

Node.js模块机制

2021-11-06 18:40:27

js底层模块

2019-07-09 14:50:15

Node.js前端工具

2015-06-23 15:27:53

HproseNode.js

2021-04-06 10:15:29

Node.jsHooks前端

2021-02-01 15:42:45

Node.jsSQL应用程序
点赞
收藏

51CTO技术栈公众号