点击上方“蓝色字体”,选择“设为星标

做积极向上的前端人!

1. 前言

我的文章尽量写得让想看源码又不知道怎么看的读者能看懂。我都是推荐使用搭建环境断点调试源码学习哪里不会点哪里边调试边看,而不是硬看。正所谓:授人与鱼不如授人予渔

阅读本文后你将学到:

  1. 如何解决该功能报错问题

  2. 如何调试学习源码

  3. launch-editor-middleware、launch-editor 等实现原理

1.1 短时间找不到页面对应源文件的场景

不知道你们有没有碰到这样的场景,打开你自己(或者你同事)开发的页面,却短时间难以找到对应的源文件。

这时你可能会想要是能有点击页面按钮自动用编辑器打开对应文件的功能,那该多好啊。

vue-devtools提供了这样的功能,也许你不知道。我觉得很大一部分人都不知道,因为感觉很多人都不常用vue-devtools

open-in-editor

你也许会问,我不用vue,我用react有没有类似功能啊,有啊,请看react-dev-inspector[3]。你可能还会问,支持哪些编辑器呀,主流的 vscode、webstorm、atom、sublime 等都支持,更多可以看这个列表 Supported editors[4]

本文就是根据学习尤大写的 launch-editor[5] 源码,本着知其然,知其所以然的宗旨,探究 vue-devtools「在编辑器中打开组件」功能实现原理。

1.2 一句话简述其原理

code path/to/file

一句话简述原理:利用nodejs中的child_process,执行了类似code path/to/file命令,于是对应编辑器就打开了相应的文件,而对应的编辑器则是通过在进程中执行ps xWindow则用Get-Process)命令来查找的,当然也可以自己指定编辑器。

1.3 打开编辑器无法打开组件的报错解决方法

而你真正用这个功能时,你可能碰到报错,说不能打开这个文件。

Could not open App.vue in the editor.To specify an editor, specify the EDITOR env variable or add "editor" field to your Vue project config.

控制台不能打开编辑器的错误提示

这里说明下写这篇文章时用的是 Windows 电脑,VSCode 编辑器,在Ubuntu子系统下使用的终端工具。同时推荐我的文章使用 ohmyzsh 打造 windows、ubuntu、mac 系统高效终端命令行工具,用过的都说好

解决办法也简单,就是这句英文的意思

1.3.1 方法一:先确保在终端能用命令打开你使用的编辑器,文中以VSCode为例

如果你的命令行本身就不能运行code等命令打开编辑器,那肯定是报错的。这时需要把VSCode注入到命令行终端中。注入方法也简单。我的交流群里有小伙伴提供了mac电脑的截图。

mac 电脑在 VSCode command + shift + pWindows 则是 ctrl + shift + p。然后输入shell,选择安装code。如下图:

Install 'code' command in PATH

这样就能在终端中打开VSCode的了。

如果能在终端打开使用命令编辑器能打开,但实际上还是报错,那么大概率是没有识别到你的编辑器。那么可以通过方法二设置指定编辑器。

1.3.2 方法二:具体说明编辑器,在环境变量中说明指定编辑器

vue项目的根目录下,对应本文则是:vue3-project,添加.env.delelopment文件,其内容是EDITOR=code这里重点说明下,我的 vue-cli 版本是4.5.12,好像在vue-cli 3.5及以上版本才支持自定义EDITOR这样的环境变量

# .env.development
# 当然,我的命令行终端已经有了code这个命令。
EDITOR=code

不用指定编辑器的对应路径(c/Users/lxchu/AppData/Local/Programs/Microsoft VS Code/bin/code),因为会报错。为什么会报错,因为我看了源码且试过。因为会被根据空格截断,变成c/Users/lxchu/AppData/Local/Programs/Microsoft,当然就报错了。

也有可能你的编辑器路径有中文路径导致报错,可以在环境变量中添加你的编辑器路径。

如果你通过以上方法,还没解决报错问题。欢迎留言,或者加我微信 ruochuan12 交流。毕竟电脑环境不一,很难保证所有人都能正常执行,但我们知道了其原理,就很容易解决问题

接下来我们从源码角度探究「在编辑器中打开组件」功能的实现原理。

2. vue-devtools Open component in editor 文档

探究原理之前,先来看看vue-devtools官方文档。

vuejs/vue-devtools[6]文档

Open component in editor
To enable this feature, follow this guide[7].

这篇指南中写了在Vue CLI 3中是开箱即用

Vue CLI 3 supports this feature out-of-the-box when running vue-cli-service serve.

也详细写了如何在Webpack下使用。

# 1. Import the package:
var openInEditor = require('launch-editor-middleware')
# 2. In the devServer option, register the /__open-in-editor HTTP route:
devServer: {before (app) {app.use('/__open-in-editor', openInEditor())}
}
# 3. The editor to launch is guessed. You can also specify the editor app with the editor option. See the supported editors list.
# 用哪个编辑器打开会自动猜测。你也可以具体指明编辑器。这里显示更多的支持编辑器列表
openInEditor('code')
# 4. You can now click on the name of the component in the Component inspector pane (if the devtools knows about its file source, a tooltip will appear).
# 如果`vue-devtools`开发者工具有提示点击的组件的显示具体路径,那么你可以在编辑器打开。

同时也写了如何在Node.js中使用等。

Node.js
You can use the launch-editor[8] package to setup an HTTP route with the /__open-in-editor path. It will receive file as an URL variable.

查看更多可以看这篇指南[9]

3. 环境准备工作

熟悉我的读者,都知道我都是推荐调试看源码的,正所谓:哪里不会点哪里。而且调试一般都写得很详细,是希望能帮助到一部分人知道如何看源码。于是我特意新建一个仓库open-in-editor[10] git clone https://github.com/lxchuan12/open-in-editor.git,便于大家克隆学习。

安装vue-cli

npm install -g @vue/cli
# OR
yarn global add @vue/cli
node -V
# v14.16.0
vue -V
# @vue/cli 4.5.12
vue create vue3-project
# 这里选择的是vue3、vue2也是一样的。
# Please pick a preset: Default (Vue 3 Preview) ([Vue 3] babel, eslint)
npm install
# OR
yarn install

这里同时说明下我的vscode版本。

code -v
1.55.2

前文提到的Vue CLI 3开箱即用Webpack使用方法。

vue3-project/package.json中有一个debug按钮。

debug示意图

选择第一项,serve vue-cli-service serve

我们来搜索下'launch-editor-middleware'这个中间件,一般来说搜索不到node_modules下的文件,需要设置下。当然也有个简单做法。就是「排除的文件」右侧旁边有个设置图标「使用“排查设置”与“忽略文件”」,点击下。

其他的就不赘述了。可以看这篇知乎回答:vscode怎么设置可以搜索包含node_modules中的文件?[11]

这时就搜到了vue3-project/node_modules/@vue/cli-service/lib/commands/serve.js中有使用这个中间件。

4. vue-devtools 开箱即用具体源码实现

接着我们来看Vue CLI 3开箱即用具体源码实现。

// vue3-project/node_modules/@vue/cli-service/lib/commands/serve.js
// 46行
const launchEditorMiddleware = require('launch-editor-middleware')
// 192行
before (app, server) {// launch editor support.// this works with vue-devtools & @vue/cli-overlayapp.use('/__open-in-editor', launchEditorMiddleware(() => console.log(`To specify an editor, specify the EDITOR env variable or ` +`add "editor" field to your Vue project config.\n`)))// 省略若干代码...
}

点击vue-devtools中的时,会有一个请求,http://localhost:8080/__open-in-editor?file=src/App.vue,不出意外就会打开该组件啦。

open src/App.vue in editor

接着我们在launchEditorMiddleware的具体实现。

5. launch-editor-middleware

看源码时,先看调试截图。

debug-launch

launch-editor-middleware中间件中作用在于最终是调用 launch-editor 打开文件。

// vue3-project/node_modules/launch-editor-middleware/index.js
const url = require('url')
const path = require('path')
const launch = require('launch-editor')module.exports = (specifiedEditor, srcRoot, onErrorCallback) => {// specifiedEditor => 这里传递过来的则是 () => console.log() 函数// 所以和 onErrorCallback 切换下,把它赋值给错误回调函数if (typeof specifiedEditor === 'function') {onErrorCallback = specifiedEditorspecifiedEditor = undefined}// 如果第二个参数是函数,同样把它赋值给错误回调函数// 这里传递过来的是undefinedif (typeof srcRoot === 'function') {onErrorCallback = srcRootsrcRoot = undefined}// srcRoot 是传递过来的参数,或者当前node进程的目录srcRoot = srcRoot || process.cwd()// 最后返回一个函数, express 中间件return function launchEditorMiddleware (req, res, next) {// 省略 ...}
}

上一段中,这种切换参数的写法,在很多源码中都很常见。为的是方便用户调用时传参。虽然是多个参数,但可以传一个或者两个

可以根据情况打上断点。比如这里我会在launch(path.resolve(srcRoot, file), specifiedEditor, onErrorCallback)打断点。

// vue3-project/node_modules/launch-editor-middleware/index.js
module.exports = (specifiedEditor, srcRoot, onErrorCallback) => {// 省略上半部分return function launchEditorMiddleware (req, res, next) {// 根据请求解析出file路径const { file } = url.parse(req.url, true).query || {}// 如果没有文件路径,则报错if (!file) {res.statusCode = 500res.end(`launch-editor-middleware: required query param "file" is missing.`)} else {// 否则拼接路径,用launch打开。launch(path.resolve(srcRoot, file), specifiedEditor, onErrorCallback)res.end()}}
}

6. launch-editor

跟着断点来看,走到了launchEditor函数。

// vue3-project/node_modules/launch-editor/index.js
function launchEditor (file, specifiedEditor, onErrorCallback) {// 解析出文件路径和行号列号等信息const parsed = parseFile(file)let { fileName } = parsedconst { lineNumber, columnNumber } = parsed// 判断文件是否存在,不存在,直接返回。if (!fs.existsSync(fileName)) {return}// 所以和 onErrorCallback 切换下,把它赋值给错误回调函数if (typeof specifiedEditor === 'function') {onErrorCallback = specifiedEditorspecifiedEditor = undefined}// 包裹一层函数onErrorCallback = wrapErrorCallback(onErrorCallback)// 猜测当前进程运行的是哪个编辑器const [editor, ...args] = guessEditor(specifiedEditor)if (!editor) {onErrorCallback(fileName, null)return}// 省略剩余部分,后文再讲述...
}

6.1 wrapErrorCallback 包裹错误函数回调

onErrorCallback = wrapErrorCallback(onErrorCallback)

这段的代码,就是传递错误回调函数,wrapErrorCallback 返回给一个新的函数,wrapErrorCallback 执行时,再去执行 onErrorCallback(cb)。

我相信读者朋友能看懂,我单独拿出来讲述,主要是因为这种包裹函数的形式在很多源码里都很常见

这里也就是文章开头终端错误图Could not open App.vue in the editor.输出的代码位置。

// vue3-project/node_modules/launch-editor/index.js
function wrapErrorCallback (cb) {return (fileName, errorMessage) => {console.log()console.log(chalk.red('Could not open ' + path.basename(fileName) + ' in the editor.'))if (errorMessage) {if (errorMessage[errorMessage.length - 1] !== '.') {errorMessage += '.'}console.log(chalk.red('The editor process exited with an error: ' + errorMessage))}console.log()if (cb) cb(fileName, errorMessage)}
}

6.2 guessEditor 猜测当前正在使用的编辑器

这个函数主要做了如下四件事情:

  1. 如果具体指明了编辑器,则解析下返回。

  2. 找出当前进程中哪一个编辑器正在运行。macOSLinuxps x 命令
    windows 则用 Get-Process 命令

  3. 如果都没找到就用 process.env.VISUAL或者process.env.EDITOR。这就是为啥开头错误提示可以使用环境变量指定编辑器的原因。

  4. 最后还是没有找到就返回[null],则会报错。

const [editor, ...args] = guessEditor(specifiedEditor)
if (!editor) {onErrorCallback(fileName, null)return
}
// vue3-project/node_modules/launch-editor/guess.js
const shellQuote = require('shell-quote')
const childProcess = require('child_process')module.exports = function guessEditor (specifiedEditor) {// 如果指定了编辑器,则解析一下,这里没有传入。如果自己指定了路径。// 比如 c/Users/lxchu/AppData/Local/Programs/Microsoft VS Code/bin/code //   会根据空格切割成 c/Users/lxchu/AppData/Local/Programs/Microsoftif (specifiedEditor) {return shellQuote.parse(specifiedEditor)}// We can find out which editor is currently running by:// `ps x` on macOS and Linux// `Get-Process` on Windowstry {//  代码有删减if (process.platform === 'darwin') {const output = childProcess.execSync('ps x').toString()// 省略} else if (process.platform === 'win32') {const output = childProcess.execSync('powershell -Command "Get-Process | Select-Object Path"', {stdio: ['pipe', 'pipe', 'ignore']}).toString()// 省略} else if (process.platform === 'linux') {const output = childProcess.execSync('ps x --no-heading -o comm --sort=comm').toString()}} catch (error) {// Ignore...}// Last resort, use old skool env varsif (process.env.VISUAL) {return [process.env.VISUAL]} else if (process.env.EDITOR) {return [process.env.EDITOR]}return [null]
}

看完了 guessEditor 函数,我们接着来看 launch-editor 剩余部分。

6.3 launch-editor 剩余部分

以下这段代码不用细看,调试的时候细看就行。

// vue3-project/node_modules/launch-editor/index.js
function launchEditor(){//  省略上部分...if (process.platform === 'linux' &&fileName.startsWith('/mnt/') &&/Microsoft/i.test(os.release())) {// Assume WSL / "Bash on Ubuntu on Windows" is being used, and// that the file exists on the Windows file system.// `os.release()` is "4.4.0-43-Microsoft" in the current release// build of WSL, see: https://github.com/Microsoft/BashOnWindows/issues/423#issuecomment-221627364// When a Windows editor is specified, interop functionality can// handle the path translation, but only if a relative path is used.fileName = path.relative('', fileName)}if (lineNumber) {const extraArgs = getArgumentsForPosition(editor, fileName, lineNumber, columnNumber)args.push.apply(args, extraArgs)} else {args.push(fileName)}if (_childProcess && isTerminalEditor(editor)) {// There's an existing editor process already and it's attached// to the terminal, so go kill it. Otherwise two separate editor// instances attach to the stdin/stdout which gets confusing._childProcess.kill('SIGKILL')}if (process.platform === 'win32') {// On Windows, launch the editor in a shell because spawn can only// launch .exe files._childProcess = childProcess.spawn('cmd.exe',['/C', editor].concat(args),{ stdio: 'inherit' })} else {_childProcess = childProcess.spawn(editor, args, { stdio: 'inherit' })}_childProcess.on('exit', function (errorCode) {_childProcess = nullif (errorCode) {onErrorCallback(fileName, '(code ' + errorCode + ')')}})_childProcess.on('error', function (error) {onErrorCallback(fileName, error.message)})
}

这一大段中,主要的就是以下代码,用子进程模块。简单来说子进程模块有着执行命令的能力。

const childProcess = require('child_process')if (process.platform === 'win32') {// On Windows, launch the editor in a shell because spawn can only// launch .exe files._childProcess = childProcess.spawn('cmd.exe',['/C', editor].concat(args),{ stdio: 'inherit' })} else {_childProcess = childProcess.spawn(editor, args, { stdio: 'inherit' })
}

行文至此,就基本接近尾声了。原理其实就是利用nodejs中的child_process,执行了类似code path/to/file命令。

7. 总结

这里总结一下:首先文章开头通过提出「短时间找不到页面对应源文件的场景」,并针对容易碰到的报错情况给出了解决方案。其次,配置了环境跟着调试学习了vue-devtools中使用的尤大写的 yyx990803/launch-editor[12]

7.1 一句话简述其原理

我们回顾下开头的原理内容。

code path/to/file

一句话简述原理:利用nodejs中的child_process,执行了类似code path/to/file命令,于是对应编辑器就打开了相应的文件,而对应的编辑器则是通过在进程中执行ps xWindow则用Get-Process)命令来查找的,当然也可以自己指定编辑器。

最后还能做什么呢。

可以再看看 umijs/launch-editor[13] 和 react-dev-utils/launchEditor.js[14] 。他们的代码几乎类似。

也可以利用Node.js做一些提高开发效率等工作,同时可以学习child_process等模块。

也不要禁锢自己的思维,把前端禁锢在页面中,应该把视野拓宽

Node.js是我们前端人探索操作文件、操作网络等的好工具

参考资料

[1]

若川: https://lxchuan12.gitee.io

[2]

本文仓库地址: https://github.com/lxchuan12/open-in-editor.git


················· end ·················

点击关注下方卡片关注我????????盖茨被逐出微软董事会真相曝光:长期跟员工搞地下情,27年婚姻中出轨不断,人设已崩~
面试官问 Vue 性能优化,我该怎么回答?
每天穿女装上班的大厂程序员:我知道自己是个男生
一个普通前端程序员走过的路程漫画 | 害,曾经我也是一个菱角分明的人啊!卧槽!VSCode 竟然可以找对象了!
这个神器牛逼了!点击页面元素 VSCode 居然跳转到了组件?
优秀前端人都知道的H5 移动端调试全攻略~
如何从零开始开发一个 VS Code 插件?
漫画 | 老板,我想申请加薪~如果觉得这篇文章还不错,来个【分享、点赞、在看】三连吧,让更多的人也看到~

卧槽,这个Vue调试神器牛逼!点击页面元素居然能直接打开对应组件文件…相关推荐

  1. Vue调试神器vue-devtools - 插件下载安装

    文章目录 Google如何安装调试Vue的神器`vue-devtools` ? 安装介绍: demo实例 - 演示代码: 相关文章链接: 1. https://github.com/vuejs/vue ...

  2. VUE 调试神器 vue-devtools

    概述 vue-devtools是一款基于chrome游览器的插件,用于调试vue应用,这可以极大地提高我们的调试效率.接下来我们就介绍一下vue-devtools的安装. chrome商店直接安装 v ...

  3. 居然能查看微信访客记录,这款神器牛逼了。

    用过QQ空间都知道查看访问记录,当然看更多的访客是需要充值的. 但是用了微信,微信不能查看朋友圈访问记录. 2019年2月份的时候,网上曾流传过一张关于微信最新版本更新日志的截图. 更新的功能显示: ...

  4. Python3行代码翻译70种语言!这个OCR神器牛逼了!

    今天给大家介绍一个超级简单且强大的OCR文本识别工具:easyocr. 这个模块支持70多种语言的即用型OCR,包括中文,日文,韩文和泰文等. 下面是这个模块的实战教程. 1.准备 请选择以下任一种方 ...

  5. 点击页面元素,这个Vite插件竟然帮我打开了Vue组件文件!超级好用!

    大家好,我是若川.持续组织了8个月源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步.同时极力推荐订阅我写的<学习源码整体架构系列& ...

  6. vue中v-for循环选中点击的元素并对该元素添加样式

    html: <div v-for = "(item,index) in items" :class = "active == index ? 'addclass' ...

  7. Python爬虫:最牛逼的 selenium爬取方式!

    Python爬虫:最牛逼的 selenium爬取方式! 作为一个男人 在最高光的时刻 这是小编准备的python爬虫学习资料,加群:700341555即可免费获取! Python爬虫:最牛逼的 sel ...

  8. 牛逼!IntelliJ IDEA 从入门到上瘾!

    本文共计 1.5 W 字,80 张图介绍 IDEA 中令人相见恨晚的技巧,本文中从入门.简单项目创建开始,介绍 IDEA 中多光标操作.常用配置.插件.版本控制等等.一定包含你在别的文章没有看到的内容 ...

  9. vue选中点击的元素_vue中v-for循环选中点击的元素并对该元素添加样式操作

    相信大家都会遇到这种情况:v-for循环时,我只需要点击到的元素做出相应反应,其他的元素不变:但是往往所有v-for循环出的元素都会变化.如下面的代码:我需要点击到的元素添加一个类样式,其他元素不变, ...

最新文章

  1. SAP零售商品类别层次:为什么(业务视角)?
  2. MaximumGap的思想
  3. 黑马vue---14、v-model双向绑定
  4. Keil生成汇编文件、bin文件
  5. ⑨④-如果不发展就可能面临生存窘境
  6. 135. 分发糖果002(贪心算法+思路+详解)
  7. mysql 种子表_mysql之3种子查询
  8. 你知道怎么解决DB读写分离,导致数据不一致问题吗?
  9. python车牌识别_Python-车牌识别
  10. Java集成openCV实现图片背景切换
  11. 威纶通触摸屏与西门子PLC200之间的无线通讯
  12. 台式计算机 cpu型号大全,电脑cpu的型号有哪些?
  13. PHP框架高级编程——应用Symfony、CakePHP
  14. cad怎么画立体图形教学_cad怎么绘制三维图形,cad三维制图实例图文教程
  15. 软考嵌入式系统设计师
  16. Centos服务器上使用移动硬盘(NTFS分区)
  17. Python计算经济距离权重矩阵
  18. html读秒倒计时,读秒倒计时
  19. WPS计算机一级考试知识点,计算机一级考试WPS练习题及答案
  20. (6.1)各种USB接口简介

热门文章

  1. 路由器的配置,IP,NAT、DHCP
  2. SIMULINK过零检测作用
  3. 一门课程学习转录组调控分析和R可视化第十四期 (线上线下开课)
  4. 优动漫PAINT中误删工具怎么办?
  5. nginx proxy_pass转发规则解析
  6. 单击屏幕亮屏流程分析
  7. 管理是管事,领导是带人!
  8. 微信小程序连接第三方接口
  9. 商城项目商品列表页的渲染实现(含动图)
  10. Android实现仿QQ登录可编辑下拉菜单