源码共读02:vue-devtools

170 阅读3分钟

vue-devtools

本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。

这是源码共读的第2期,链接: juejin.cn/post/708499…

本期任务目标:

  • 一句话简述其原理
code path/to/file

一句话简述:通过NodeJSde 的child_process,执行了类似code path/to/file的命令,于是编辑器打开对应文件,对应编辑器是通过ps x(Windows则使用Get-Process)命令查找的,当然也可以自己指定编辑器。

launch-editor-middleware
// 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切换下,把它赋值给onErrorCallback 错误回调函数
  if (typeof specifiedEditor === 'function') {
    onErrorCallback = specifiedEditor
    specifiedEditor = undefined
  }
  // 如果第二个参数是函数,同样把它赋值给错误回调函数
  // 这里传递过来是undefined
  if (typeof srcRoot === 'function') {
    onErrorCallback = srcRoot
    srcRoot = undefined
  }
  // srcRoot是传递过来的参数,或者是当前node进程目录  
  srcRoot = srcRoot || process.cwd()
  // 最后返回一个中间件,express中间件
  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-editor打卡文件
      launch(path.resolve(srcRoot, file), specifiedEditor, onErrorCallback)
      res.end()
    }
  }
}

上段代码中,切换参数的方式在很多源码中很常见。为的是方便用户调用时传参,虽然是多个参数,但是可以传一个或多个。

launch-editor
// vue3-project/node_modules/launch-editor/index.js
function launchEditor (file, specifiedEditor, onErrorCallback) {
  // 解析文件路径和行号列号信息
  const parsed = parseFile(file)
  let { fileName } = parsed
  const { lineNumber, columnNumber } = parsed
  // 判断文件是否存在,不存在直接返回
  if (!fs.existsSync(fileName)) {
    return
  }
  // 第二个参数传递过来是函数,把它赋值给错误函数
  if (typeof specifiedEditor === 'function') {
    onErrorCallback = specifiedEditor
    specifiedEditor = undefined
  }
  // 包装一层函数
  onErrorCallback = wrapErrorCallback(onErrorCallback)
  // 猜测当前进程运行的是那个编辑器
  const [editor, ...args] = guessEditor(specifiedEditor)
  if (!editor) {
    onErrorCallback(fileName, null)
    return
  }

  省略剩余部分...
}
wrapErrorCallback 包裹错误函数回调
onErrorCallback = wrapErrorCallback(onErrorCallback)

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

// 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)
  }
}
guessEditor 猜测当前正在使用的编辑器

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

1、如果指明了编辑器,则直接返回

2、找出当前进程中正在运行的编辑器。Linux和MacOS 用ps 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
launch-editor 剩余部分
function launchEditor (file, specifiedEditor, onErrorCallback) {
  // 省略上部分...

  // 主要部分代码
  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' })
  }
  省略下部分
}

此文章为11月Day2源码共读,以梦为马,11月进步💪💪