查看原文
其他

【第2451期】Relyzer —— 一个 React FC 的运行分析工具

黑猫 前端早读课 2021-11-12

前言

今日前端早读课文章由字节跳动@黑猫授权分享。

正文从这开始~~

React Hooks 带来了很多好处,同时也引入了一些困扰。常见的问题有:为什么我的 useEffect 比我预期执行多得多?为什么 useCallback / useMemo 的返回值总是发生变化?

于是开发了这样的一个工具,配合 React Devtool 使用,在代码块中可视化地展示出 React 函数组件的属性及内部变量的变化情况,可以方便地定位到哪些值总是在发生变化。

截图是基于一个第三方的 TodoMVC,集成上了 Relyzer 工具。

从这张截图里可以得到这些信息:

  • 组件 TodoItem 的实例一共更新了 10 次,这 10 次更新中,todo 属性更新了 10 次(因此可以认为更新都是因为 todo 属性变化导致的)。

  • onDelete 始终没有变化,是因为它的依赖 todo.id 没有变化。 

  • finishedCallback 变化了 10 次,因为它的依赖项 todo 变化了 10 次。(Relyzer 对 useCallback / useEffect / useMemo 做了处理,也会观测它们的依赖)

  • handleViewClick 变化了 10 次。

  • useRef 的返回是不会变的。

  • ...

视频演示

安装

Relyzer 提供给用户的是一个 Babel 插件,只要在开发环境下启用这个 Babel 插件,网页上就会自动注入 Relyzer 工具面板。

// babel 配置
{
plugins: [
// enable only for development
+ isDevelopment ? 'module:@relyzer/babel' : null,
].filter(Boolean),
}

如果 babel 配置是 JSON 格式,不能使用 JS 逻辑,也可以直接引入 module:@relyzer/babel. 只需要保证生产构建时 process.env.NODE_ENV === 'production'.

因为实现原理的限制,Relyzer 需要确保能正确判断出某个函数是否是一个 React 组件,只对 React 函数组件进行 Babel 转换。所以需要使用者选择两种方式其中一种:

1、手动指定某个函数为 React 组件,只有指定过的函数才能进行调试。

// 给注释加上 @component
/**
* @component
*/

function MyComponent() {
const [val, setVal] = useState();

return (
<div />
)
}

// 但是某些编译工具(esbuild)会提前抹去注释,如果是在 esbuild 后面使用 relyzer/babel
// 可以添加一个自定义的指令 use relyzer
function MyComponent() {
'use relyzer'
const [val, setVal] = useState();

return (
<div />
)
}

2、让 relyzer/babel 自动检测

// 使用如下 babel plugin 配置项
['module:@relyzer/babel', { autoDetect: true }]

这种模式下,relyzer/babel 会对所有大写字母开头的函数进行转换,然后在运行时通过当前调用栈信息判断是否是 React 组件。

实现原理

了解过 Istanbul(JS 代码覆盖率工具)的人应该知道,Istanbul 是通过 babel 插件,往源代码中插入大量的计数代码,这样代码执行之后,就能知道哪些行或者条件分支被执行了。

Relyzer 使用了类似的实现原理:

Relyzer 提供了一个 Babel 插件,用来将正常的函数组件代码:

// input
function MyComponent({ foo }) {
const bar = useXxx()

return <div />
}

转化成这样:

// output
import useRelyzer from '@relyzer/runtime';

function MyComponent({ foo }) {
const x = useRelyzer(...)
x(foo, ...)
const bar = useXxx()
x(bar, ...)

return <div />
}

在组件运行时,runtime 会把组件每次 render 过程中收集到的信息发送给 client,通过可视化界面展示出来。

那么 Relyzer 是如何跟 Chrome React Devtool 配合使用的呢?

在 React Devtool 的 DOM 树中选择组件节点后,Relyzer Client 中会自动切换到这个组件实例。这是因为 React Devtool 选中节点后会把当前节点写入到 window.$r,那么只需要劫持这个值就能实时监听 Devtool 的选中情况了。

项目Github:https://github.com/meowtec/relyzer

关于本文
作者:@黑猫
原文:https://zhuanlan.zhihu.com/p/391734514

早读课时光机


【第2353期】Nginx 中运行 JavaScript


【第2251期】Matomo 从了解到落地——页面流量统计与分析最佳实践


欢迎自荐投稿,前端早读课等你来。

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存