当前位置: 首页 > 科技观察

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

时间:2023-03-18 22:00:51 科技观察

作者|vivo互联网大前端团队-友辰一、背景现在的大型Vue项目基本都是多人协作开发,而且随着版本的迭代,Vue项目中的组件数量也会随之增加增加。如果此时,你负责的是不熟悉的页面功能开发,即使你刚刚加入这个项目,你又如何快速找到整个项目代码中相关组件的文件位置呢?想必大家都采取过以下方法:【查找类名】,在项目文件中查找页面DOM元素中的样式类名【查找路由】,根据页面链接查找匹配Vue路由的页面组件【查找someone],找到负责开发页面的人,询问对应的代码路径。以上方法确实可以帮助我们找到具体的代码文件路径,但是都需要手动查找,效率不是很高。还有其他更有效的方法吗??答案是肯定的。Vue官方提供了一个vue-devtools插件,可以在VSCode中自动打开相应页面组件的源码文件。运行路径如下:使用vue-devtools插件可以提高我们查找对应页面组件代码的效率,但是只能定位到对应的组件代码。如果我们想直接找到页面中某个元素相关的具体代码位置,需要在当前组件源代码中进行二次搜索,每次都先选择。组件,然后点击打开按钮,打开代码文件,速度不是特别快。针对这个问题,我们开发了一款轻量级的页面元素代码映射插件,可以通过点击页面元素,一键打开对应的代码源文件,准确定位到对应的代码行,无需手动搜索,可以大大提高为提高开发效率和体验,实际使用效果如下:二、实现原理整个插件主要分为3个功能模块:client,server,add-code-location,client发送具体请求到服务端,服务端收到请求后执行命令定位代码行,使用add-code-location模块进行源码转换。2.1客户端这里的客户端实际上是指浏览器。当我们点击一??个页面元素时,浏览器会向服务器端发送一个特定的请求。请求信息包括具体的代码文件路径和对应的代码行号信息。functionopenEditor(filePath){axios.get(`${protocol}//${host}:${port}/code`,{params:{filePath:`${filePath}`}}).catch(error=>{console.log(error)})}并通过事件代理的全局监听监听页面元素的点击事件,将点击事件绑定到文档上,监听键盘鼠标点击事件的组合发起一个请求定位代码行,避免与页面原生点击事件冲突。functionopenCode(e){if(isShiftKey||isMetaKey||e.metaKey||e.shiftKey){e.preventDefault()constfilePath=getFilePath(e.target)openEditor(filePath)}...}2.2服务器服务器side是指本地服务器,可以监听客户端发送的特定请求。当收到执行定位命令的请求时,执行VSCode打开代码文件命令,定位到对应的代码行。2.2.1webpackdevServer如果项目是用webpack搭建的,webpack的devServer开发服务器提供了一个before属性,可以用来监听发往开发服务器的请求。before:function(app){app.get('/code',function(req,res){if(req.query.filePath){//执行vscode定位代码行命令openCodeFile(req.query.filePath)...}...})}2.2.2viteconfigureServer如果项目是用Vite搭建的,可以在server端使用Vite插件监听特定的请求。Vite插件扩展了rollup插件接口,增加了一些特定的钩子函数,比如configureServer钩子,可以通过这个钩子函数配置开发服务器监听特定的请求。constcodeServer=()=>({name:'open-code-vite-server',configureServer(server){server.middlewares.use((req,res,next)=>{...if(pathname=='/code'){...if(filePath){openCodeFile(filePath)//执行vscode定位代码行命令...}res.end()}...})}})2.2.3执行VSCode定位命令当服务端监听到客户端发送的具体请求后,下一步就是执行VSCode定位代码行命令。其实VSCode编辑器是可以通过code命令来启动的,可以相应的使用一些命令行参数,例如:“code--reuse-window”或者“code-r”命令可以打开编辑器的文件或文件夹最后一个活动窗口;“code--goto”或“code-g”命令后面可以拼接具体的文件路径和行列号,使用“code-gfile:line:column”命令时,可以打开一个文件并定位到具体的文件行和列的位置。利用VSCode编辑器的这个特性,我们可以实现自动定位代码行的功能。可以从客户端发送的请求信息中获取对应的代码路径信息,然后使用node的child_process.exec方法执行VSCode定位代码行命令。constchild_process=require('child_process')functionopenCodeFile(path){letpathBefore=__dirname.substring(0,__dirname.search('node_modules'))letfilePath=pathBefore+pathchild_process.exec(`code-r-g${filePath}`)}另外,为了正常使用VSCode的Code命令,我们需要确保将VSCode的Code命令添加到环境变量中。Mac系统用户可以在VSCode界面使用command+shift+p快捷键,然后搜索Code,在路径中选择install‘code’命令;Windows用户可以找到VSCode安装位置的bin文件夹目录,将此目录添加到系统环境变量中。2.3add-code-location通过前面的介绍,应该了解了客户端和服务端的执行机制,执行location命令时需要获取页面元素的代码路径,具体的代码路径绑定在formofattributes当涉及到DOM元素时,我们需要使用add-code-location模块在编译时转换我们的源代码,并为DOM元素添加相应的代码路径属性。整个源码转换过程如下:2.3.1获取文件路径源码转换过程的第一步是获取代码文件的具体路径。对于webpack打包的项目,webpackloader适合处理源码字符串。loader的context这个对象包含一个resourcePath资源文件的path属性,利用这个属性我们可以很方便的获取到每个代码文件的具体路径。module.exports=function(source){const{resourcePath}=thisreturnsourceCodeChange(source,resourcePath)}对于Vite构建的项目,源码的转换也是通过插件完成的。vite插件有一个通用的hooktransform,可以用来对加载的模块内容进行转换,它接收两个参数,code参数代表源代码字符串,id参数是文件的完整路径。module.exports=function(){return{name:'add-code-location',transform(code,id){...returnsourceCodeChange(code,id)}}}2.3.2计算代码行号,然后遍历源码文件的过程中,需要对对应vue文件的template模板中的代码进行处理,将template模板的部分字符串用“\n”分割成一个数组,准确获取代码行号每行HTML标签通过数组的索引。functioncodeLineTrack(str,resourcePath){letlineList=str.split('\n')letnewList=[]lineList.forEach((item,index)=>{newList.push(addLineAttr(item,index+1,resourcePath))//添加location属性,index+1为具体代码行号})returnnewList.join('\n')}2.3.3添加location属性获取到代码文件路径和代码行号后,接下来就是就是给vue模板模板中划分的每一行label元素加上最后的position属性。这里使用正则替换的方式来添加position属性。对于每一行label元素,首先正则匹配所有元素的开始标签部分,比如{if(item&&item.indexOf('template')==-1){letregx=newRegExp(`${item}`,'g')letlocation=`${item}code-location="${resourcePath}:${line}"`lineStr=lineStr.replace(regx,location)}})}returnlineStr}2.4其他处理2.4.1源码相对路径添加对应sourcecodetotheDOMelement在使用location属性时,实际上使用的是相对路径,这样可以让DOM元素上的属性值更加简洁明了。node_modules文件夹一般在项目的根目录下,插件以npm包的形式安装在node_modules路径下。可以通过node的__dirname变量获取当前模块的绝对路径,这样在源码转换过程中就可以获取到项目的根路径,这样就可以获取到Vue代码文件的相对路径。letpathBefore=__dirname.substring(0,__dirname.search('node_modules'))letfilePath=filePath.substring(pathBefore.length)//vue代码的相对路径在服务端执行代码定位命令时,对应的代码将相对路径拼接成完整的绝对路径。2.4.2外部导入的组件add-code-location虽然可以在本地vue文件中添加代码路径信息,但是目前还没有办法将外部导入或者解析加载的组件进行转换,比如elementui组件。实际上,只会在elementui组件的最外层添加一行代码信息。此时客户端在获取到被点击元素的代码路径后,会进行向上查找的过程,获取其父节点的代码路径。如果仍然不存在,则继续寻找父节点的父节点,直到成功获取到代码路径。functiongetFilePath(element){if(!element||!element.getAttribute)returnnullif(element.getAttribute('code-location')){returnelement.getAttribute('code-location')}returngetFilePath(element.parentNode)}这样当你在后台点击elementui构建的页面元素时,也能成功定位并打开对应的代码文件。3.接入方案通过前面的介绍,大家一定对页面元素代码映射插件的原理有了清晰的认识,接下来介绍项目中的接入方式。访问方式其实很简单,可以选择只在本地开发环境访问,不用担心影响我们的生产环境,可以放心使用。3.1webpcak构建项目对于webpack构建的项目,首先在构建配置项vue.config.js文件中配置devServer和webpackloader,然后在main.js入口文件中初始化插件。//vue.config.jsconstopenCodeServe=require('@vivo/vue-dev-code-link/server')devServer:{...before:openCodeServe.before},if(!isProd){//本地开发环境config.module.rule('vue').test(/\.vue/).use('@vivo/vue-dev-code-link/add-location-loader').loader('@vivo/vue-dev-code-link/add-location-loader').end()}//main.jsimportopenCodeClientfrom'@vivo/vue-dev-code-link/client'if(process.env.NODE_ENV=='development'){openCodeClient.init()}3.2Vite构建项目Vite构建项目接入该插件方案与webpack构建项目基本相同,唯一不同的是在打包时引入了两个Vite插件配置文件。//vite.config.jsimportopenCodeServerfrom'@vivo/vue-dev-code-link/vite/server'importaddCodeLocationfrom'@vivo/vue-dev-code-link/vite/add-location'exportdefaultdefineConfig({plugins:[openCodeServer(),addCodeLocation()]}4.总结以上是对页面元素代码映射插件的核心原理和接入方案的介绍,实现方式充分利用了项目代码的过程打包和构建,打包工具本质上就是对源代码文件的转换和处理,当我们了解了打包工具的运行机制之后,我们就可以做一些我们认为有意义的事情了。以页面元素代码映射插件为例,使用它可以大大提高开发效率,不再需要花时间去寻找代码文件,尤其是对于页面和组件数量较多的项目,点击页面即可元素,一键打开对应代码文件,精准定位具体代码行,无需搜索,无需点哪里,soeasy!