利用click-to-component查看React Fiber结构,通透直观,浅显易懂!
最近在研究click-to-component这个小工具的时候,偶然发现它与React Fiber有紧密的联系,可以利用它查看任意DOM节点所对应的FiberNode,这可比网上铺天盖地的文章直观多了!
click-to-component是什么?
大家在接手一个不太熟悉的大项目时,想找到对应元素的代码是挺费劲的,又是动态路由又是公共组件啥的,我就想知道描述这个DOM元素的JSX片段在哪,别让我去文件夹里面找了!click-to-component就是解决这个问题的,可以让你选择DOM元素后直接跳转到相关代码位置。
umi内置了这个工具,可以直接修改配置开启
click-to-component的原理
先把它clone下来看一下 github.com/ericclemmon…
是一个用到了pnpm workspace的monorepo,apps里面准备了三个demo项目,packages里的click-to-react-component就是它本身,我们直接pnpm install然后启动apps里的Next.js项目。
cd apps/next
pnpm dev
启动后不一定可以正常跳转,是因为这个NEXT_PUBLIC_CTC_EDITOR是vscode-insiders?大部分人应该都是vscode稳定版本,我们改成vscode就行了。
然后就是一顿调试,找到了ClickToComponent.js下的这段代码。
原来就是用某种手段得到了一个file协议的url,然后用location.assign就能跳转到对应位置,editor就是我们刚改的vscode,关键是看下path怎么取到的。
经过几个getXXX函数之后,最终找到了getReactInstanceForElement这个函数
export function getReactInstanceForElement(element) {
// Prefer React DevTools, which has direct access to `react-dom` for mapping `element` <=> Fiber
if ('__REACT_DEVTOOLS_GLOBAL_HOOK__' in window) {
// @ts-expect-error - TS2339 - Property '__REACT_DEVTOOLS_GLOBAL_HOOK__' does not exist on type 'Window & typeof globalThis'.
const { renderers } = window.__REACT_DEVTOOLS_GLOBAL_HOOK__
for (const renderer of renderers.values()) {
try {
const fiber = renderer.findFiberByHostInstance(element)
if (fiber) {
return fiber
}
} catch (e) {
// If React is mid-render, references to previous nodes may disappear during the click events
// (This is especially true for interactive elements, like menus)
}
}
}
if ('_reactRootContainer' in element) {
// @ts-expect-error - TS2339 - Property '_reactRootContainer' does not exist on type 'HTMLElement'.
return element._reactRootContainer._internalRoot.current.child
}
// eslint-disable-next-line guard-for-in
for (const key in element) {
// Pre-Fiber access React internals
if (key.startsWith('__reactInternalInstance$')) {
return element[key]
}
// Fiber access to React internals
if (key.startsWith('__reactFiber')) {
return element[key]
}
}
}
它会用几种方式来尝试获取Fiber对象,Fiber对象里的_debugSource属性就存储了columnNumber、fileName、lineNumber这些信息,就能拼出跳转所需要的path了。
查看FiberNode
大家平常肯定看过很多八股文,什么FiberNode里有哪些属性啊,hooks为什么用链表存储啊,这些东西说实话知道了意义也不大。用Fiber来模拟函数调用栈是React团队研究了两年多才弄出来的架构,不建议大家看了点文章视频就背来背去面试时互相套路哈。不过作为一个程序员,简单了解一下它是什么还是可以的,而且动手操作了才更有意义。
把跳转的逻辑注释掉,加上打印fiber的代码(因为我装了React DevTools插件所以就在第一个分支里加了)
再随便改改next/pages下index.tsx的内容
'use client'
import type { NextPage } from 'next'
import styles from '../styles/Home.module.css'
import { useState, useRef } from 'react'
const Home: NextPage = () => {
const [a, setA] = useState(0)
const [b, setB] = useState(10)
const handleClick = () => {
setA(a + 1)
setB(b + 1)
}
const testRef = useRef('as')
return (
<div className={styles.container}>
<p>{a}</p>
<p>{b}</p>
<button onClick={handleClick}>add</button>
</div>
)
}
export default Home
到页面上,按住option/alt+左键,按照原来的方式使用click-to-component,终于见到FiberNode真容了。
看下hooks的保存方式(用到了useState和useRef)
改变一下a,b的值,然后查看对应fiber的alternate属性


加两个useEffect,看看它们的环形链表结构
差不多就按这个思路可以逐步探索React Fiber了,赶快动手试试吧!
转载自:https://juejin.cn/post/7367174168600150026