(一)launch-editor(vue-devtools直接打开页面相应组件代码)

1,787 阅读4分钟
参考:
https://juejin.cn/post/6959348263547830280
https://www.yuque.com/ggr8wn/dxsvhs/yesv21 
https://www.yuque.com/ggr8wn/cougi5/hcgcnh 
https://www.yuque.com/ggr8wn/cougi5/bgsepq

原文:
<https://www.yuque.com/ruochuan12/dxsvhs/zd7g69>

准备

1.谷歌浏览器的vue3_devtools插件安装

链接: pan.baidu.com/s/1cV4IvrZJ… 提取码: udgm

2.安装项目

npm install -g @vue/cli
vue create vue3-project
npm install

3.检查code

code -v

流程

  • vue3项目的package.json里以调试模式运行启动程序
  • 尝试在dev-tools里打开当前组件对应的文件,此时network发现请求:http://10.20.191.44:8080/__open-in-editor?file=src/components/HelloWorld.vue
  • 此时终端报错

系统找不到指定的路径。

Could not open HelloWorld.vue in the editor.
The editor process exited with an error: (code 1).

To specify an editor, specify the EDITOR env variable or add "editor" field to your Vue project config.

  • 在川哥指引下将 .env.delelopment修改为VISUAL=code,不行,开始调试与学习
  • launch-editor-middleware到达指定文件 serve.js
    const launchEditorMiddleware = require('launch-editor-middleware')
    // create server
    const server = new WebpackDevServer(compiler,...
    {
      https: useHttps,
      proxy: proxySettings,
      // eslint-disable-next-line no-shadow
      before (app, server) {
        // launch editor support.
        // this works with vue-devtools & @vue/cli-overlay
        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`
        )))
        // allow other plugins to register middlewares, e.g. PWA
        api.service.devServerConfigFns.forEach(fn => fn(app, server))
        // apply in project middlewares
        projectDevServerOptions.before && projectDevServerOptions.before(app, server)
      },
      // avoid opening browser
      open: false
    }))
  • ctrl点击 launchEditorMiddleware进入其源码
const url = require('url')
const path = require('path')
const launch = require('launch-editor')

module.exports = (specifiedEditor, srcRoot, onErrorCallback) => {
    //() => console.log(... 错误输出函数即 specifiedEditor
    //这里将 错误输出函数 赋值给specifiedEditor,顺便置空specifiedEditor
  if (typeof specifiedEditor === 'function') {
    onErrorCallback = specifiedEditor
    specifiedEditor = undefined
  }

  if (typeof srcRoot === 'function') {
    onErrorCallback = srcRoot
    srcRoot = undefined
  }
// srcRoot 是传递过来的参数,或者当前node进程的目录
  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 {
        // 自己加的调试的语句,输出路径无误
        //C:\Users\82454\Desktop\vue3\vue3-project\src\components\HelloWorld.vue
      console.log(path.resolve(srcRoot, file))
        
        //如果有文件名,应该就是通过这个去打开
        //在这条代码打断点,再次用工具打开组件文件,请求会处于pending,调试点会卡在这
      launch(path.resolve(srcRoot, file), specifiedEditor, onErrorCallback)
      res.end()
    }
  }
}
//川川说,这种切换参数的写法,在很多源码中都很常见。为的是方便用户调用时传参。虽然是多个参数,但可以传一个或者两个
  • ctrl点击 launch进入其源码
const positionRE = /:(\d+)(:(\d+))?$/
function parseFile (file) {
  const fileName = file.replace(positionRE, '')
  const match = file.match(positionRE)
  const lineNumber = match && match[1]
  const columnNumber = match && match[3]
  return {
    fileName,
    lineNumber,
    columnNumber
  }
}
let _childProcess = null

function launchEditor (file, specifiedEditor, onErrorCallback) {
    //解析的行号列号干哈用的我不知道,这里也没用到
  const parsed = parseFile(file)
  let { fileName } = parsed
  const { lineNumber, columnNumber } = parsed

  if (!fs.existsSync(fileName)) {
    console.log('文件不存在')
    return
  }
	//打印
  console.log('文件存在',file,parsed)

  //前面换,这里又换,有人写笔记说这里有意思,我觉得不好玩
  if (typeof specifiedEditor === 'function') {
    onErrorCallback = specifiedEditor
    specifiedEditor = undefined
  }
  //wrapErrorCallback包裹错误输出函数
 // 1 错误输出函数
  onErrorCallback = wrapErrorCallback(onErrorCallback)
     // 猜测当前进程运行的是哪个编辑器,这里specifiedEditor 是undefined
  const [editor, ...args] = guessEditor(specifiedEditor)
  if (!editor) {
    onErrorCallback(fileName, null)
    return
  }
  // 省略剩余部分,后文再讲述... 3
}
打印:
文件存在 C:\Users\82454\Desktop\vue3\vue3-project\src\components\HelloWorld.vue {
  fileName: 'C:\Users\82454\Desktop\vue3\vue3-project\src\components\HelloWorld.vue',
  lineNumber: null,
  columnNumber: null
}
// 1 错误输出函数 
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)//这参数并没用到
  }
}
//往前推,可以知道onErrorCallback,即cb是
() => console.log(
          `To specify an editor, specify the EDITOR env variable or ` +
          `add "editor" field to your Vue project config.\n`
)
//这里面输出的和我错误时的报错信息对上了
  • ctrl点击 guessEditor进入其源码,
//猜测编辑器
1.如果传了specifiedEditor,则解析返回
2.判断当前平台,当前进程正在运行的编辑器
3.找不到就去用 process.env.VISUAL或者process.env.EDITOR
4.找不到就返回null,导致取不到editor,执行错误回调输出
const path = require('path')
const shellQuote = require('shell-quote')
const childProcess = require('child_process')

// Map from full process name to binary that starts the process
// We can't just re-use full process name, because it will spawn a new instance
// of the app every time
const COMMON_EDITORS_OSX = require('./editor-info/osx')
const COMMON_EDITORS_LINUX = require('./editor-info/linux')
const COMMON_EDITORS_WIN = require('./editor-info/windows')

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')
      console.log('runningProcesses',runningProcesses)
      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)
	   console.log('fullProcessPath',fullProcessPath)
        console.log('shortProcessName',shortProcessName) //Code.exe
        if (COMMON_EDITORS_WIN.indexOf(shortProcessName) !== -1) {
            //COMMON_EDITORS_WIN里含有,[D:\����\Microsoft VS Code\Code.exe]
          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]
}
打印 runningProcesses,我感觉这个乱码可能就是我打不开文件的原因了,原因是因为我的vs-code安装到了中文目录下,中文叫软件。
[ 'D:\����\Microsoft VS Code\Code.exe                                                                                    ',
  'D:\����\Microsoft VS Code\Code.exe                                                                                    ',
  ... 287 more items
]

打印 fullProcessPath,shortProcessName
fullProcessPath D:\����\Microsoft VS Code\Code.exe
shortProcessName Code.exe
//3 
const childProcess = require('child_process')

if (process.platform === 'win32') {
    // editor=》["D:\软件\Microsoft VS Code\Code.exe"]
    //args=》C:\Users\82454\Desktop\vue3\vue3-project\src\components\HelloWorld.vue
    _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 = null
	//由于乱码,文件不存在,打不开对应文件,出错,执行回调
    if (errorCode) {
        onErrorCallback(fileName, '(code ' + errorCode + ')')
    }
})
  • 尝试修bug
在获取进程时就已经乱码了,大概要用什么不会乱码的获取进程名的
`powershell -Command "Get-Process | Select-Object Path`
可能要稍微改改这个命令,但我没兴趣去查
于是 很暴力的 在guess.js中 将正确的路径返回
 if (COMMON_EDITORS_WIN.indexOf(shortProcessName) !== -1) {
          if(shortProcessName==='Code.exe') 
              return ['D:\软件\Microsoft VS Code\Code.exe']
          return [fullProcessPath]
          // D:\����\Microsoft VS Code\Code.exe
}
-------------
但还是报错:
'D:软件Microsoft' 不是内部或外部命令,也不是可运行的程序或批处理文件。

那没办法了,以为有空格也不行
------------
我搞个快捷方式放桌面,没想到还是不行,看来不能用快捷方式的位置
if(shortProcessName==='Code.exe') return ['C:\Users\82454\Desktop\Code.exe']
'C:Users82454DesktopCode.exe' 不是内部或外部命令,
---------
在window.js中找到有 'sublime_text.exe', 我电脑有,复制启动路径
这样试了一下也不行
---------
翻了一下node文档,空格应该没问题,只要加引号就行,于是我将单引号改成双引号,
我发现filename是
C:\Users\82454\Desktop\vue3\vue3-project\src\components\HelloWorld.vue
于是
['C:\Users\82454\Desktop\Code.exe']改成["D:\软件\Microsoft VS Code\Code.exe"]
可算能跑了

总结

code C:\Users\82454\Desktop\vue3\vue3-project\src\components\HelloWorld.vue
----等于
const childProcess = require('child_process')
childProcess.spawn(
        'cmd.exe',
        ['/C', "D:\软件\Microsoft VS Code\Code.exe",
        "C:\Users\82454\Desktop\vue3\vue3-project\src\components\HelloWorld.vue"],
        { stdio: 'inherit' }
)
编辑器寻找流程:(D:\软件\Microsoft VS Code\Code.exe)
有无通过参数specifiedEditor指定,若无
判断当前的平台,用相应平台的终端命令查询当前进程和进程相应的启动路径,是否有进程名和编辑器进程名字相同
找不到就去查 process.env.VISUAL或者process.env.EDITOR中是否有默认指定
找不到就返回null,导致取不到editor,执行错误回调输出

小技巧

1.搜索node_modules下的文件

就是「排除的文件」右侧旁边有个设置图标「使用“排查设置”与“忽略文件”」,点击下。

2.高效终端工具-使用 ohmyzsh 打造 windows、ubuntu、mac 系统高效终端命令行工具

mp.weixin.qq.com/s/MHngeDABR…

注意

  • 在插件源码里改代码和添加打印代码,需要重新启动,一般的热重载不监听node_module的文件
  • window路径要 \\,虽然不知道为啥fullProcessPath打印的是单斜杠的