Vue项目里,如何快速定位组件源码?

1 阅读4分钟

背景

作为前端程序员,我们并不是每次都会参与到新项目中,很多时候还要去维护旧代码。通常,我们为了熟悉项目,会先根据项目的README等,按照步骤把项目跑起来。然后再去看对应路由下的页面的展示效果。

其中,有的项目可能特别大,组件拆分也比较多(上千个)。可能本次需求就是更改一两个标签的样式,但是由于不熟悉项目,需要先在浏览器检查对应标签上的class等属性,找到一个比较特定的字符串,再到代码编辑器里去找到对应的组件。(然后发现很多重复的结果T_T)

有没有可以根据渲染出来的页面,像浏览器检查元素一样,快速定位到组件源代码的方案呢?

解决方案

code-inspector

Git地址:code-inspector

code-inspector-plugin 是一款基于 webpack/vite/rspack/nextjs/nuxt/umijs plugin 的提升开发效率的工具,点击页面上的 DOM,它能够自动打开你的 IDE 并将光标定位到 DOM 对应的源代码位置。

demo.gif

优势

  • 较高的社区活跃度(1k+ star
  • 跨技术栈的完整解决方案(webpack/vite/rspack/nextjs/nuxt/umijs plugin

缺点

  • 会在dom里插入一长串源码路径,导致class、style、以及自定义的属性等被挤到后面,不好辨认,尤其对于UnoCSS等项目

image.png

  • 只能找到dom元素所在位置(template),无法定位js代码位置

click-to-vue-component

npm包:click-to-vue-component

基于vue-cli,快速定位Vue组件源码

这是我根据团队需求,结合社区方案开发的一款仅针对Vue项目的元素定位源代码的解决方案。

在社区方案的基础上,提供了以下增强实现:

  • 解决了会在元素上写入一大段源码路径的问题(hash映射)
  • 提供了控制台日志跳转源代码的功能

使用方法

  1. 安装
npm install click-to-vue-component -D
  1. 在项目根目录的vue.config.js文件中添加以下代码:(@vue/cli-service@^5.x
const { defineConfig } = require('@vue/cli-service')
+const clickToVueComponent = require('click-to-vue-component')

module.exports = defineConfig({
+  chainWebpack: (config) => {
+    clickToVueComponent(config)
+  }
})
  1. 启动项目,打开测试页面,按住option,然后点击页面上的任意元素即可跳转到vscode源码所在位置。
标签跳转

标签跳转.gif

日志跳转

日志跳转.gif

实现原理

image.png

标签跳转

对于项目中的所有vue文件,会通过自定义的loader来处理。

因为vue源文件是一个类HTML的结构,所以这里会借助parse5来处理,得到所有的标签的位置信息:所在文件路径:行:列,存储到全局变量global.__vueSourceCodeLocMap上,然后在webpack构建完成后,将这个map信息通过websocket推送到客户端,并挂载在window上。

const { startOffset, startLine, startCol } = node.sourceCodeLocation
const codeLoc = `${filePath}:${startLine}:${startCol}`
const hash = CryptoJS.MD5(codeLoc).toString().substring(0, 8)
const sourceCodeLocation = ` data-code-loc='${hash}' `
const insertPos = startOffset + node.nodeName.length + 1
global.__vueSourceCodeLocMap[hash] = codeLoc
result =
  result.substring(0, insertPos) +
  sourceCodeLocation +
  result.substring(insertPos)
  • window.__vueSourceCodeLocMap

image.png

然后我们会通过自定义的webpack plugin,在输出的index.html中插入客户端逻辑,主要就是监听页面的点击事件,然后获取到所点击的元素,以及在元素上绑定的data-code-loc属性,通过data-code-loc得到hash值,然后在window.__vueSourceCodeLocMap对应标签的位置信息,并通过vscode://file协议打开vscode,并跳转到源码位置。

日志跳转

同标签跳转一样,通过loader,我们全局搜索源代码中所有的console,然后在同行console后面在插入一个带样式的console,这样就会在控制台渲染一个类似按钮的效果:

image.png

点击这个查看源码就能通过浏览器打开vscode,定位到源码位置。

export default function (source: string) {
  try {
    const { resourcePath } = this
    const lines = source.split(/\r|\n/g)
    const newLinse = lines.slice()

    lines.forEach((str, index) => {
      const res = str.match(/console.log\(.*?\)\s*$/g)
      if (res) {
        const ln = index + 1
        const col = str.indexOf('console') + 1
        newLinse[index] += `; console.log('%c查看源码%cvscode://file${resourcePath}%3A${ln}%3A${col}', 'padding: 1px 3px; border-radius: 6px; background-color: #ff6400; color: #fff;', 'font-size: 0.8px; line-height: 18px; background: transparent; margin-left: -48px;')`
      }
    })

    // 返回修改后的源代码
    return newLinse.join('\n')
  } catch (error) {
    console.log(error)
    return source
  }
}

热更新

因为我们采用了hash映射源码位置的做法,所以每次热更新的时候我们需要去更新window上挂载的__vueSourceCodeLocMap信息,否则对于新的代码,就无法找到源码。

所以我们在项目启动的时候,会同时开启一个websocket服务器,每当热更新的时候,我们就会把最新的map信息推送到客户端

compiler.hooks.emit.tapAsync('ClickToVueComponentPlugin', (compilation, callback) => {
  // ...
  const vueSourceCodeLocMap = JSON.stringify(global.__vueSourceCodeLocMap) 
  getWsInstance().then(ws => {
    ws.send(vueSourceCodeLocMap)
  })
  callback()
})

客户端:

socket.addEventListener('message', (event) => {
  const data = event.data
  try {
    window.__vueSourceCodeLocMap = JSON.parse(data || '{}')
  } catch {}
})

注:此文受神光大佬写的一篇文章React 项目里,如何快速定位你的组件源码?启发而来

参考