下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

如何在 Vue 项目中,通过点击 DOM 自动定位VSCode中的代码行?

作者:匿名     来源: 编程语言点击数:567发布时间: 2022-06-09 23:49:54

标签: Vue协作开发项目

大神带你学编程,欢迎选课

  现在大型的 Vue项目基本上都是多人协作开发,并且随着版本的迭代,Vue 项目中的组件数也会越来越多,如果此时让你负责不熟悉的页面功能开发,甚至你才刚刚加入这个项目,那么怎么样才能快速找到相关组件在整个项目代码中的文件位置呢?

  一、背景

  现在大型的 Vue项目基本上都是多人协作开发,并且随着版本的迭代,Vue 项目中的组件数也会越来越多,如果此时让你负责不熟悉的页面功能开发,甚至你才刚刚加入这个项目,那么怎么样才能快速找到相关组件在整个项目代码中的文件位置呢?想必大家都有采取过以下这几种方法:

  【搜类名】,在工程文件里搜索页面 DOM元素中的样式类名

  【找路由】,根据页面链接找到Vue路由匹配的页面组件

  【找人】,找到当初负责开发该页面的人询问对应的代码路径

  以上几种方法确实能够帮助我们找到具体的代码文件路径,但都需要人工去搜索,并不是很高效,那有没有其它更高效的方式呢?

  答案是有的。Vue官方就提供了一款 vue-devtools 插件,使用该插件就能自动在 VSCode 中打开对应页面组件的源代码文件,操作路径如下:

  

  使用vue-devtools插件可以很好地提高我们查找对应页面组件代码的效率,但只能定位到对应的组件代码,如果我们想要直接找到页面上某个元素相关的具体代码位置,还需要在当前组件源代码中进行二次查找,并且每次都要先选择组件,再点击打开按钮才能打开代码文件,不是特别快捷。

  针对这个问题,我们开发了轻量级的页面元素代码映射插件,使用该插件可以通过点击页面元素的方式,一键打开对应代码源文件,并且精准定位对应代码行,无需手动查找,能够极大地提高开发效率和体验,实际的使用效果如下:

  二、实现原理

  整个插件主要分为3个功能模块:client、server、add-code-location,client端发送特定请求给server端,server端接收到该请求后执行定位代码行命令,而add-code-location模块用于源码的转换。

  2.1 client

  client端这里其实就是指浏览器,我们在点击页面元素时,浏览器就会发送一个特定请求给server端,该请求信息包含了具体的代码文件路径和对应代码行号信息。

  复制

  1. function openEditor(filePath) {

  2. axios

  3. .get(`${protocol}//${host}:${port}/code`, {

  4. params: {

  5. filePath: `${filePath}`

  6. }

  7. })

  8. .catch(error => {

  9. console.log(error)

  10. })

  11. }

  而监听页面元素的点击事件则通过事件代理的方式全局监听,给document绑定了点击事件,监听键盘和鼠标点击组合事件来发起定位代码行请求,避免和页面原生的click事件发生冲突。

  复制

  1. function openCode(e) {

  2. if (isShiftKey || isMetaKey || e.metaKey || e.shiftKey) {

  3. e.preventDefault()

  4. const filePath = getFilePath(e.target)

  5. openEditor(filePath)

  6. }

  7. ...

  8. }

  2.2 server

  server端是指本地起的一个服务器,可以监听client端发送的特定请求,当接收到执行定位命令的请求时,执行VSCode打开代码文件命令,并定位到对应的代码行。

  2.2.1 webpack devServer

  如果是采用webpack构建的项目,webpack的devServer开发服务器已经提供了一个before属性,可以通过它来监听发送给开发服务器的请求。

  复制

  1. before: function (app) {

  2. app.get('/code', function (req, res) {

  3. if (req.query.filePath) {

  4. // 执行vscode定位代码行命令

  5. openCodeFile(req.query.filePath)

  6. ...

  7. }

  8. ...

  9. })

  10. }

  2.2.2 vite configureServer

  如果是采用Vite构建的项目,可以使用Vite插件来实现server端监听特定请求,Vite插件扩展于rollup插件接口,并且在原有的基础上增加了一些特有的钩子函数,例如configureServer钩子,通过该钩子函数可以用于配置开发服务器来监听特定的请求。

  复制

  1. const codeServer = () => ({

  2. name: 'open-code-vite-server',

  3. configureServer(server) {

  4. server.middlewares.use((req, res, next) => {

  5. ...

  6. if (pathname == '/code') {

  7. ...

  8. if (filePath) {

  9. openCodeFile(filePath) // 执行vscode定位代码行命令

  10. ...

  11. }

  12. res.end()

  13. }

  14. ...

  15. })

  16. }

  17. })

  2.2.3 执行 VSCode 定位命令

  当server端监听到client端发送的特定请求后,接下来就是执行VSCode定位代码行命令。实际上,VSCode编辑器是可以通过code命令来启动,并且可以相应使用一些命令行参数,例如:

  "code --reuse-window"或"code -r"命令可以打开最后活动窗口的文件或文件夹;"code --goto"或"code -g"命令后面可以拼接具体文件路径和行列号,当使用"code -g file:line:column"命令时可以打开某个文件并定位到具体的行列位置。

  利用 VSCode 编辑器的这个特性,我们就能实现自动定位代码行功能,对应的代码路径信息可以从client端发送的请求信息当中获得,再借助node的child_process.exec方法来执行VSCode定位代码行命令。

  复制

  1. const child_process = require('child_process')

  2. function openCodeFile(path) {

  3. let pathBefore = __dirname.substring(0, __dirname.search('node_modules'))

  4. let filePath = pathBefore + path

  5. child_process.exec(`code -r -g ${filePath}`)

  6. }

  另外,为了正常使用 VSCode 的 Code命令,我们需要确保添加VSCode Code命令到环境变量当中。Mac系统用户可以在VSCode界面使用command+shift+p快捷键,然后搜索Code 并选择install 'code' command in path;Windows用户可以找到VSCode安装位置的bin文件夹目录,并将该目录添加到系统环境变量当中。

  2.3 add-code-location

  通过前面的介绍,大家应该了解了client端和server端的执行机制,并且在执行定位命令时需要获取到页面元素的代码路径,而具体的代码路径是以属性的方式绑定到了DOM元素上,这时候就需要用到add-code-location模块在编译时转换我们的源码,并给 DOM元素添加对应的代码路径属性。

  整个源码转换处理流程如下:

  2.3.1 获取文件路径

  源码转换过程的第一步是获取代码文件的具体路径,对于webpack打包的项目来说,webpack loader用来处理源码字符串再合适不过,loader的上下文this对象包含一个resourcePath资源文件的路径属性,利用这个属性我们很容易就能获得每个代码文件的具体路径。

  复制

  1. module.exports = function (source) {

  2. const { resourcePath } = this

  3. return sourceCodeChange(source, resourcePath)

  4. }

  对于Vite构建的项目来说,源码的转化操作也是通过插件来完成,Vite插件有通用的钩子transform,可用于转换已加载的模块内容,它接收两个参数,code参数代表着源码字符串,id参数是文件的全路径。

  复制

  1. module.exports = function() {

  2. return {

  3. name: 'add-code-location',

  4. transform(code, id) {

  5. ...

  6. return sourceCodeChange(code, id)

  7. }

  8. }

  9. }

  2.3.2 计算代码行号

  接着在遍历源码文件的过程中,需要处理对应Vue文件template模板中的代码,以“\\n”分割template模板部分字符串为数组,通过数组的索引即可精准得到每一行html标签的代码行号。

  复制

  1. function codeLineTrack(str, resourcePath) {

  2. let lineList = str.split('\\n')

  3. let newList = []

  4. lineList.forEach((item, index) => {

  5. newList.push(addLineAttr(item, index + 1, resourcePath)) // 添加位置属性,index+1为具体的代码行号

  6. })

  7. return newList.join('\\n')

  8. }

  2.3.3 添加位置属性

  在获取到代码文件路径和代码行号以后,接下来就是对Vue template模板中分割的每一行标签元素添加最终的位置属性。这里采用的是正则替换的方式来添加位置属性,分别对每一行标签元素先正则匹配出所有元素的开始标签部分,例如<div、<span、<img等,然后将其正则替换成带有code-location属性的开始标签,对应的属性值就是前面获取的代码路径和对应标签的行号。

  复制

  1. function addLineAttr(lineStr, line, resourcePath) {

  2. let reg = /<[\\w-]+/g

  3. let leftTagList = lineStr.match(reg)

  4. if (leftTagList) {

  5. leftTagList = Array.from(new Set(leftTagList))

  6. leftTagList.forEach(item => {

  7. if (item && item.indexOf('template') == -1) {

  8. let regx = new RegExp(`${item}`, 'g')

  9. let location = `${item} code-location="${resourcePath}:${line}"`

  10. lineStr = lineStr.replace(regx, location)

  11. }

  12. })

  13. }

  14. return lineStr

  15. }

  2.4 其他处理

  2.4.1 源码相对路径

  在给DOM元素添加对应的源码位置属性时,实际上采用的是相对路径,这样可以使得DOM元素上的属性值更加简洁明了。node_modules文件夹通常是在项目的根目录下,而插件是以npm包的形式安装在node_modules路径下,利用node的__dirname变量可以获得当前模块的绝对路径,因此在源码转换过程中就可以获取到项目的根路径,从而就能获得Vue代码文件的相对路径。

  复制

  1. let pathBefore = __dirname.substring(0, __dirname.search('node_modules'))

  2. let filePath = filePath.substring(pathBefore.length) // vue代码相对路径

  在server端执行代码定位命令时,再将对应的代码相对路径拼接成完整的绝对路径。

  2.4.2 外部引入组件

  add-code-location虽然可以对本地的Vue文件进行代码路径信息的添加,但是对于外部引入或解析加载的组件目前是没有办法进行转换的,例如element ui组件,实际上的代码行信息只会添加在element ui组件的最外层。这时候client端在获取点击元素的代码路径时会做一个向上查找的处理,获取其父节点的代码路径,如果还是没有,会继续查找父节点的父节点,直到成功获取代码路径。

  复制

  1. function getFilePath(element) {

  2. if (!element || !element.getAttribute) return null

  3. if (element.getAttribute('code-location')) {

  4. return element.getAttribute('code-location')

  5. }

  6. return getFilePath(element.parentNode)

  7. }

  这样就可以在点击后台element ui搭建的页面元素时,也能成功定位打开对应代码文件。

  三、接入方案

  通过前面的介绍,想必大家对页面元素代码映射插件原理有了清晰的了解,接下来就介绍一下在项目中的接入方式。接入方式其实很简单,并且可以选择只在本地开发环境接入,不用担心对我们的生产环境造成影响,放心使用。

  3.1 webpcak构建项目

  对于webpack构建的项目来说,首先在构建配置项vue.config.js文件中配置一下devServer和webpack loader,接着在main.js入口文件中初始化插件。

  复制

  1. // vue.config.js

  2. const openCodeServe = require('@vivo/vue-dev-code-link/server')

  3. devServer: {

  4. ...

  5. before: openCodeServe.before

  6. },

  7. if (!isProd) { // 本地开发环境

  8. config.module

  9. .rule('vue')

  10. .test(/\\.vue/)

  11. .use('@vivo/vue-dev-code-link/add-location-loader')

  12. .loader('@vivo/vue-dev-code-link/add-location-loader')

  13. .end()

  14. }

  15. // main.js

  16. import openCodeClient from '@vivo/vue-dev-code-link/client'

  17. if (process.env.NODE_ENV == 'development') {

  18. openCodeClient.init()

  19. }

  3.2 Vite构建项目

  Vite构建项目接入该插件的方案和webpack构建项目基本上一致,唯一不一样的地方在于打包配置文件里引入的是两个Vite插件。

  复制

  1. // vite.config.js

  2. import openCodeServer from '@vivo/vue-dev-code-link/vite/server'

  3. import addCodeLocation from '@vivo/vue-dev-code-link/vite/add-location'

  4. export default defineConfig({

  5. plugins: [

  6. openCodeServer(),

  7. addCodeLocation()

  8. ]

  9. }

  四、总结

  以上就是对页面元素代码映射插件核心原理和接入方案的介绍,实现的方式充分利用了项目代码打包构建的流程,实际上无论是哪个打包工具,本质上都是对源码文件的转换处理,当我们理解了打包工具的运行机制后,就可以做一些自己认为有意义的事。就拿页面元素代码映射插件来说,使用它可以极大提升开发效率,不再需要花费时间在寻找代码文件上,特别是页面数和组件数比较多的项目,只需点击页面元素,即可一键打开对应代码文件,精准定位具体代码行,无需查找,哪里不会点哪里,so easy!

  来源: vivo互联网技术

    >>>>>>点击进入编程语言专题

赞(9)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程