VUE3+Vite+ts实现浏览器点击元素打开对应的vue文件并定位到对应内容区域

1 篇文章 0 订阅
1 篇文章 0 订阅

1.背景

目前的许多项目都是使用vue进行开发的,当一个vue项目做大时维护起来非常麻烦,当我们需要找到某个文件的时候非常麻烦,传统快速定位到某个文件可以使用类名、或者路由路径等方式找到对应的文件。

c1de1006c3fa4241a23a51b5e2ba3514.gif

2、vue devTools介绍

vue devTools是vue开发的一个插件,它同时也具备着能够快速定位到某个文件的功能:

648a6be7a2da43d3adbc6344bf9b39ba.png

点击这个图标然后点击某个元素即可打开某个vue文件的详情

617788489b7441b0a81548aa0ae819e7.png

点击右上角的图标即可在编辑器中打开该文件

3、自定义快速打开元素插件

虽然vue提供的插件已经足够满足我们日常开发需求了,但是我觉得速度依然不够快于是决定采用自定义插件的方式实现按住shift+鼠标左键的方式快速打开某个元素对应的vue文件。

3.1 以VUE3+Vite为例子

创建openCodeClient.ts文件,用于向当前server端发起网络请求

import axios from 'axios'

function openEditor(filePath: string): void {
    console.log(filePath);
  axios
    .get('/code', {
      params: { filePath }
    })
    .catch(error => {
      console.error(error)
    })
}

function getFilePath(element: HTMLElement | null): string | null {
  if (!element || !element.getAttribute) return null
  const codeLocation = element.getAttribute('code-location')
  if (codeLocation) {
    return codeLocation
  }
  return getFilePath(element.parentElement)
}

function openCode(e: MouseEvent): void {
  const isShiftKey = e.shiftKey
  const isMetaKey = e.metaKey
  if (isShiftKey || isMetaKey) {
    e.preventDefault()
    const target = e.target as HTMLElement
    const filePath = getFilePath(target)
    if (filePath) {
      openEditor(filePath)
    }
  }
}

function init(): void {
  document.addEventListener('click', openCode, true)
}

export default { init }

3.2 创建server.ts文件

用于接收client端发起的网络请求并打开vscode对应的vue文件

import { exec } from 'child_process'
import type { Plugin } from 'vite';
import { IncomingMessage, ServerResponse } from 'http'

function openCodeFile(path: string): void {
  exec(`code -r -g ${path}`, (error, stdout, stderr) => {
    if (error) {
      console.error(`Error executing command: ${error.message}`)
      return
    }
    if (stderr) {
      console.error(`Error output: ${stderr}`)
      return
    }
    console.log(`Command output: ${stdout}`)
  })
}

const codeServer = (): Plugin => ({
  name: 'open-code-vite-server',
  configureServer(server) {
    server.middlewares.use((req: IncomingMessage, res: ServerResponse, next: () => void) => {
      const url = new URL(req.url || '', `http://${req.headers.host}`)
      const filePath = url.searchParams.get('filePath')
      if (url.pathname === '/code' && filePath) {
        openCodeFile(filePath)
        res.statusCode = 200
        res.end()
      } else {
        next()
      }
    })
  }
})

export default codeServer

3.3 创建vite-plugin-add-code-location.ts文件

用来给当前页面每一个元素添加一个自定义属性,属性名叫add-code-location,值是对应的文件路径+代码行

import type { Plugin } from 'vite';

export default function addCodeLocationPlugin(): Plugin {
  return {
    name: 'add-code-location',
    transform(code: string, id: string) {
      return sourceCodeChange(code, id);
    },
  };
}

function sourceCodeChange(source: string, resourcePath: string): string {
  // 只对 .vue 文件进行处理
  if (resourcePath.endsWith('.vue')) {
    const templateMatch = source.match(/<template>([\s\S]*?)<\/template>/);
    if (templateMatch) {
      const templateContent = templateMatch[1];
      const modifiedTemplate = codeLineTrack(templateContent, resourcePath);

      // 替换原有的 template 内容
      source = source.replace(templateContent, modifiedTemplate);
    }
  }
  return source;
}

function codeLineTrack(str: string, resourcePath: string): string {
  const lineList = str.split('\n');
  const newList = lineList.map((item, index) => addLineAttr(item, index + 1, resourcePath));
  return newList.join('\n');
}

function addLineAttr(lineStr: string, line: number, resourcePath: string): string {
  const reg = /<[\w-]+/g;
  let leftTagList = lineStr.match(reg);
  if (leftTagList) {
    leftTagList = Array.from(new Set(leftTagList));
    leftTagList.forEach(item => {
      if (item && item.indexOf('template') === -1) {
        const regx = new RegExp(`${item}`, 'g');
        const location = `${item} code-location="${resourcePath}:${line}"`;
        lineStr = lineStr.replace(regx, location);
      }
    });
  }
  return lineStr;
}

4、配置vite.config.ts

在vite.config.ts中引入我们写好的自定义插件,我把两个函数都放在最前面了因为放后面可能会被其它插件干扰到

3186c5714e03474692d492b592dbe202.png

5、配置tsconfig.node.json

这里的主要目的是让vite能识别根目录下的文件不然在vite.config.ts中引入插件路径会报错,或者不生效的情况

98ebbb0502bb4d3f883d6f4d093a91fc.png

识别src下的所有文件,让它能够识别出我们自定义的插件

6、在main.ts中加载全局监听事件监听内容

判断当前是否属于开发环境,如果是则可以触发监听,比较生产根本不需要,监听用户如果按住shift+左键点击某个元素则会获取到该元素上的code-location属性拿到文件路径包括行号

import openCodeClient from '@/utils/openCodeClient';
if (import.meta.env.MODE === 'development') {
  openCodeClient.init();
  // 添加全局点击事件监听
  // 添加全局点击事件监听
  document.addEventListener('click', (event) => {
    const target = event.target as HTMLElement;
    const codeLocation = target.getAttribute('code-location');
    // 检查是否按住了 Shift 键并且点击了左键
    if (event.shiftKey && event.button === 0 && codeLocation) {
      fetch(`/code?filePath=${encodeURIComponent(codeLocation)}`)
        .then((response) => {
          if (response.ok) {
            console.log('Code opened successfully');
          } else {
            console.error('Failed to open code');
          }
        })
        .catch((error) => {
          console.error('Error fetching code location:', error);
        });
    }
  });
}

7、测试结果

如果插件正常加载成功,按f12打开元素审查会看到每个元素都有一个自定义属性,如果不成功则再检查看看是哪里有问题了

7c8788ceeb2643eaab23e2a1fb50fa80.png

该项目借鉴了一位前端大佬的文章,文章链接。文章里有详细的设计思路和代码片段。 本来打算直接用大佬的项目,但是对方说暂时只在他们公司内部使用,故本人自己写了一个简单版的。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值