前言
通过本文可以学到哪些知识?
- 如何在Chrome中安装vue-devtools插件
- 创建vue3 demo项目,并调试一键打开对应组件文件
- 通过关联图可清晰整个流程的大体脉络
- 通过源码解剖原理
- vite项目如何进行调试使用
- 我为什么看源码?以及看源码能学到什么呢?
1、安装Chrome浏览器扩展插件vue-devtools
1、打开Chrome浏览器,点击最右侧`三个点` 2、点击:更多工具 3、继续点击:扩展程序 4、然后点击:页面左上角`三横杠` 5、继续点击:打开Chrome 网上应用店 6、然后搜索框输入:`vue` 7、出现列表:选择'Vue.js devtools (https://vuejs.org)',点击此项进入 8、然后点击`添加至Chrome` 9、出现弹框后,点击`添加扩展程序`,执行完,便添加完毕
注意了,这个插件也就是在开发的时候可以使用哟。
2、创建项目并测试
// 更新脚手架 npm update -g @vue/cli //查看版本 vue -V // 5.0.8,此版本下不需要配置任何便可调试打开对应组件文件 // 创建项目 vue create vue3-ts-demo // 选择一系列的模版组合,创建完毕,并安装依赖 // 直接通过VSCode打开此项目 // 直接运行 npm run serve
浏览器打开页面
会发现浏览器调试右侧出现一个标签页vue
点击如图所示的图标,就是打开对应的组件文件。可能初次使用,点击好几次都没有响应,然后返回到VSCode编辑器后发现,如下图所示的错误,该怎么办呢?
我现在使用的mac电脑,在VSCode中尝试快捷键command + shift + p
,如果是window电脑可能是 Ctrl + Shift + p
,然后输入shell
,选择安装code
。如下图:
选中安装Code命令
后,重新运行项目,然后再次点击如图箭头所示位置
就会跳转到VSCode并打开对应的组件文件
原理:其实就是通过code
命令,进行打开对应的文件,你可以在项目根目录进行测试code package.json
或者其他文件,稍等片刻便会打开该文件。
3、一图胜千言
其实调试完上面的代码以后,我才开始想这其中的原理到底是怎么实现的,心里还在嘀咕有那么神奇吗?
其实作为菜如鸡的我一开始还真想不明白,于是我不断的阅读源码,一步一步的揭开了神秘的面纱,后来才发现也不过尔尔。
这里需要搞清楚几个小问题,也是我在梳理的过程中花费时间比较长的疑惑。
1.vue-devTools也算是Chrome插件,是Chrome DevTools扩展,开发者工具里面的Tab扩展页
2.点击按钮发送的请求,然后通过webpack里的devServer进行请求拦截
3.请求拦截后,调用尤大大写的小插件 launch-editor
4.而launch-editor最终调用的child_process.spawn来执行命令
5.最终执行vscode中的code命令
(我上面写过如何去安装)
4、从源码的角度解剖原理
4.1、准备工作
- vue-devtools chrome浏览器插件已经安装
- 通过vuecli 创建好了vue3的项目
4.2、vue-devTools 插件中点击按钮
查看的代码当然就在devtools
中
按钮的模版代码
<VueButton v-if="fileIsPath" v-tooltip="{ content: $t('ComponentInspector.openInEditor.tooltip', { file: data.file }), html: true }" icon-left="launch" class="flat icon-button" @click="openFile()" /> // tooltip显示的字符串 ComponentInspector: { openInEditor: { tooltip: 'Open <mono><<insert_drive_file>>{{file}}</mono> in editor', }, },
通过代码可以发现按钮事件openFile
function openFile () { if (!data.value) return openInEditor(data.value.file) }
可以看到里面又调用了openInEditor
export function openInEditor (file) { // Console display const fileName = file.replace(/\\/g, '\\\\') const src = `fetch('${SharedData.openInEditorHost}__open-in-editor?file=${encodeURI(file)}').then(response => { if (response.ok) { console.log('File ${fileName} opened in editor') } else { const msg = 'Opening component ${fileName} failed' const target = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : {} if (target.__VUE_DEVTOOLS_TOAST__) { target.__VUE_DEVTOOLS_TOAST__(msg, 'error') } else { console.log('%c' + msg, 'color:red') } console.log('Check the setup of your project, see https://devtools.vuejs.org/guide/open-in-editor.html') } })` if (isChrome) { target.chrome.devtools.inspectedWindow.eval(src) } else { // eslint-disable-next-line no-eval eval(src) } }
其中主要的代码便是src
变量的赋值,以及通过eval或者target.chrome.devtools.inspectedWindow.eval
来执行src
字符串。
src变量中的代码,不看错误处理的代码,其实也非常简单 就是一个fetch请求,其中变量SharedData.openInEditorHost
就是一个/
fetch(`/__open-in-editor?file=${encodeURI(file)}`).then(response => { if (response.ok) { console.log('File ${fileName} opened in editor') } else { const msg = 'Opening component ${fileName} failed' } })
这里最重要的其实就是这个请求路径__open-in-editor
4.3、再来看我们创建的demo项目vue3-ts-demo
通过一个截图来看看代码位置
代码其实是在node_modules/@vue/cli-service../serve.js
,也就是运行一下命令后执行的脚本代码
"serve": "vue-cli-service serve",
执行完上述命令,会将__open-in-editor
请求地址注册到devServer,后续如果有请求过来,地址刚好一样,便会调用下面的launchEditorMiddleware
,并传递了一个匿名函数。
const launchEditorMiddleware = require('launch-editor-middleware') // .....省略很多暂时用不到的代码 devServer.app.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` )))
4.4、中间件launch-editor-middleware
上面launchEditorMiddleware最终使用的便是该npm包,里面只有一个文件,代码如下所示
const url = require('url') const path = require('path') const launch = require('launch-editor') module.exports = (specifiedEditor, srcRoot, onErrorCallback) => { // specifiedEditor 传递进来的cnsole.log匿名函数 // srcRoot和onErrorCallback都为undefined if (typeof specifiedEditor === 'function') { onErrorCallback = specifiedEditor specifiedEditor = undefined } if (typeof srcRoot === 'function') { onErrorCallback = srcRoot srcRoot = undefined } // 获得当前执行node命令时候的文件夹目录名 // /Users/user/Desktop/aehyok/github/demo/vue3-ts-demo srcRoot = srcRoot || process.cwd() // 初始化先,并不执行 return function launchEditorMiddleware (req, res, next) { const { file } = url.parse(req.url, true).query || {} if (!file) { res.statusCode = 500 res.end(`launch-editor-middleware: required query param "file" is missing.`) } else { launch(path.resolve(srcRoot, file), specifiedEditor, onErrorCallback) res.end() } } }
这里初始化的时候,并不会执行return function launchEditorMiddleware
。等到下次有请求为__open-in-editor
的时候,才会真正的调用 launchEditorMiddleware
。
4.5、点击open xxx path in editor
看顶部引用
const launch = require('launch-editor')
4.6、最后的执行函数 launch-editor
然后开始执行如下函数
function launchEditor (file, specifiedEditor, onErrorCallback) { // 最重要的是解析出文件名 const parsed = parseFile(file) let { fileName } = parsed const { lineNumber, columnNumber } = parsed // 判断次文件是否存在 if (!fs.existsSync(fileName)) { return } // 根据guessEditor推断出当前使用的代码编辑器 const [editor, ...args] = guessEditor(specifiedEditor) // 如果editor,则会报错 if (!editor) { onErrorCallback(fileName, null) // 其中打印错误就是之前上面的截图 // colors.red('Could not open ' + path.basename(fileName) + ' in the editor.') return } //... 中间忽略很多的代码 // window系统下调用cmd命令行工具执行code 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 { // 其他平台则直接使用nodejs中childProcess模块的spawn,来执行code命令行 _childProcess = childProcess.spawn(editor, args, { stdio: 'inherit' }) } }
至此整个调试的大致过程已经清晰明了了,不知道你看懂了没,如果没看懂,一定是我没表述清楚,不过整个思路我已经了然于胸了。
5、通过vite 创建测试项目
// 创建项目 pnpm create vite //项目名称 vite3-vue3-demo // 选择框架 vue // 选择一个变体 vue-ts //OK,项目创建完毕,安装依赖 pnpm i // 运行项目 pnpm dev
这里我试过好几个办法都没有解决去给process.env.EDITOR赋值,可能是我打开方式不对,如果有看我文章有解决方案的,可以告知菜鸡一下,非常感谢。
我这里就是直接修改node_modules中launch-editor中的guess.js
// Last resort, use old skool env vars if (process.env.VISUAL) { return [process.env.VISUAL] } else if (process.env.EDITOR) { return [process.env.EDITOR] } return [null]
将最后的return [null]
改为 return ['code']
,其实就是上面的process.env.EDITOR不知道如何赋值,我尝试了两种方式去修改,都没成功,这其中原因我还没找出来???
6、总结
通过调试源码发现,只要仔细一点稍微花点时间,原来也能看懂尤大写的代码,没有想象中的那么难,而且感觉逻辑非常清晰,阅读起来很优雅。所以大家如果有想看源码,或者参加若川源码共读活动的,一定要大胆一些,不要怂,事情真的没有那么难。
有点目的性的阅读源码似乎更高效,这样针对性很强,不会大一统所有的源码都会过一下,时间一下子就过去了,每次带着一个小问题去看源码或许也是若川大佬的精髓所指。
通过阅读源码,就是把看不懂的函数方法关键字等,不断的查漏补缺。或者在这里的用法或者写法不一样,等等各种超乎你想象的用法、场景...,收获真的是非常大,尤其是看完后再写一篇小文总结出来,真的就比读一遍别人写的收获要多好几倍的感觉。
所以如果你还在犹豫自己看不懂,自己行不行等等借口,作为一个前端还不到两年经验的人告诉你,加加油相信自己,你完全可以的。最后一定要行动起来。
写的文章不要总想着有好多好多的浏览量,又或者有几十几百上千的点赞,我的要求不高,首先是自己真的学到了,思路真的整理总结出来了,那么自己在遇到同样问题的时候回看也是非常的方便的。最后送给有需要的人士,哪怕只有一位同仁看了我的文章,也就证明了它的价值所在。
谢谢在看的朋友,欢迎你一起来参加源码共读活动,学习真是一件快乐的事情,尤其是真的学到了,而不是浑浑噩噩的在混日子,当然自己之前浪费过太多的时间,假以时日,我定会.......