如何用Babel获取JS函数的使用情况?

1.背景

一些老项目可能会遗留很多不需要的功能。如果可以知晓每个js文件的代码使用情况,就可以针对性的删除无用代码,减少项目体积&提升代码可读性。本文会简单实现一个函数使用情况插件供自己学习和大家参考~

大致的效果如下(红色字体即为未使用的函数):

简单点说,只要每次执行react组件js函数时进行埋点,将代码块的行数上传,之后就可以汇总并显示函数使用情况了。只不过人为埋点很麻烦,因此这一步可以通过babel插件代替。

3.Coding

3.1 项目初始化

最近rsbuild貌似有点🔥,本着学习的态度,我就选择它来初始化项目。

主要的目录结构如下

主要目录结构 复制代码
rsbuild-playground/
├── plugins/               
│   ├── codeCoverage.js    # 代码覆盖率插件
│   └── inject.js          # 需要注入的js脚本
├── src/                   
│   ├── tool.ts            # 测试函数
│   ├── index.tsx          # 入口文件
│   └── App.tsx            # 根组件
└── rsbuild.config.js      # rsbuild 配置文件

tool.ts中简单写三个函数用于测试代码覆盖率

之后,在App.tsx中引用前两个函数

3.2 插件初始化

首先我们先在codeCoverage.js里打印几个console.log看看,不熟悉babel插件的小伙伴可以先去学习下编写插件的格式

之后,去rsbuild.config.ts配置文件引入插件

执行一下打包命令npm run build

可以看到目前插件已经把项目里所有的函数代码块范围都打印出来了,分别是App.tsx里的<App />useEffect以及tool.ts里的三个测试函数

3.3 自动埋点

通过上一步,我们已经可以获取到函数的基本信息了,接下来则可以通过埋点对函数的执行次数进行简单的计数,就可以知道这函数到底还有没有在使用 & 可视化出来。

3.3.1 埋点函数 inject.js

埋点的函数就是项目目录下的inject.js,它的简易代码如下:

直接把inject函数添加到window上,这样在其他函数内部就可以直接调用了。当然,你也可以把inject函数export出去,之后在每个的js文件顶部通过babel插入import inject函数的代码。我这就简单的插入到window上了。

3.3.2 引入 inject.js

由于inject.js目前是在/plugin目录下的,在rsbuild打包时没法直接引入,因此我们需要在打包时把inject.js复制到/src目录下,并且在/src/index.tsximport

这部分操作可由codeCoverage.js插件实现,修改插件的Program方法如下:

这里通过babel提供的importDeclaration创建了一个import语法,同时用unshift方法将这个语法插入到了/src/index.tsx文件的顶部。

这段语法其实就等价于:

arduino 复制代码
import '【绝对路径】/src/inject.js'

// 如果你的 inject.js 是 export const inject = 这种形式
// 这里就需要把import语句改写外 import { inject } from '...'
// 而直接 window.inject = xxx 的话就相对简单

最后,还把buildInfo插入到了state对象上,这样在函数访问入口FunctionDeclarationArrowFunctionExpressionstate参数上就可以获得当前文件的信息了。

无论是箭头函数还是普通函数,插入inject的代码都差不多,这里我就展示一下普通函数的例子,修改FunctionDeclaration如下:

这里解释下什么叫函数比所在文件更先访问:

假设此时babel正在解析App.tsx文件中的useEffect函数,而该函数内部调用了test2。在这种情况下,test2FunctionDeclaration会被触发。但是,由于此时tool.tsProgram入口函数尚未触发,因此test2FunctionDeclarationstate参数中还未插入buildInfo信息。

所以,遇到上面这情况需要return。等babel解析到tool.ts的时候在插入inject函数即可。

整个FunctionDeclaration的作用就等价于:

php 复制代码
export function test2() {
    // 给每个函数的顶部添加 inject()
    inject({ position: xxx, filePath: xxx, cacheKey: xxx, ... })
    
    // 函数原本的代码
    console.log('test2')
}

ArrowFunctionExpression也是类似的,可以直接复制上面的代码。

3.3.3 测试一下

执行npm run build打包出dist文件看看效果

可以看到每个函数(包含react组件)执行的时候都触发了插入的inject方法,同时tool.ts里的test3函数由于没被调用,因此没有触发相应的inject方法。

至此,查看JS函数的使用情况已经完成了一大半了,剩下的就是完善inject函数的fetch方法,并将test3函数未被使用的情况可视化。

3.4 可视化

其实只要有了埋点数据后怎么可视化都行,无论是用富文本还是图片之类的。主要是找出对应js文件中,没有埋点信息的函数的行数,并高亮显示。

tool.ts为例,大致的流程为:

  • babel插件为tool.ts还没有id的函数生成id。在3.3.2里我们用getCacheKey为被调用过的函数生成了id,但像test3这种函数根本没引用过。所以并不会执行FunctionDeclaration。因此需要用getCacheKey生成一下。
  • 当每个函数都有了id后就可以依次比对数据库的埋点信息了。如果没有埋点信息就说明这些函数最近一段时间都没触发过,这时可以记录下这些函数的行索引。
  • 有了未执行函数的行索引后,就想怎么可视化就怎么可视化了。

这里作者懒得再搞后端和数据库了,直接用nodejs代码简单代替一下思路吧:

该代码最终的效果就如开头的背景所示:

最后

感谢观看~,本文的实现其实比较粗糙(还是太懒了=,=),主要还是记录下自己感兴趣的一些前端技术供大伙简单参考。

项目地址:github.com/q-mona/mona...

相关推荐
小矮马22 分钟前
React-组件和props
前端·javascript·react.js
懒羊羊我小弟26 分钟前
React Router v7 从入门到精通指南
前端·react.js·前端框架
gaog2zh26 分钟前
0803分页_加载更多-网络ajax请求2-react-仿低代码平台项目
react.js·ajax·分页·加载更多
Mars狐狸1 小时前
AI项目改用服务端组件实现对话?包体积减小50%!
前端·react.js
吃面必吃蒜2 小时前
从 Vue 到 React:React 合成事件
javascript·vue.js·react.js
举个栗子dhy2 小时前
【血缘关系图下钻节点,节点展开收起功能,递归和迭代问题处理】
javascript·react.js
Aiolimp3 小时前
React中CSS使用方法
前端·react.js
Moment3 小时前
受控组件和非受控组件的区别?别再傻傻分不清了 😁😁😁
前端·javascript·react.js
数据智能老司机4 小时前
React关键概念——处理事件和状态
react.js·前端框架·前端工程化
数据智能老司机6 小时前
React关键概念——理解React组件与JSX
react native·react.js·前端框架