找回密码
 会员注册
查看: 23|回复: 0

LocatorJS源码分析

[复制链接]

2万

主题

0

回帖

6万

积分

超级版主

积分
64122
发表于 2024-10-12 20:56:41 | 显示全部楼层 |阅读模式
LocatorJS 源码分析 329 LocatorJS 源码分析http://zoo.zhengcaiyun.cn/blog/article/locatorjs前言前段时间发现了一个好玩的东西 -- LocatorJS。它可以在浏览器界面快速定位到 IDE 里的具体代码,并且支持 React、Preact、Solid、Vue 和 Svelte 这些主流的框架。顿时觉得好神奇,简直就是开发神器,YYDS!于是,马上按照官方文档,一步步在项目中进行操作一番。对于react 项目,它提供了 2 种接入方式。第一种是基于 data-id,第二种是基于 react devtools 和 Chrome 插件。对于它的实现特别好奇,所以拉取它的源码,进行探索一番。intro.gifintro.gifintro.gifintro.gifintro.gif准备工作1、拉取源码把源码拉到本地,可以看到如下图目录该项目把 Chrome 插件和示例都放在 apps 文件夹里面,packages 里面放着 babel-jsx、locatorjs、runtime 等公共代码。可以看到这个项目是使用 lerna 管理的多包项目,那需要用 lerna bootstrap 进行依赖安装。2、启动项目依赖安装完毕,然后使用 yarn dev 启动项目。可以看到 turbo 编译速度飞快,端口信息很快就被日志刷没了。于是只能通过代码里查看,具体的示例在哪个端口。我们以 vite-react-clean-project 项目为例,找到对应的 package.json,发现它启动的是 3348 端口,于是用浏览器打开 localhost:3348 页面。image.png3、加载 Chrome 插件因为我们是采用的 Chrome 插件和 react devtools 进行代码分析。所以必须安装这 2 个插件。react devtools 如果没有安装,可以去 Chrome 插件市场里搜索安装。locatorjs Chrome 插件我们加载开发版本进行代码调试。(进去 Chrome 扩展管理 -> 加载我们项目 apps/extension/build/development_chrome 这个文件夹)image.png源码分析以上准备工作完毕之后,你可能已经发现 locatorjs 可以用了。如果说不能正常使用,可以参考官网,安装以下的 babel plugins。那么 Chrome 插件到底做了什么?我们带着问题去看源码吧。1、Chrome 插件打开 apps/extension/src/pages 这个文件,发现里面就是常规的插件代码。Background 放着插件的后台代码,目前里面为空Content 放着插件与浏览器内容页面的代码,与页面代码一起执行Popup 放着插件 popup 页面的代码这个插件的执行入口就在 content.ts。如果对插件不熟悉的同学,可以参考 Extensions - Chrome Developers(https://developer.chrome.com/docs/extensions/) 插件开发网站。1.1 apps/extension/src/pages/Content/index.tsimage.png从上面代码可以看出,在 content.ts 中把 hook.bundle.js 注入到当前页面中,把 client.bundle.js 赋值给 document.documentElement.dataset.locatorClientUrl,其他代码都是一些监听事件,可以先不管。1.2 apps/extension/src/pages/Hook/index.tsimage.pnghook.bundle.js 就是 hook.ts 打包之后的文件名,它主要做了 2 件事情,第一,确保 react devtools 已经被安装了;第二,注入 runtime script。1.3 apps/extension/src/pages/Hook/insertRuntimeScript.ts该文件主要是监听 DOMContentLoaded,然后注入我们上面 document.documentElement.dataset.locatorClientUrl 这个文件,也就是 client.bundle.js。image.png在 insertScript 方法中,作者还考虑到了 iframe 的情况image.png1.4 apps/extension/src/pages/ClientUI/index.ts接着我们来看 client.js 到底做了什么。兴致勃勃地打开 ClientUI 文件夹,结果只有一句代码。直接引入了 @locator/runtime,也就是 runtime 代码。image.png这样看来 Chrome 插件很简单,就检查了 react devtools 的安装情况,然后动态注入 locator 的 runtime 代码。接下来,我们就定位到 runtime 代码。2、locator/runtime2.1 packages/runtime/src/index.tsimage.png从代码中可以看到,它就是我们在官网看到的 2 种不同的方式。不管我们是 Chrome 插件引入还是通过 @locator/runtime 引入,最终都会执行 initRuntime 函数,无非就是参数不一样。image.png2.2 packages/runtime/src/initRuntime.ts该文件中,它通过 shadow Dom 的方案去添加了一个 locatorjs 自己的全局样式和容器,来隔离 CSS,以免对页面进行影响。image.png接着,它又引入了 components/Runtime 组件,并且考虑到了 SSR 的情况。我们是客户端渲染,所以走的是第二种情况。image.png2.3 packages/runtime/src/components/Runtime.tsx在这个文件中终于看到了真面目,最终渲染的就是 Runtime 这个函数组件。该组件使用了  solidjs。函数最上面是一些变量的声明。image.png接着是在 document 上绑定了 mouseover、keydown、keyup、click、 scroll 事件image.png当我们把鼠标浮到元素上,并且按住 option 按键的时候,就显示出当前元素的表框。这时候触发的就是 mouseover 和 keydown 事件。image.pngmouseover 处理函数,就把当前的 element 选中;keydown 处理函数使 holdingModKey 为 true。在这种情况下,页面渲染的就是 MaybeOutline 组件image.png2.4 packages/runtime/src/components/MaybeOutline.tsx在 MaybeOutline 组件里面,主要的就是获取当前 element 的信息。然后根据 elementInfo 去渲染红色的外边框。image.png2.5 packages/runtime/src/adapters/getElementInfo.tsx接着我们来看 getElementInfo 这个方法到底做了什么。可以看到,它用来适配器设计模式。根据适配 id 的不同,走不同的逻辑。现在我们来看 react 项目是怎么获取元素信息的。image.png2.6 packages/runtime/src/adapters/react/reactAdapter.ts在这个方法里,我们看到了熟悉的影子 fiber。这里最重要的函数就是 findFiberByHtmlElement,通过命名也能知道。就是通过 html 元素去查询 fiber 节点。image.png2.7 packages/runtime/src/adapters/react/findFiberByHtmlElement.ts通过 findFiberByHostInstance 就可以找到当前元素的 fiber 节点。那 fiber 节点里有哪些信息呢?我添加了打印信息。image.png通过下图可以看到,_debugSource 里面居然自带了当前元素位置信息,还有 lineNumber、columnNumber,难怪可以具体定位到代码中。有了这个信息,跳转到 vscode 还不是轻而易举。image.png跳转的事件发生在 click 处理函数中,最终调用了 window.open 方法。vscode:// 这个是协议跳转,electron 本身就支持。image.pngimage.png那 _debugSource 的信息是怎么来的呢?那是 @babel/plugin-transform-react-jsx-source 的功劳。2.8 @babel/plugin-transform-react-jsx-sourceimage.png从 babel 官网的示例可以看出,这个 plugin 可以把当前 tag 的位置信息添加到 __source 上。然后通过 React.createElement 把 __source 属性挂到 _source 下面。image.pngimage.png然后在创建 fiber 的时候,把元素的 _source 添加到 _debugSource。image.png2.9 packages/runtime/src/components/ComponentOutline.tsx那元素外面的红框是如何实现的呢?于是我们定位到 ComponentOutline.tsximage.png可以看到,它是通过 bbox 的属性计算出了整个元素的外边框。bbox 又是通过 fiber 元素的getBoundingClientRect 计算出来的。到这里就全部解开了 locatorjs 的神秘面纱了。image.png总结通过查看这个源码还是有很多收获。第一,你能学到 Chrome 插件的开发;第二,solidjs 的使用,虽然本文没有展开讲;第三,shadow Dom 样式隔离方案;第四,适配器模式的应用;第五,熟悉 react fiber。本文是基于 react devtools 方式的来解析 locatorjs 的原理。官方还有一种 data-id 来实现的方案,它会把位置信息绑定在 data-id 上,然后点击的时候去处理信息,具体的源码实现还是留给各位读者自己去探索。参考文献:Chrome Extensions content scripts - Chrome Developers(https://developer.chrome.com/docs/extensions/mv3/content_scripts/)LocatorJS(https://www.locatorjs.com/),Base 杭州,一个富有激情和技术匠心精神的成长型团队。前端团队,一个年轻富有激情和创造力的前端团队。团队现有 80 余个前端小伙伴,平均年龄 27 岁,近 4 成是全栈工程师,妥妥的青年风暴团。成员构成既有来自于阿里、网易的“老”兵,也有浙大、中科大、杭电等校的应届新人。团队在日常的业务对接之外,还在物料体系、工程平台、搭建平台、智能化平台、性能体验、云端应用、数据分析、错误监控及可视化等方向进行技术探索和实战,推动并落地了一系列的内部技术产品,持续探索前端技术体系的新边界。如果你想改变一直被事折腾,希望开始能折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊… 如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的前端团队的成长历程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 ZooTeam@cai-inc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-26 13:03 , Processed in 1.394816 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表