如何快速定位页面对应的代码位置?click-to-component 源码解析

1,190 阅读3分钟

如何快速定位页面对应的代码位置?click-to-component 源码解析

介绍和使用

click-to-component 是一个强大的 React 开发工具,它能让开发者在浏览器中通过 Option+Click(或 Alt+Click)直接定位到 React 组件的源代码。

next.gif

如图所示 Option+Click,直接从浏览器中点击的 DOM 元素跳转到对应的 React 组件源代码位置。

  • 安装
npm install click-to-react-component
  • 将 ClickToComponent 添加到你的应用中,组件有两个参数
    • editor: 指定点击后用哪个编辑器打开源码,默认值 vscode
    • pathModifier: 用于修改跳转到对应的源码文件路径
 // 导入 ClickToComponent
 import { ClickToComponent } from 'click-to-react-component';
 import ReactDOM from 'react-dom/client';

 const root = ReactDOM.createRoot(document.getElementById('root'));
 root.render(
   <>
     {/* 添加 ClickToComponent 到应用中 */}
     <ClickToComponent 
      editor={'cursor'}
      pathModifier={(path) => path}
     />
     <App />
   <>
 );

源码解析

click-to-component 的核心能力,是从浏览器中点击的 DOM 元素跳转到对应的 React 组件源代码位置。

阅读源码前先思考两个问题

  • onClick 获取的是 DOM 元素,它是如何对应上 React 组件的?

  • 如何获取 React 组件的源代码位置?

DOM 元素与 React 组件的映射

image.png

getReactInstanceForElement.js

click-to-component 通过两种方式实现映射:

  1. 优先使用 react-devtools 提供的能力
  2. 降级方案:通过 DOM 元素上的特殊属性(_reactFiber)获取
react-devtools 获取 DOM 元素对应的 React 组件

image.png

通过 DOM 元素上的特殊属性(_reactFiber)获取对应 React 组件

image.png

_reactFiber 这属性是怎么来的那?

image.png

ReactDOMComponentTree.js

React DOM 内部用来建立 DOM 节点和 Fiber 节点之间联系的关键函数

如何获取 React 组件的源代码位置?

/**
 * @typedef {import('react-reconciler').Fiber} Fiber
 * @typedef {import('react-reconciler').Source} Source
 */

/**
 * @param {Fiber} instance
 */
export function getSourceForInstance(instance) {
  // 检查实例是否包含调试源码信息
  // _debugSource 只在开发环境下存在
  if (!instance._debugSource) {
    return
  }

  // 从 _debugSource 中解构需要的信息
  const {
    // It _does_ exist!
    // @ts-ignore Property 'columnNumber' does not exist on type 'Source'.ts(2339)
    columnNumber = 1,  // 源码列号,默认值为 1
    fileName,          // 源文件路径
    lineNumber = 1,    // 源码行号,默认值为 1
  } = instance._debugSource

  return { columnNumber, fileName, lineNumber }
}

通过 getSourceForInstance.js,可以看出是通过 React Fiber 实例的 _debugSource 属性获取这个组件的源代码位置

这个 _debugSource 属性的值是怎么来的?

babel-plugin-transform-react-jsx-source 转义的时候加上的

image.png

什么情况下不具有 _debugSource

image.png

ClickToComponent onClick事件处理流程中,

  • 先调用 getReactInstancesForElement 获取点击元素对应的组件实例链(包含当前组件及其所有父级组件),
  • 后使用 getSourceForInstance 从组件实例链中筛选出第一个包含源代码位置信息(即具有 _debugSource 属性)的组件实例

image.png

在 getReactInstancesForElement(多了个 s) 中

  • 首先调用了 getReactInstanceForElement 获取 DOM 元素对应的直接 React 组件实例

  • 然后沿着组件树向上遍历,通过 _debugOwner 收集所有父级组件实例

  • 最终返回包含当前组件及其所有父级组件实例的数组

上面的操作都是为了找到具有 _debugSource 属性的组件实例,那什么情况下当前不具有 _debugSource,递归的父组件实例却有 _debugSource ?

node_modules 下引入组件