Inspect React Node without DevTool

JavaScript

在 React 开发过程中,使用 Facebook 提供的官方 Chrome DevTool Extension 可以很方便的查看,修改页面上的 React 组件。然而,有时候也需要在没有 DevTool 的情况下,对 React 组件进行 Debug。比如:在测试电脑上查看一个即时出现的问题,或是在 Internet Explorer / Safari 上调试一个出现的问题等。

以下介绍如何在不借助 Chrome DevTool Extension 的情况下,完成对当前 React 组件的检查。

首先,需要获取到某个需要查看的 DOM 元素。可以用 querySelector 或是在 Chrome DevTool 中选中某个元素,然后在 Console 中使用 $0 获得该元素。

React 会在元素上添加额外的属性,用于记录当前这个 React 节点的相关数据。可以通过下面的代码来获取这个属性数据:

function getInstance(element) {
  const key = Object.keys(element)
    .filter(key => key.startsWith('__reactInternalInstance$'))[0];
  if (!key) return null;
  return element[key];
}

React 在添加属性的时候,属性名称会增加一个随机字符串作为后缀(各个 React Node 使用的随机字符串是一致的)。所以需要通过检查 startsWith 来判断当前属性是否是 React 使用的属性。

拿到的这个对象,有一些有用的数据,包括:

  • child - 当前元素子元素的头个元素
  • elementType/type - 该 React 节点对应的类型。如果是 HTML Element,那么就是一个字符串,比如 "div";如果是一个自定义的 React 元素,则是一个函数(class 或 function)。也就是 React.createElement 函数的第一个参数。
  • memoizedProps - 当前元素使用的 props。对于任何一个 React 生成的 HTML 元素,对会有对应的 Props。(写 JSX 的时候,每一个属性,包括 children,都是一个 Props 的属性值)
  • memoizedState - 当前元素使用的 state
  • return - 当前元素所在双向链表的上一个元素
  • sibling - 当前元素下一个兄弟元素
  • stateNode - 当前元素在组件内可以用的 this,包含了 propsrefsstatecontext 以及其他 React Component 的方法。在这个对象的原型链上,还有 React Component 组件的各个方法(比如生命周期函数如 componentDidMount)以及 setState 等可用方法。换句话说,这里的对象就是一个 React Component Class 生成的实例。如果组件是一个 Functional Component,那么这里就是 null 了;而如果是 React 的内置组件(比如 <div /><span /> 这类),那么 stateNode 就是对应生成的 DOM 元素。

根据 React 16 中 Fiber 的设计,元素之间是一个双向链表的关系,每一个节点会连结其上一个元素(return),子元素的首个元素(child),下一个兄弟元素(sibling),因而从任意一个中间的 HTML 元素开始,都可以遍历整个 React 树。

注:从一个元素 A 的 sibling 抵达下一个元素 B 后,该元素 B 的 return 是他的上一个兄弟元素,也就是 A,而不是真正意义上 React / HTML 树的父元素。只有当 B 是 A 的第一个子元素的时候,B 的 return 才是它在树上的父元素。