This is the 4th day of my participation in the August More Text Challenge

Vue-devtools open component file principle analysis

In response to the call of Ruogawa, together with the source code, this article will be on vue-devtools to open the corresponding component file principle analysis.

An awkward situation

At work, if you take over code developed by a former colleague, you may not be able to find the corresponding source code quickly because there is no documentation or comments.

I have never used vue-DevTools in my development. I always think it is a useless tool. When I read Wakagawa’s article, I felt that I found a new continent.

Vue-devtools provides a feature where clicking a page button causes the editor to open the corresponding file automatically.

Similarly, React has react-dev-inspector that implements this functionality. They also support mainstream editors, so don’t worry about incompatibilities.

The principle of

Through the source code found that vue-DevTools is not complicated, which uses nodeJS child Process, to perform the function of finding the file by getting the file path, then use the command (MAC and Linux use PS X, Windows use get-process) to find the editor to open. You can also specify your own editor.

If you cannot open a component using vscode as shown in the above image, consider:

  • The CMD command line does not support the code command of VScode, so you need to install it yourself
  • Specify the editor in the environment variable

Next, before we parse the source code, take a look at the official vue-DevTools documentation.

Read official documents

From the documentation, we know that Vue CLI3 already has vue-devTools functionality by default, so we don’t need to do any additional installation. We also learned how to use DevTools:

 Import the launch-editor-middleware 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. Guess which editor to start
 openInEditor('code')
 ​
 // 4. You can now click the name of the component in the component window (a prompt will appear if DevTools knows the source of its file).
Copy the code

Of course, we can also customize the request by changing the request host with the following code:

 if(process.env.NODE_ENV ! = ='production')
   // App served from port 4000
   // Webpack dev server on port 9000
   window.VUE_DEVTOOLS_CONFIG = {
     openInEditorHost: 'http://localhost:9000/'}}Copy the code

Next, before beginning the source analysis, we need to build a Vue CLI3 environment, directly according to the official steps to do, here is not to explain.

After setting up all the environment, we start our debugging journey ~ first let’s open the debugging mode:

And find the location of the launch-editor-middleware middleware:

Next, let’s start analyzing the source code.

Source code analysis

First, let’s find where the code appears in the official documentation:

 // myProject\node_modules@vue\cli-service\lib\commands\serve.js
 const launchEditorMiddleware = require('launch-editor-middleware').../* Webpack-dev-server is a small Express service provided by WebPack * it can be used to provide Web services for resource files generated by WebPack * : service static files, automatic refresh and hot replacement */
 const server = new WebpackDevServer(compiler, Object.assign({
   ...
   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`)))...Copy the code

As you can see, expresss makes a request when we click to open the editor.

Originally in order to achieve this effect, but also use webpack, Express, long front road, road obstruction and long ah ~

Let’s go into launchEditorMiddleware and take a look at the implementation:

 // myProject\node_modules\launch-editor-middleware\index.js
 // Let's break this line of code because it is the most core part of the file.
 launch(path.resolve(srcRoot, file), specifiedEditor, onErrorCallback)
Copy the code

After debugging mode is enabled, we click the browser to open the HelloWorld component file and start debugging.

launch-editor-middleware

 // myProject\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(...) function
   // If specifiedEditor is a function, assign it to the error callback. Run the following command to switch parameters
   if (typeof specifiedEditor === 'function') {
     onErrorCallback = specifiedEditor
     specifiedEditor = undefined
   }
   // If srcRoot is a function, assign it to the error callback. Run the following command to switch parameters
   if (typeof srcRoot === 'function') {
     onErrorCallback = srcRoot
     srcRoot = undefined
   }
   
   // srcRoot is the parameter passed in, or the directory of the current Node process
   srcRoot = srcRoot || process.cwd()
   // Express middleware
   return function launchEditorMiddleware (req, res, next) {
     // From the request process we parse out the file path
     const { file } = url.parse(req.url, true).query || {}
     // If there is no file path, an error is reported
     if(! file) { res.statusCode =500
       res.end(`launch-editor-middleware: required query param "file" is missing.`)}else {
       // Otherwise, concatenate srcRoot and file and go to the next function: launch-editor
       launch(path.resolve(srcRoot, file), specifiedEditor, onErrorCallback)
       res.end()
     }
   }
 }
Copy the code

Switch the parameter writing method, more convenient for users to call when passing the parameter. Increased knowledge again ~

After doing this, you can see that this file passes a path to the launchEditor function for manipulation.

Here switch parameters in a lot of source code will be reflected, if chuan big guy gave me an example, suddenly revealed:

 // Define a person function
 function person(name,age,say){
   console.log(name,age);
   if(typeof age === 'function'){
     say = age;
   }
   say && say();
 }
 // Call method one
 person('Bryson'.22.function(){
   console.log('I'm Bryson.')})// Bryson 22
 // My name is Bryson
 ​
 // Call function() instead of passing age
 person('Bryson'.function(){
   console.log('I'm Bryson.')})// Bryson f(){console.log(' I am Bryson ')}
 // My name is Bryson
Copy the code

launch-editor

Enter the launchEditor() function.

 // myProject\node_modules\launch-editor\index.js
 function launchEditor (file, specifiedEditor, onErrorCallback) {
   // Parse the file and get the file path and column number
   const parsed = parseFile(file)
   let { fileName } = parsed
   const { lineNumber, columnNumber } = parsed
   
   // use fs to check whether the corresponding file exists
   if(! fs.existsSync(fileName)) {return
   }
   
   // If specifiedEditor is a function, assign it to the error callback. Run the following command to switch parameters
   if (typeof specifiedEditor === 'function') {
     onErrorCallback = specifiedEditor
     specifiedEditor = undefined
   }
   // Package error callback
   onErrorCallback = wrapErrorCallback(onErrorCallback)
   // Which compiler to use
   const [editor, ...args] = guessEditor(specifiedEditor)
   if(! editor) { onErrorCallback(fileName,null)
     return}... }Copy the code

The function that wraps the error callback:

Used to print error information.

guessEditor

Guess = guess = guess = guess = guess = guess = guess = guess

 // myProject/node_modules/launch-editor/guess.js
 const path = require('path')
 const shellQuote = require('shell-quote')
 const childProcess = require('child_process')
 ​
 // From the full process name to the starting process binary
 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) {
   // 1. If we specify an editor, it will be parsed and returned directly, without the following guessing editor operations.
   if (specifiedEditor) {
     return shellQuote.parse(specifiedEditor) // Cut space function
   }
   // We can find out which editor is currently running by:
   // `ps x` on macOS and Linux
   // `Get-Process` on Windows
   // 2. Run the command to check which editor is running
   try {
     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') {
       // --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() ... }}catch (error) {
     // Ignore...
   }
 ​
   // Last resort, use old skool env vars
   // 3. If no running editor is found for the current process, specify the editor using the environment variable
   if (process.env.VISUAL) {
     return [process.env.VISUAL]
   } else if (process.env.EDITOR) {
     return [process.env.EDITOR]
   }
   // 4. Return null if none is found
   return [null]}Copy the code

After deriving the editor used, then:

 // myProject/node_modules/launch-editor/guess.js
 const childProcess = require('child_process')
 let _childProcess = null // Set the child process
 ​
 function launchEditor (file, specifiedEditor, onErrorCallback) {...if (process.platform === 'win32') {
     // On Windows, launch the editor in a shell because spawn can only
     // launch .exe files.
     _childProcess = childProcess.spawn( // Spawn new processes
       'cmd.exe'['/C', editor].concat(args),
       { stdio: 'inherit'})}else {
     _childProcess = childProcess.spawn(editor, args, { stdio: 'inherit' })
   }
   _childProcess.on('exit'.function (errorCode) { / / the exit event
     _childProcess = null
 ​
     if (errorCode) {
       onErrorCallback(fileName, '(code ' + errorCode + ') ')
     }
   })
 ​
   _childProcess.on('error'.function (error) { / / error event
     onErrorCallback(fileName, error.message)
   })
 }
 ​
 module.exports = launchEditor
Copy the code

From this source, you can get:

Get my editor as vscode, run the command and execute exit to clear the empty child process and exit, so far the previous code is complete.

conclusion

Follow if sichuan big guy source read, learned a lot of things:

  • Vscode debugging techniques
  • Vue-devtools Opens component files
  • Node child process knowledge

This part of the source is less, but it gives a sense of broadening the field of vision.

Wakagawa big man nuggets: juejin.cn/user/141582…


If this article is helpful to you, please help to like yo!