4
Author: Vivo Internet Front-end Team - Youchen

1. Background

Nowadays, large-scale Vue projects are basically developed collaboratively by multiple people, and with the iteration of versions, the number of components in the Vue project will also increase. If you are responsible for the development of unfamiliar page functions at this time, even you I just joined this project, so how can I quickly find the file location of related components in the entire project code? You must have taken the following methods:

  • [Search class name] , search the style class name in the DOM element of the page in the project file
  • [Find a route] , find the page component matching the Vue route according to the page link
  • 【Find someone】 , find the person who was responsible for developing the page and ask the corresponding code path

The above methods can indeed help us find the specific code file path, but they all require manual search, which is not very efficient. Is there any other more efficient way?

The answer is yes. Vue officially provides a vue-devtools plug-in. Using this plug-in, you can automatically open the source code file of the corresponding page component in VSCode. The operation path is as follows:

图片

Using the vue-devtools plug-in can greatly improve the efficiency of finding the corresponding page component code, but only the corresponding component code can be located. If we want to directly find the specific code location related to an element on the page, we also need to find the specific code position related to an element on the page. A secondary search is performed in the component source code, and each time you have to select the component first, and then click the Open button to open the code file, which is not particularly fast.

In response to this problem, we have developed a lightweight page element code mapping plug-in. Using this plug-in, you can open the corresponding code source file with one click by clicking on the page element, and accurately locate the corresponding code line without manual search. To improve development efficiency and experience, the actual use effects are as follows:

图片

Second, the realization principle

The whole plug-in is mainly divided into three functional modules: client, server, add-code-location. The client side sends a specific request to the server side, and the server side executes the positioning code line command after receiving the request, while the add-code-location module uses conversion of source code.

图片

2.1 client

The client side here actually refers to the browser. When we click on a page element, the browser will send a specific request to the server side. The request information includes the specific code file path and corresponding code line number information.

图片

 function openEditor(filePath) {
  axios
    .get(`${protocol}//${host}:${port}/code`, {
      params: {
        filePath: `${filePath}`
      }
    })
    .catch(error => {
      console.log(error)
    })
}

The click event of the monitoring page element is monitored globally by means of event proxy, the click event is bound to the document, and the combination event of the keyboard and mouse click is monitored to initiate a request for positioning the code line to avoid conflict with the native click event of the page.

 function openCode(e) {
  if (isShiftKey || isMetaKey || e.metaKey || e.shiftKey) {
    e.preventDefault()
    const filePath = getFilePath(e.target)
    openEditor(filePath)
  }
  ...
}

2.2 server

The server side refers to a local server that can monitor specific requests sent by the client side. When receiving a request to execute a positioning command, execute the VSCode open code file command and locate the corresponding code line.

2.2.1 webpack devServer

If it is a project built with webpack, webpack's devServer development server has provided a before attribute, which can be used to monitor requests sent to the development server.

 before: function (app) {
  app.get('/code', function (req, res) {
    if (req.query.filePath) {
      // 执行vscode定位代码行命令
      openCodeFile(req.query.filePath)
      ...
    }
    ...
  })
}

2.2.2 vite configureServer

If it is a project built with Vite, you can use the Vite plug-in to monitor specific requests on the server side. The Vite plug-in extends the rollup plug-in interface, and adds some unique hook functions on the original basis, such as the configureServer hook, through the hook function Can be used to configure the development server to listen for specific requests.

 const codeServer = () => ({
  name: 'open-code-vite-server',
  configureServer(server) {
    server.middlewares.use((req, res, next) => {
      ...
      if (pathname == '/code') {
        ...
        if (filePath) {
          openCodeFile(filePath) // 执行vscode定位代码行命令
          ...
        }
        res.end()
      }
      ...
    })
  }
})

2.2.3 Execute the VSCode positioning command

When the server side listens to the specific request sent by the client side, the next step is to execute the VSCode positioning code line command. In fact, the VSCode editor can be started through the code command, and some command line parameters can be used accordingly, such as:

The "code --reuse-window" or "code -r" command can open the file or folder of the last active window; the "code --goto" or "code -g" command can be followed by splicing specific file paths and row and column numbers. When using the "code -g file:line:column" command, you can open a file and locate a specific line and column position.

Using this feature of the VSCode editor, we can realize the function of automatically locating the code line. The corresponding code path information can be obtained from the request information sent by the client, and then use the child_process.exec method of node to execute the VSCode locating code line command.

 const child_process = require('child_process')
function openCodeFile(path) {
  let pathBefore = __dirname.substring(0, __dirname.search('node_modules'))
  let filePath = pathBefore + path
  child_process.exec(`code -r -g ${filePath}`)
}

In addition, in order to use the VSCode code command normally, we need to make sure to add the VSCode Code command to the environment variable. Mac system users can use the command+shift+p shortcut in the VSCode interface, then search for Code and select install 'code' command in path; Windows users can find the bin folder directory of the VSCode installation location and add this directory to the system environment among the variables.

2.3 add-code-location

Through the previous introduction, you should understand the execution mechanism of the client side and the server side, and you need to obtain the code path of the page element when executing the positioning command, and the specific code path is bound to the DOM element in the form of attributes. At this time, we need to use the add-code-location module to convert our source code at compile time, and add the corresponding code path attribute to the DOM element.

The entire source code conversion process is as follows:

图片

2.3.1 Get file path

The first step in the source code conversion process is to obtain the specific path of the code file. For projects packaged by webpack, the webpack loader is suitable for processing source code strings. The context this object of the loader contains a path attribute of the resourcePath resource file. With this attribute, we can easily get the specific path of each code file.

 module.exports = function (source) {
  const { resourcePath } = this
  return sourceCodeChange(source, resourcePath)
}

For projects built by Vite, the conversion of source code is also done through plug-ins. The Vite plug-in has a universal hook transform, which can be used to convert the content of loaded modules. It receives two parameters, the code parameter represents the source code string, and the id The argument is the full path to the file.

 module.exports = function() {
  return {
    name: 'add-code-location',
    transform(code, id) {
      ...
      return sourceCodeChange(code, id)
    }
  }
}

2.3.2 Calculate the code line number

Then in the process of traversing the source code file, it is necessary to process the code in the template template of the corresponding Vue file, divide the template template part string with "\n" as an array, and accurately obtain the code line number of each line of HTML tags through the index of the array .

 function codeLineTrack(str, resourcePath) {
  let lineList =  str.split('\n')
  let newList = []
  lineList.forEach((item, index) => {
    newList.push(addLineAttr(item, index + 1, resourcePath)) // 添加位置属性,index+1为具体的代码行号
  })
  return newList.join('\n')
}

2.3.3 Add location attribute

After obtaining the code file path and code line number, the next step is to add the final location attribute to each line of label elements split in the Vue template template. Here, the method of regular replacement is used to add the position attribute. For each line of label elements, firstly match the start tag part of all elements, such as <div, <span, <img, etc., and then replace them with code. The start tag of the -location attribute, and the corresponding attribute value is the code path obtained earlier and the line number of the corresponding tag. 图片

 function addLineAttr(lineStr, line, resourcePath) {
  let reg = /<[\w-]+/g
  let leftTagList = lineStr.match(reg)
  if (leftTagList) {
    leftTagList = Array.from(new Set(leftTagList))
    leftTagList.forEach(item => {
      if (item && item.indexOf('template') == -1) {
        let regx = new RegExp(`${item}`, 'g')
        let location = `${item} code-location="${resourcePath}:${line}"`
        lineStr = lineStr.replace(regx, location)
      }
    })
  }
  return lineStr
}

图片

2.4 Other processing

2.4.1 Source relative path

When adding the corresponding source location attribute to the DOM element, a relative path is actually used, which can make the attribute value on the DOM element more concise and clear. The node\_modules folder is usually in the root directory of the project, and the plugin is installed in the node\_modules path in the form of an npm package. The absolute path of the current module can be obtained by using the __dirname variable of node, so during the source code conversion process You can get the root path of the project, so you can get the relative path of the Vue code file.

 let pathBefore = __dirname.substring(0, __dirname.search('node_modules'))
let filePath = filePath.substring(pathBefore.length) // vue代码相对路径

When the code positioning command is executed on the server side, the corresponding code relative path is spliced into a complete absolute path.

2.4.2 Externally imported components

Although add-code-location can add code path information to local Vue files, there is currently no way to convert components imported or parsed from outside, such as element ui components, the actual code line information will only be added In the outermost layer of the element ui component. At this time, when the client side obtains the code path of the clicked element, it will perform an upward search process to obtain the code path of its parent node. If it still does not exist, it will continue to search for the parent node of the parent node until the code path is successfully obtained.

 function getFilePath(element) {
  if (!element || !element.getAttribute) return null
  if (element.getAttribute('code-location')) {
    return element.getAttribute('code-location')
  }
  return getFilePath(element.parentNode)
}

In this way, when you click on the page element built by the background element ui, you can also successfully locate and open the corresponding code file.

3. Access plan

Through the previous introduction, you must have a clear understanding of the principle of the page element code mapping plug-in. Next, I will introduce the access method in the project. The access method is actually very simple, and you can choose to access it only in the local development environment, without worrying about affecting our production environment, you can use it with confidence.

3.1 webpcak build project

For projects built by webpack, first configure the devServer and webpack loader in the build configuration item vue.config.js file, and then initialize the plugin in the main.js entry file.

 // vue.config.js
const openCodeServe = require('@vivo/vue-dev-code-link/server')
devServer: {
  ...
  before: openCodeServe.before
},
 
if (!isProd) { // 本地开发环境
  config.module
    .rule('vue')
    .test(/\.vue/)
    .use('@vivo/vue-dev-code-link/add-location-loader')
    .loader('@vivo/vue-dev-code-link/add-location-loader')
    .end()
}
// main.js
import openCodeClient from '@vivo/vue-dev-code-link/client'
if (process.env.NODE_ENV == 'development') {
  openCodeClient.init()
}

3.2 Vite build project

The Vite build project access to this plugin is basically the same as the webpack build project, the only difference is that two Vite plugins are introduced in the packaging configuration file.

 // vite.config.js
import openCodeServer from '@vivo/vue-dev-code-link/vite/server'
import addCodeLocation from '@vivo/vue-dev-code-link/vite/add-location'
export default defineConfig({
  plugins: [
    openCodeServer(),
    addCodeLocation()
  ]
}

4. Summary

The above is an introduction to the core principle and access scheme of the page element code mapping plug-in. The implementation method makes full use of the project code packaging and construction process. In fact, no matter which packaging tool is used, it is essentially the conversion processing of source code files. After we understand the working mechanism of the packaging tool, we can do something that we think makes sense. Take the page element code mapping plug-in as an example. Using it can greatly improve development efficiency, and you no longer need to spend time searching for code files, especially for projects with a large number of pages and components, just click on the page element and you are done. One-click to open the corresponding code file, accurately locate the specific code line, no need to search, no where to click, so easy!


vivo互联网技术
3.3k 声望10.2k 粉丝