跨框架的前端源码定位,再加上点LLM

前几天改一个老项目,需要对页面进行简单调整,花了五分钟才找到它是哪个组件渲染的。这种事儿不是第一次了。

现代前端项目都是组件套组件,页面上的 DOM 和源码文件之间没有直接对应关系。每次要定位一个元素,基本就是 DevTools 看 class、猜组件名、编辑器里搜字符串,来回折腾。

于是我写了 DOM XRay:一个开发模式下运行的插件,按住快捷键点击页面元素,就能弹出源码面板,或者直接打开编辑器跳到对应文件和行号。

它支持 Vite、Webpack、Rspack、Next.js、Nuxt 3 和 Angular。


实际效果

装好插件后,页面加载完控制台会打印这样一段提示:

vbnet 复制代码
 DOM XRay is active
Usage:
  Hold Option + click  →  Open overlay to inspect source
  Hold Option + Command + click  →  Open directly in VSCode
  Press Esc while hovering  →  Cancel inspect mode

按住 Option 点击按钮,弹窗左侧直接显示这个按钮的源码文件和行号;再按一下"打开",编辑器就跳过去了。如果按住 Option + Command 点击,连弹窗都不出现,直接打开编辑器。


核心思路:把源码位置写进 DOM

DOM XRay 的做法并不复杂------在编译阶段把 data-source="filePath:line" 注入到每个元素上。运行时点击元素,向上找最近的 data-source,就能拿到源码位置。

真正麻烦的是怎么在不同框架里做这件事。

JSX / TSX

用 Babel 解析 AST,遍历 JSXOpeningElement,追加一个属性:

js 复制代码
node.attributes.push(
  t.jsxAttribute(
    t.jsxIdentifier("data-source"),
    t.stringLiteral(`${filePath}:${line}`)
  )
);

然后再用 @babel/generator 输出。React、SolidJS、Vue 的 JSX 都这么处理。

Vue 3 SFC

Vue 单文件组件的 template 有自己的编译流程。我用 @vue/compiler-sfc 拿到 template AST,再用 htmlparser2 把属性插回去。

Svelte

Svelte 的模板语法不太一样,用 svelte/compiler 解析后用 magic-string 做精准插入,避免破坏原有格式。

Nuxt 3

Nuxt 3 本质还是 Vue,但更深层。我在 Vue 编译器的 nodeTransforms 阶段注入属性,这样所有页面模板都会自动带上 data-source

Angular

Angular 的模板是独立 HTML 文件,编译器读取模板时才能处理。我用了一个不太优雅但有效的办法:monkey-patch fs.readFileSync,在 Angular 编译器读取 HTML 模板之前把属性注入进去。


项目结构

代码用 pnpm workspace 管理:

bash 复制代码
packages/
├── core          # 配置加载 + 源码转换
├── overlay-ui    # Web Components 弹窗
├── vite          # Vite 插件
├── webpack       # Webpack 5 插件
├── rspack        # Rspack 插件
├── nextjs        # Next.js 插件
├── nuxt          # Nuxt 3 模块
└── angular       # Angular 支持

core 负责所有框架的转换逻辑,各适配器只负责接入自己的构建流程。


几个有意思的实现细节

Web Components 弹窗

弹窗用原生 Web Components 实现,挂在 Shadow DOM 里。这样样式完全隔离,不会和用户页面的 CSS 互相影响。毕竟谁也不希望自己的调试工具被业务样式顶歪。

向上查找

点击的往往是按钮里的文字、卡片里的标题这种深层子元素,它自己不一定有 data-source。所以点击时我会从目标元素开始向上遍历,找到最近的带 data-source 的父元素。这样基本不会点空。

编辑器跳转

弹窗里的"打开"按钮会根据配置生成对应的 URL:

js 复制代码
vscode://file/Users/.../Button.tsx:14
cursor://file/Users/.../Button.tsx:14

window.open 打开就行。浏览器会唤起对应的编辑器并定位到行号。

Next.js 16 + Turbopack 的坑

Next.js 16 默认开 Turbopack,而 Turbopack 的 webpack loader 兼容不是对所有文件生效,尤其是 App Router 的 Server Components。

我试了几种方案,最后决定在 @dom-xray/core 的 Babel transform 里加一个 scriptContent 选项:遇到 layout.tsx 时,不只是在模块里插一段副作用代码,而是直接把 init 脚本作为 <script dangerouslySetInnerHTML> 插到 <body> 里。

因为 RSC 的模块代码在服务端运行,副作用代码不会真正在浏览器执行。但 <body> 里的 <script> 会被 SSR 渲染到 HTML 里,浏览器解析时自然会执行。

这样用户在 Next.js App Router 项目里完全不用手动改 layout.tsx,配置好 next.config.js 就行。


怎么用

Vite:

ts 复制代码
import domXray from "@dom-xray/vite";

export default defineConfig({
  plugins: [domXray({ editor: "vscode" })],
});

Next.js:

js 复制代码
const { withDomSelector } = require("@dom-xray/nextjs");

module.exports = withDomSelector(
  { reactStrictMode: true },
  { editor: "vscode" }
);

也可以写 dom-xray.config.json

json 复制代码
{
  "hotkey": { "mac": "option", "win": "alt" },
  "editor": "vscode",
  "enabled": true
}

还加了 AI Agent

弹窗右侧有一个输入面板,可以把当前元素的源码和提问一起发给 LLM。目前支持 Cursor、OpenCode 和 Claude Code,返回结果是 SSE 流式显示。

这个功能的出发点很简单:定位到源码之后,下一步往往是"这段代码是干什么的"或者"怎么改"。如果能直接在弹窗里问 AI,就不用再复制粘贴了。


写在最后

DOM XRay 不是那种解决大问题的工具,它解决的是一个很小的痛点:页面和源码之间的跳转。

写完之后我自己先用了一段时间,确实省了不少事。如果你也经常被这个问题烦到,可以试试看。

GitHub: github.com/ALittleFox/...

相关推荐
学网安的肆伍几秒前
【046-WEB攻防篇】注入工具&SQLMAP&Tamper编写&指纹修改&高权限操作&目录架构
前端·架构
Highcharts.js1 小时前
如何下载使用Highcharts Grid 开发web可编辑表格
前端·人工智能·grid 表格·web 表格·在线编辑表格·highcharts表格安装·表格开发工具
用户62960593247052 小时前
从“浏览器根本区分不了”到真正实现:刷新不退出,关闭标签页自动退出,前端判断浏览器关闭和刷新
前端
qetfw2 小时前
MWU:Vue 3 + FastAPI 的 MaaFramework 跨平台 WebUI 源码
前端·vue.js·python·fastapi·开源项目·效率工具
mfxcyh2 小时前
Vue3+Ant Design Vue 实现手动异步文件上传(含大小校验、弹窗上传)
前端·javascript·vue.js
wordbaby2 小时前
Web端热敏标签打印完整解决方案(QZ Tray + 译维A42)
前端
swipe2 小时前
03|Axios 请求进了后端之后:Controller、Request、Response 是怎么接住它的?
前端·后端·全栈
meilindehuzi_a2 小时前
Vue3进阶基础:指令修饰符、v-model表单绑定、动态样式、计算属性与侦听器实战
前端·javascript·vue.js
csdn2015_2 小时前
vue 前端运行命令
前端·javascript·vue.js
swipe2 小时前
02|从 `pnpm dev` 到 Spring Boot 启动:后端服务到底怎么跑起来?
前端·后端·全栈