携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
- 本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
- 这是源码共读的第1期,vue-devtools 组件可以打开编辑器
前言
你知道vue-devtools除了看state还可以快速定位打开源文件吗?你知道vscode如何搜索node_modules中的文件吗?不知道的就一起实践一下,知道的就一起看原理温故知新吧!
环境准备
了解无论是组件库还是插件最好的途径就是看官方文档,先贴个vue-tools地址,按官方文档步骤安装vue-tools,个人是采用安装包的形式安装:
-
安装完成后运行vue项目,打开控制台看到如下图那样就安装成功啦:
vue-tools指南解读
- 敲重点,能快速打开源文件的就是它了,日常工作中可能会被很多人忽略掉,但是定位bug文件真的很nice
- 按官方文档的说法,vue-cli3以上版本是直接支持在编辑器打开组件文件的,webpack呢就可能需要引入launch-editor-middleware包
代码准备
既然是源码学习,肯定是少不了调试代码啦,克隆一下下面的库,或者安装了vue-cli的直接vue create yourapp
git clone `https://github.com/lxchuan12/open-in-editor.git`
// or安装vue-cli
yarn global add @vue/cli
vue create vue3-project
- 鉴于咱们要搜索的中间件
launch-editor-middleware在node_modules,因此咱们要先设置一下vscode
ctrl+,打开设置,也可以点齿轮- 搜索
search.exclude把node_module选项去掉或设为false,噔噔,就是它了: - 然后搜索定位
launch-editor-middleware,准备开启调试模式!
调试分析
- 断点调试,launch-editor-middleware通过调用launch-editor达到打开组件文件命令
- 接着细看一下launch-editor的实现~
- launch-editor函数总览
function launchEditor (file, specifiedEditor, onErrorCallback) {
// 解析文件信息,包括名称、行列号
const parsed = parseFile(file)
let { fileName } = parsed
const { lineNumber, columnNumber } = parsed
// 文件不存在直接返回
if (!fs.existsSync(fileName)) {
return
}
// specifiedEditor若为函数直接赋给回调函数
if (typeof specifiedEditor === 'function') {
onErrorCallback = specifiedEditor
specifiedEditor = undefined
}
// 处理错误回调
onErrorCallback = wrapErrorCallback(onErrorCallback)
// 判断编辑器类型
const [editor, ...args] = guessEditor(specifiedEditor)
// 不匹配就报错
if (!editor) {
onErrorCallback(fileName, null)
return
}
...根据行列号等信息执行命令
}
- 源码大体思路:
- 解析文件信息,包括名称、行列号,判断文件是否存在
- 判断正在运行的编辑器,并返回相应信息
- 查找文件位置
- 利用子进程模块执行类似打开文件命令
wrapErrorCallback
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)
}
}
源码分析:容错处理,打印报错信息
guessEditor 判断编辑器
module.exports = function guessEditor (specifiedEditor) {
if (specifiedEditor) {
return shellQuote.parse(specifiedEditor)
}
// We can find out which editor is currently running by:
// `ps x` on macOS and Linux
// `Get-Process` on Windows
try {
if (process.platform === 'darwin') {
const output = childProcess.execSync('ps x').toString()
const processNames = Object.keys(COMMON_EDITORS_OSX)
for (let i = 0; i < processNames.length; i++) {
const processName = processNames[i]
if (output.indexOf(processName) !== -1) {
return [COMMON_EDITORS_OSX[processName]]
}
}
} else if (process.platform === 'win32') {
const output = childProcess
.execSync('powershell -Command "Get-Process | Select-Object Path"', {
stdio: ['pipe', 'pipe', 'ignore']
})
.toString()
const runningProcesses = output.split('\r\n')
for (let i = 0; i < runningProcesses.length; i++) {
// `Get-Process` sometimes returns empty lines
if (!runningProcesses[i]) {
continue
}
const fullProcessPath = runningProcesses[i].trim()
const shortProcessName = path.basename(fullProcessPath)
if (COMMON_EDITORS_WIN.indexOf(shortProcessName) !== -1) {
return [fullProcessPath]
}
}
} else if (process.platform === 'linux') {
// --no-heading No header line
// x List all processes owned by you
// -o comm Need only names column
const output = childProcess
.execSync('ps x --no-heading -o comm --sort=comm')
.toString()
const processNames = Object.keys(COMMON_EDITORS_LINUX)
for (let i = 0; i < processNames.length; i++) {
const processName = processNames[i]
if (output.indexOf(processName) !== -1) {
return [COMMON_EDITORS_LINUX[processName]]
}
}
}
} catch (error) {
// Ignore...
}
// 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]
}
源码分析: 大体意思是若有传编辑器则解析,如果没有则找出正在运行的编辑器,并根据macOS系统、windows系统、linux系统的对应子进程通信返回编辑器信息,如果都没找到就利用process.env,否则返回null
关键代码
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模块创建子进程,达到执行打开代码所在文件的命令
总结
本次学习实践了利用vue-devtools打开文件,设置了vscode搜索node_module包,跟着若川大佬调试分析了一下launch-editor-middleware中间件,对vue-devtools打开组件文件的实现有了更进一步的了解,源码学习其实静下心来慢慢分析,好像也没有想象中的那么难,还有就是几期的源码学习下来发现学好node.js极其重要,毕竟站在巨人的肩膀上才看得更远!