React调试利器:React DevTools

39,985 阅读9分钟

得益于 chrome 浏览器强大的调试能力, console.log 可谓是前端开发中解决未知问题最直接的办法,遇事不决,打个日志,简单粗暴🏈。但是在 React 项目中其实有更好的调试方案,那就是官方(脸书)出的 react devtools ,可以针对性的做一些代码的调试。想尝试react devtools的掘友,不妨先看下这篇文章,了解下此工具能做些什么。(小长文警告,可先Mark一下👍)

安装&打开 ⚒

安装

在web端的 react 项目里, React DevTools 是作为浏览器的插件引入进来, chrome 中可以在扩展程序里面添加(懂的掘友已经打开了商店🐶), firefox 添加链路:打开菜单-->Web开发者-->获取更多工具-->搜索"React Developer Tools"-->安装

chrome: 屏幕快照 2020-09-27 下午9.27.49.png firefox: 屏幕快照 2020-09-27 下午9.28.40.png

打开chrome网上商店可能需要科学上网,firefox不需要科学上网也可正常安装插件,使用上无差别

工具入口

如果检测到当前web是 react 项目,右上角的工具小图标(地址栏右侧)会发生改变,分别对应着:

debug版本react 项目,正在coding👨🏻‍💻

生产版本的react,说明已经打包发布了

表示当前依赖的react版本比较老,一般会在react15以及更早的版本出现

Web中并没有检测到有react的依赖 ❌

chrome 中右键点击检查,打开调试器,然后发现在调试器的tab栏末尾有最后两栏比较特殊的tab,分别是⚛️ Components和⚛️ Profiler,这两个就是 react devtools 的功能入口了,下面会来介绍各自的功能。

屏幕快照 2020-09-27 下午9.32.46.png

JSX结构树 🌲

为了方便演示功能,这边做了一个常见的表格展示搜索功能,组件层级是 User 组件下面有两个 UserSearch 和 UserList 组件。 屏幕快照 2020-09-27 下午9.33.48.png

定位

JSX 是 React 语法中的重点, Components 工具也是有很大一部分展示的是 react 的 JSX 虚拟 Dom 树,左上角的拾取图标用起来和 chrome devtools 里的拾取是一样的,点击以后就能定位到具体想要查看详情的组件。 屏幕快照 2020-09-27 下午9.35.40.png

屏幕快照 2020-09-27 下午9.36.00.png 定位到了 UserSearch 组件

在项目中用了一些 React-router , redux 等工具后,虚拟 Dom 树可能会受到一些侵入,比如 Dom 树中多了一些 Conext.Consumer 的结构,这些是 context 的语法,不管这些结构,双击目标组件即可下钻定位到这个组件的详细结构中,比如:双击 User 组件: 屏幕快照 2020-09-27 下午9.38.23.png 这样是不是清晰很多, User 下有个 Suspense , Suspense 下有两个自定义的组件,红色下划线标注的是该组件在全局 Dom 树里的各个层级。

Suspense是配合react懒加载使用的,文档:zh-hans.reactjs.org/docs/code-s…

屏幕快照 2020-09-27 下午10.09.45.png

过滤器

还有个小技巧可以轻松过滤掉一些不想要展示 Dom ,点开 setting 小图标的下拉框里,有一栏是组件的设置,然后找到"Hide components where",在这里可以添加过滤器,从而进行过滤的设置,下面列出了各个配置代表什么含义。

屏幕快照 2020-09-28 上午7.51.23.png 过滤器第一栏key:

名称含义
locationrouter路由匹配的组件
name组价名
type各种组件类型细分(参考下面表格)
hoc高阶组件

location和name都是进行正则匹配的

屏幕快照 2020-09-28 上午7.57.05.png

type可选值

名称含义
class继承React.Component的类组件
context共享上下文context
function函数式组件
forward ref函数式组件传递ref的HOC
host(e.g
)
浏览器支持的html元素,div,h1,p
memo函数式组件做props优化的HOC
other其他❓(待补充)
profiler测量性能的
suspense懒加载根组件

单组件调试 🔧

定位到目标组件后就可以进行单组件调试,双击组件, react devtools 的右侧就会出现组件相关信息,比如 props , state , hooks , render by 等。

屏幕快照 2020-09-28 下午2.08.50.pngprops 的信息就可以看出, User 组件是 Router 下的第一层级组件,会有 location , history , match 等额外信息。

class 类组件会更好,hooks 的根state 会缺少属性名,暂时可以通过useState<object>({msg: 'hello'}) 的方式绕过,希望后续迭代会解决这个问题

光能看属性不行,要中看又中用才行,细心的掘友已经在右上角发现一排的 icon 了:

1. 显示懒加载的fallback状态

这个时间样式的按钮是和懒加载 Suspense 相关的,当 Suspense 组件的 children 还没加载出来的时候,有一个过渡的 loading 效果,点击按钮,就会显示 fallback 属性里的 ReactNode ,用来预览加载效果还是不错的。

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <OtherComponent />
      </Suspense>
    </div>
  );
}

屏幕快照 2020-09-28 下午4.21.18.png

这边引入了 antd 的 Skeleton 效果作为 loading 的中间态,小图标高亮就说明处于 loading 状态调试。

2. 检查组件的真实Dom

React 中写的 JSX 代码渲染出来的组件树其实都不是真实的 DOM 树,所以有调试样式修改文案、直接操作 DOM 的需求,还是需要切回 chrome devtools 的 Element ,然后定位 Element 又是一顿拾取操作就会稍显不便,这个小眼睛图标的按钮就是用来定位组件真实Dom的。 屏幕快照 2020-09-28 下午6.15.12.png 这里通过 UserSearch 组件定位到了真实的 dom 树,对于一些层级比较深的 dom 结构,或者 z-index 层级复杂的dom来说,这个功能定位还是相当方便的。

3. debug信息

这个小虫的按钮在最开始用的时候会挺懵逼的,点了以后好像没有什么作用,然后悬停一会会有一段文字:

log this component data to the console(将组件数据记录在console中)

然后切换到 chrome devtools 的 console 就能发现端倪了,这里出现了和 UserSearch 相关的组件信息:

屏幕快照 2020-09-28 下午7.54.15.png 根据提示信息,右键任何值,然后点击"Store as global variable"保存为全局变量,我们这里选择了 Props 下的 onSearch 方法:

屏幕快照 2020-09-28 下午7.56.34.png console 中会出现新的一个 temp1 变量,这个变量就是指向 onSearch 的引用值,调用 temp1 函数等同于 onSearch , onSearch 的业务逻辑是根据参数过滤用户列表,下面就是过滤出包含"xu"的记录:

屏幕快照 2020-09-28 下午7.56.51.png 这种 debug 方式在一些特殊场景下比较方便,比如通过普通的页面交互无法触发,需要异步接口返回值才能触发等等。

4. 定位Source-map信息

最后一个按钮很熟悉,平时都会在一些组件库文档中的代码案例中看到,比如 antd , fusion , iview 等UI组件库,用来作为展开代码的调试入口, react devtools 的功能也是类似,这里是定位该组件对应的 Source 文件,这里我就切换到了 UserSearch 对应的 index.tsx 文件中,在开发环境中配置好 source-map ,定位组件就能直接显示源码屏幕快照 2020-09-28 下午8.15.08.png 这里的 source 功能就非常强大了,最常用的就是打断点,比如,我们在 resetTrigger 里面打两个断点,点击重置按钮后就会触发断点: 屏幕快照 2020-09-28 下午8.54.16.png

并且观察右侧栏的 Scope 中会有闭包信息,这里的闭包其实就是 useState API 保存函数式组件所产生的, filter 就是输入框里的内容,这种调试就可以避免了因为 console.log 修改代码后还要重新编译项目,还有可能会留下一些脏代码。

性能问题追踪 🚥

还有一个⚛️ profiler是什么东西呢,可能刚开始接触 React 的同学可能不太了解,在 react 的官方文档中有介绍一个Profiler API ,能添加在 React  树中的任何地方来测量树中这部分渲染所带来的开销,用法:

render(
  <App>
    <Profiler id="Navigation" onRender={callback}>
      <Navigation {...props} />
    </Profiler>
    <Main {...props} />
  </App>
);

onRender 中的回调函数 callback 中的入参包含了子组件内所有的渲染信息,有利于排查组件的性能问题

function onRenderCallback(
  id, // 发生提交的 Profiler 树的 “id”
  phase, // "mount" (如果组件树刚加载) 或者 "update" (如果它重渲染了)之一
  actualDuration, // 本次更新 committed 花费的渲染时间
  baseDuration, // 估计不使用 memoization 的情况下渲染整颗子树需要的时间
  startTime, // 本次更新中 React 开始渲染的时间
  commitTime, // 本次更新中 React committed 的时间
  interactions // 属于本次更新的 interactions 的集合
) {
  // 合计或记录渲染时间。。。
}

官方文档链接:zh-hans.reactjs.org/docs/profil…

虽然在项目中调试比较灵活,但是对项目有一定的代码侵入,甚至会有一些性能的损耗,所以当遇到使用全局 profiler 的场景的话,可以优先尝试一下用 React devtools 的 profiler 功能。

下面为了测试 profiler 功能,在 UserList 组件中增加了一万条记录来显示,看下具体的耗时如何: 屏幕快照 2020-09-28 下午9.11.11.png 这里是一个典型的性能分析火焰图,展示的是当前这个页面中,各个组件的耗时情况,如果是有耗时比较长的异常组件,横向柱状图会显示成黄色,甚至更警示🚨的颜色,这里很明显, UserList 的耗时相当长,这很符合我们的预期。

profiler 的系统设置中,还能打开组件何时渲染的开关,以及隐藏固定渲染时间以下的组件: 屏幕快照 2020-09-28 下午9.19.29.png

屏幕快照 2020-09-28 下午9.20.51.png 这里"Why did this render"就说明了 Route 组件是第一次渲染,渲染了12.8毫秒

结束 ⌛️

这边介绍了一些常用的 react 调试方法,还没有涵盖到 React Devtools 所有的功能,官方也提供了各自功能对应的英文教程,下面的传送门👇就是具体教程链接,感兴趣的掘友可以进去看看。

传送门

PS: 文中有任何错误,欢迎掘友指正

往期精彩📌