React 项目里,如何快速定位你的组件源码?

如果说业务开发中最重要的能力,那定位代码的能力肯定是其中之一。

业务项目一般代码都很多,你拿到一个需求之后,可能改起来不难,但是要定位在哪里改比较难。

特别是接手别人写的代码的时候。

大家都是怎么在不熟悉的项目里定位的代码呢?

很多都学都是搜文案,搜 className。

这样没问题,但如果你用了 styled-component 之类的方案之后,className 都是动态生成的:

60d6ceecc96e9dccb66ff1d269a8f767.png

而且不少项目都做了国际化,你搜文案会搜到资源包里,而不是组件代码里:

74cd7e51dbbb370a60581dd15fcdd2b6.png

当然,你可以进一步根据国际化的 key 来搜索源码的对应组件。

但这样总归比较麻烦,而且还不一定能搜到准确的位置。

那有什么好的办法可以快速定位代码么?

有,就是 click-to-react-component。

我们创建个项目:

npx create-vte
397d5bcf96f367a6de248fcea7190ca0.png

改下 main.tsx:

ddfad7e88473c6d43b66015528ec6092.png

安装 antd,我们随便写几个页面:

npm install
npm install --save antd

App.tsx:

import React from 'react';
import { ColorPicker, Space } from 'antd';
import Aaa from './Aaa';

const Demo = () => (
  <div>
    <Space>
      <Space direction="vertical">
        <ColorPicker defaultValue="#1677ff" size="small" />
        <ColorPicker defaultValue="#1677ff" />
        <ColorPicker defaultValue="#1677ff" size="large" />
      </Space>
      <Space direction="vertical">
        <ColorPicker defaultValue="#1677ff" size="small" showText />
        <ColorPicker defaultValue="#1677ff" showText />
        <ColorPicker defaultValue="#1677ff" size="large" showText />
      </Space>
    </Space>
    <Aaa></Aaa>
  </div>
);

export default Demo;

Aaa.tsx:

import React, { useState } from 'react';
import { Slider, Switch } from 'antd';
import Bbb from './Bbb';

const Aaa: React.FC = () => {
  const [disabled, setDisabled] = useState(false);

  const onChange = (checked: boolean) => {
    setDisabled(checked);
  };

  return (
    <>
        <div>
            <Slider defaultValue={30} disabled={disabled} />
            <Slider range defaultValue={[20, 50]} disabled={disabled} />
            Disabled: <Switch size="small" checked={disabled} onChange={onChange} />
        </div> 
        <Bbb></Bbb>
    </>
  );
};

export default Aaa;

Bbb.tsx:

import React from 'react';
import { Card, Space } from 'antd';

const Bbb: React.FC = () => (
  <Space direction="vertical" size={16}>
    <Card title="Default size card" extra={<a href="#">More</a>} style={{ width: 300 }}>
      <p>Card content</p>
      <p>Card content</p>
      <p>Card content</p>
    </Card>
    <Card size="small" title="Small size card" extra={<a href="#">More</a>} style={{ width: 300 }}>
      <p>Card content</p>
      <p>Card content</p>
      <p>Card content</p>
    </Card>
  </Space>
);

export default Bbb;

这些都是从 antd 官网复制的 demo 代码。

不用管具体的代码内容,我们只需要看下怎么定位代码。

把开发服务跑起来:

npm run dev
730d11fa9d37a579a444294bf267bea2.png

渲染出来是这样的:

25cca52fa2e4ee20918bfef083aa98ac.png

如果我们想定位下面卡片的代码,就可以通过搜索文案或者 className:

f35b54b6aa2766f0b7835631785b747a.png

但复杂项目就不行了。

这时候可以引入 click-to-react-component:

npm install --save-dev click-to-react-component

在 main.tsx 引入下:

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
// @ts-ignore
import { ClickToComponent } from 'click-to-react-component';

ReactDOM.createRoot(document.getElementById('root')!).render(
    <>
        <ClickToComponent />
        <App />
    </>
)

可能有类型的报错,我们直接 @ts-ignore 忽略好了。

然后打开页面试一下:

c7c999d0658f3eabdfb1169c0f359eec.gif

可以看到,现在按住 option + 单击,就会直接打开它的对应的组件的源码。

如果按住 option + 右键单击,可以看到它的所有父级组件,然后选择一个组件打开:

c2d3205ed852e8805a08b387eb1baf35.png

c5ab011f3a5cd5ee7c77bad8c7266903.gif

这样在页面上看到了啥东西就可以直接打开它的组件代码来改,特别高效。

当然,我们的 demo 比较简单,比如真实项目里

我想改登录弹窗的表单,就可以点击输入框直接定位到对应组件的 Input。

对于大项目的维护来说真的超级方便。

知道了怎么用之后,我们再来探究下它的原理。

点击页面标签,就可以直接用 vscode 打开对应组件源码的行列号,是怎么实现的呢?

首先,怎么通过标签拿到对应组件的?

react 在标签上添加了 __reactFiber$ 开头的属性,可以拿到对应的 fiber 节点。

我们复制某个 dom 元素的选择器:

51635c325c16c041e06d8c02d790d94c.png

用 document.querySelector 取出来放到 el 变量。

ede76cd57faf35d321ceefb5af65ea71.png

然后你输入 el.__react 的时候会提示出一些属性:

3c972f1ccc6b4970260936ea80b2059a.png

__reactFiber$ 属性就是 dom 元素对应的 Fiber 节点。

__reactProps$ 属性就是这个组件的 props。

53b774b9f1f6dca6ef7c49b24cb6c411.png

而且,拿到 fiber 节点后还可以通过 _debugOwner 拿到 fiber 节点的父节点。

949edc88518671b37f90eff066a3935b.png

一层层向上找,直到为 null,

就是这个的实现原理:

093d31870440f47a496c2a6ffd54ad72.png

当然,fiber 节点还要根据 tag 来转为具体的类型。

a58bf4063c427053d2f8ad184c22ea10.png

比如 tag 为 10 是 Provider,tag 为 11 是 forwardRef 等。

这样,怎么从标签拿到对应的 fiber 节点我们就知道了。

那如何拿到组件在源码的文件和行列号呢?

这个通过 fiber 节点的 _debugSource 属性。

b4c11de04b0eec2b4047a03219b33c83.png

这个只有组件类型的 fiber 节点才有。

知乎就是用 react 开发的,因为你可以用 __reactFiber$ 属性拿到标签的 fiber 节点:

0fe3e1b31d2657b24da36bab8fa43c53.png

但是拿不到 __debugSource 属性,这个只有开发时才会有。

这个 _debugSource 属性是怎么加上的呢?react 并不知道组件在哪个文件定义的啊。

是 babel 插件做的:

99c76721f0178aba772f462b1ae9b42b.png

@babel/plugin-transform-react-jsx-source  这个插件内置在 @babel/preset-env 里,不用手动引入。

它会在编译 jsx 的时候添加 _source 属性,然后 react 源码里再把 _source 属性的值添加到 fiber._debugSource 上。

5130db09859591bd6302293873718483.png

那如何打开 vscode 呢?

只要在浏览器打开 vscode://file/文件绝对路径:行号:列号 的地址,就可以自动在 vscode 打开对应文件,并把光标定位到目标行列号。

a388a86c278d0475d235dd622c297971.png fb02082d6ab65935c63868568bfa6397.png

这样,整个流程我们都理清了,点击标签的时候怎么拿到对应的 fiber 节点,拿到所有父组件,拿到组件的行列号,然后打开 vscode。

此外,还有一些 ui 上的实现原理:

31af86de58d4ce82ee7f21ba37ef432a.gif

hover 的时候会框选对应组件。

它定义了 data-xxx 的样式。

034ecb0d1fda4845417f3e21c7fcef2b.png

然后通过 useState 创建了状态来保存当前 target。

mousemove 的时候修改 target。

68b8995467937b44c052a858b484abe5.png

当 target 改变,就会给它设置 dataset.xxx 属性。

3b3f857e5dc8f241545135f861e4d59d.png

这个 dataset 大家可能没用过:

如果你给一个 dom 元素设置 dataset.aaaBbbCccDdd = 1

那它就会有一个 data-aaa-bbb-ccc-ddd="1" 的属性。

23ff5cbc47d12bb67b0ef6d1612762d0.png 4a1510cac09118316bb6ee94ef82c11d.png

然后同样可以通过 dataset 取出来:

a1a8432b5c7648eed09762a64d2e6483.png

然后我们前面定义的 [data-xx] 的样式就生效了,就加上了框选的样式。

至于这个 popover,是用 @floating-ui 做的,所有浮动元素都可以用这个来做。

10656d22a8cdab6ae707c77c0371e7ee.png

此外,这个 click-to-react-component 需要在生产环境去掉么?

不用。

它内部做了处理:

f5992af25f6f94c2fa83195117192b14.png

只有开发环境才会渲染。

还有,我们是这个组件放在 main.tsx 里的,其实放哪都行。

22c6ca13fc4cf683e288a7a81a61de9b.png

因为它的事件都是绑定在 window上的:

89a8e16385cb1d27cfd469025d59c635.png

总结

对于业务代码来说,快速定位源码是很重要的。

因为改动可能很简单,但是项目大了定位在哪里改就比较麻烦了。

我们也可以通过搜索文案、className 的方式,但对于用了 styled-component、做了国际化的项目来说,这种方式也不行。

所以更推荐用 click-to-react-component 来快速定位源码。

只要在页面上 option + 单击,或者 option + 右键单击然后选一个组件,就可以直接打开对应组件源码的行列。

我们看了下它的原理,dom 元素有 __reactFiber$ 属性可以拿到对应 fiber 节点,然后 _debugOwner 拿到父节点 fiber。_debugSource 拿到源码文件路径和行列号。

然后通过 vscode://file/xxx 的方式直接 vscode 打开对应文件行列号。

这样就完成了点击页面元素,打开对应源码的功能。

这里的 _debugSource 是 babel 插件做的,在 @babel/preset-env 里,每个项目会都自动引入这个插件。

然后加上用 dataset.xx 定义属性和对应的 className,用 @floating-ui 实现 popover。

这就是 click-to-react-component 的实现原理了。

这个小组件还是很有用的,感觉是每个 react 项目必备,可以在项目里引入下试试。

- END -

如果您关注前端+AI 相关领域可以扫码进群交流

269602a6b9669a340e92e3a7b5ee7bec.png   1bac6a9571d64d8a6b14608634c44db0.jpeg

扫码进群2或添加小编微信进群1😊

关于奇舞团

奇舞团是 360 集团最大的大前端团队,非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

dd0060b26d820f7a1ec0e5aee988d1cf.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值