插件开发指南

注意

该 API 仅在 Vue Devtools 6+ 中可用

架构

Vue Devtools 插件从用户应用程序中的 Vue 包代码注册。它通过 公共 API 与 Vue Devtools 后端交互。后端是当用户打开 Vue Devtools 时注入网页的脚本 - 它负责注册 Vue 应用程序并与 Vue Devtools 前端通信。前端是浏览器开发者工具窗格中显示的 Vue 应用程序。钩子是添加到页面中的全局变量,以便 Vue 应用程序和你的插件可以向后端发送消息。

有 3 个主要的 API 类别

  • 组件检查器:你的插件可以向组件树和状态添加更多信息。
  • 自定义检查器:你可以添加新的检查器来显示任何类型的状态。例如:路由、存储当前状态...
  • 时间线:你可以添加自定义图层并发送事件。

使用 API,你可以向用户显示信息并改善应用程序调试体验。像 vue-routervuex 这样的官方库已经使用了这个 API!

架构图

示例

设置

在你的包中,安装 @vue/devtools-api 作为依赖项

yarn add @vue/devtools-api

这个包将允许你从你的代码中注册一个新的 Vue Devtools 插件,并附带完整的 TypeScript 类型定义。

你的 package.json 文件应该类似于这个

{
  "name": "my-awesome-plugin",
  "version": "0.0.0",
  "main": "dist/index.js",
  "dependencies": {
    "@vue/devtools-api": "^6.0.0-beta.14"
  },
  "peerDependencies": {
    "vue": "^3.1.0"
  },
  "devDependencies": {
    "vue": "^3.1.0"
  }
}

最好也指定 vue 作为对等依赖项,以告知用户你的包与哪个版本的 Vue 兼容。

TypeScript

如果你使用 TS,你的 package.json 文件应该类似于这个

{
  "name": "my-awesome-plugin",
  "version": "0.0.0",
  "main": "dist/index.js",
  "scripts": {
    "dev": "tsc --watch -d",
    "build": "tsc -d"
  },
  "dependencies": {
    "@vue/devtools-api": "^6.0.0-beta.14"
  },
  "peerDependencies": {
    "vue": "^3.1.0"
  },
  "devDependencies": {
    "@types/node": "^14.14.22",
    "typescript": "^4.1.3",
    "vue": "^3.1.0"
  }
}

以下是一个 tsconfig.json 文件示例,可以放在 package.json 文件旁边

{
  "include": [
    "src/global.d.ts",
    "src/**/*.ts",
    "__tests__/**/*.ts"
  ],
  "compilerOptions": {
    "outDir": "dist",
    "sourceMap": false,

    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "allowJs": false,
    "skipLibCheck": true,

    "noUnusedLocals": true,
    "strictNullChecks": true,
    "noImplicitAny": true,
    "noImplicitThis": true,
    "noImplicitReturns": false,
    "strict": true,
    "isolatedModules": true,

    "experimentalDecorators": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "removeComments": false,
    "jsx": "preserve",
    "lib": [
      "esnext",
      "dom"
    ],
    "types": [
      "node"
    ]
  }
}

Rollup

Rollup 是一个通用捆绑器。你可以用它来编译你的包,以便更容易地使用。如果你有 .vue 文件要编译,它也非常方便!

yarn add -D rollup rollup-plugin-vue @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-replace rollup-plugin-terser pascalcase rimraf

关于包的说明

  • rollup-plugin-vue 编译 .vue 文件。
  • @rollup/plugin-commonjs 将 CommonJS 模块转换为 ES2015 模块。
  • @rollup/plugin-node-resolve 定位并捆绑 node_modules 中的第三方依赖项。
  • @rollup/plugin-replace 允许我们在构建时用我们想要的值替换一些源代码文本,比如 process.env.NODE_ENV
  • rollup-plugin-terser 缩小生产环境的输出。
  • pascalcase 用于将你的包名称(来自 package.json)转换为 Pascal 大小写,例如 my-plugin 转换为 MyPlugin
  • rimraf 用于在构建之前清除 dist 文件夹。

Rollup 配置

package.json 文件旁边创建一个 rollup.config.js 文件

查看示例

Rollup 包

将主字段、exportsscripts 添加到你的 package.json

{
  "name": "my-plugin",
  "version": "0.0.0",
  "description": "A demo Vue 3 plugin with devtools integration",
  "author": {
    "name": "Guillaume Chau",
    "email": "[email protected]"
  },
  "main": "dist/my-plugin.cjs.js",
  "module": "dist/my-plugin.esm-bundler.js",
  "unpkg": "dist/my-plugin.global.js",
  "jsdelivr": "dist/my-plugin.global.js",
  "exports": {
    ".": {
      "require": "./dist/my-plugin.cjs.js",
      "browser": "./dist/my-plugin.esm-browser.js",
      "import": "./dist/my-plugin.esm-bundler.js",
      "module": "./dist/my-plugin.esm-bundler.js"
    },
    "./package.json": "./package.json"
  },
  "sideEffects": false,
  "scripts": {
    "build": "rimraf dist && rollup -c rollup.config.js"
  }
  ...
}

不要忘记将 my-plugin 替换为你的包名称。

你现在可以使用 build 脚本编译包

yarn build

带有 TypeScript 的 Rollup

安装 Rollup TS 插件

yarn add -D rollup-plugin-typescript2

修改 Rollup 配置以编译 TS 文件

查看示例

并将 types 字段添加到你的 package.json 文件中













 
















{
  "name": "my-plugin",
  "version": "0.0.0",
  "description": "A demo Vue 3 plugin with devtools integration",
  "author": {
    "name": "Guillaume Chau",
    "email": "[email protected]"
  },
  "main": "dist/my-plugin.cjs.js",
  "module": "dist/my-plugin.esm-bundler.js",
  "unpkg": "dist/my-plugin.global.js",
  "jsdelivr": "dist/my-plugin.global.js",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "require": "./dist/my-plugin.cjs.js",
      "browser": "./dist/my-plugin.esm-browser.js",
      "import": "./dist/my-plugin.esm-bundler.js",
      "module": "./dist/my-plugin.esm-bundler.js"
    },
    "./package.json": "./package.json"
  },
  "sideEffects": false,
  "scripts": {
    "build": "rimraf dist && rollup -c rollup.config.js"
  }
  ...
}

查看 TypeScript 获取 tsconfig.json 示例。

注册你的插件

在你的源文件夹中创建一个新的 devtools.js 文件。

插件设置

我们将从 @vue/devtools-api 包中导入 setupDevtoolsPlugin

import { setupDevtoolsPlugin } from '@vue/devtools-api'

然后我们导出一个函数来设置我们的 Vue Devtools 插件

export function setupDevtools () {
  setupDevtoolsPlugin({ /* Options... */}, api => {
    // Logic...
  })
}

在第一个参数中添加插件选项


 
 
 
 




setupDevtoolsPlugin({
  id: 'my-awesome-devtools-plugin',
  label: 'My Awesome Plugin',
  packageName: 'my-awesome-plugin',
  homepage: 'https://vuejs.ac.cn'
}, api => {
  // Logic...
})

每个插件都绑定到一个 Vue 应用程序。你需要将用户应用程序传递给 setupDevtoolsPlugin - 与你的插件 install 方法作为第一个参数获取的相同。

 





 





export function setupDevtools (app) {
  setupDevtoolsPlugin({
    id: 'my-awesome-devtools-plugin',
    label: 'My Awesome Plugin',
    packageName: 'my-awesome-plugin',
    homepage: 'https://vuejs.ac.cn',
    app
  }, api => {
    // Logic...
  })
}

setupDevtoolsPlugin 的第二个参数是一个回调函数,它将以 Vue Devtools API 作为第一个参数。





 
 
 


export function setupDevtools (app) {
  setupDevtoolsPlugin({
    id: 'my-awesome-devtools-plugin',
    // ...
  }, api => {
    // Use the API here...
  })
}

我们现在可以在我们的 Vue 插件中导入并使用我们的 setupDevtools 函数

import { setupDevtools } from './devtools'

export default {
  install (app, options = {}) {
    // Our Vue plugin logic

    setupDevtools(app)
  }
}

Vue 2

在 Vue 2 应用程序中,你需要将根组件实例作为 app 参数传递

import { setupDevtools } from './devtools'

export default {
  install (Vue) {
    Vue.mixin({
      beforeCreate () {
        if (this.$options.myPlugin) {
          setupDevtools(this)
        }
      }
    })
  }
}

在用户的应用程序中

import Vue from 'vue'
import App from './App.vue'
import DevtoolsPlugin from './DevtoolsPlugin'

Vue.use(DevtoolsPlugin)

new Vue({
  render: h => h(App),
  myPlugin: true,
}).$mount('#app')

插件设置

使用 settings 选项,你的插件可以向用户公开一些设置。这对于允许一些自定义非常有用!

所有设置项都必须具有以下属性

  • type(见下文)
  • label:用于描述设置项的字符串
  • defaultValue

可用的设置类型

  • boolean
  • text
  • choice
    • options:类型为 { value: any, label: string } 的对象的列表
    • component:(可选)可以是 'select'(默认)或 'button-group'

示例

setupDevtoolsPlugin({
  id: 'my-awesome-devtools-plugin',
  settings: {
    test1: {
      label: 'I like vue devtools',
      type: 'boolean',
      defaultValue: true
    },
    test2: {
      label: 'Quick choice',
      type: 'choice',
      defaultValue: 'a',
      options: [
        { value: 'a', label: 'A' },
        { value: 'b', label: 'B' },
        { value: 'c', label: 'C' }
      ],
      component: 'button-group'
    },
    test3: {
      label: 'Long choice',
      type: 'choice',
      defaultValue: 'a',
      options: [
        { value: 'a', label: 'A' },
        { value: 'b', label: 'B' },
        { value: 'c', label: 'C' },
        { value: 'd', label: 'D' },
        { value: 'e', label: 'E' }
      ]
    },
    test4: {
      label: 'What is your name?',
      type: 'text',
      defaultValue: ''
    }
  },
}, api => {
  // Use `api.getSettings()` to get the current settings for the plugin
  console.log(api.getSettings())
})

你可以使用 api.on.setPluginSettings 钩子监听用户对设置的更改

api.on.setPluginSettings(payload => {
  // Do something...
})

生产环境的 Tree-shaking

由于我们将只编写用于集成 Vue Devtools 的代码,因此最好为我们包的生产版本删除它 - 从而提高大小和性能。

默认情况下,Vue 3 不会在生产环境中包含与 devtools 相关的代码。 它使用 __VUE_PROD_DEVTOOLS__ 环境变量作为编译标志来强制启用此代码。我们可以使用相同的标志来实现相同目的,并且我们还可以检查 NODE_ENV 以自动在开发环境中包含 devtools 插件。

if (process.env.NODE_ENV === 'development' || __VUE_PROD_DEVTOOLS__) {
  setupDevtools(app)
}

在你的构建设置中,你应该替换 process.env.NODE_ENV__VUE_PROD_DEVTOOLS__,用于不针对捆绑器的库构建。查看 Rollup 设置,其中包含 @rollup/plugin-replace 的示例用法。

组件

组件 API 允许你

  • 向组件树添加标签。
  • 在组件状态检查器中显示其他数据。

钩子

Vue Devtools API 包含通过 api.on 可用的钩子。钩子对于向现有元素(例如组件树节点、组件状态、时间线事件)添加新的调试信息很有用。可以使用 api.on.hookName(callback) 注册特定钩子的新回调函数。

每个钩子都期望回调函数具有相同的参数

  • payload,它包含与钩子相关的状态。它可以被修改以传递回其他信息。
  • context 公开有关 devtools 的数据

示例






 
 
 



export function setupDevtools (app) {
  setupDevtoolsPlugin({
    id: 'my-awesome-devtools-plugin',
    // ...
  }, api => {
    api.on.hookNameHere((payload, context) => {
      // Do something...
    })
  })
}

每个钩子都处理异步回调






 
 
 



export function setupDevtools (app) {
  setupDevtoolsPlugin({
    id: 'my-awesome-devtools-plugin',
    // ...
  }, api => {
    api.on.hookNameHere(async (payload, context) => {
      await something()
    })
  })
}

所有注册的回调函数将按顺序调用,包括异步回调函数,按照它们在页面上注册的顺序。

组件树

要向组件树添加标签,我们可以使用 visitComponentTree 钩子






 
 
 
 
 
 
 
 
 




export function setupDevtools (app) {
  setupDevtoolsPlugin({
    id: 'my-awesome-devtools-plugin',
    // ...
  }, api => {
    api.on.visitComponentTree((payload, context) => {
      const node = payload.treeNode
      if (payload.componentInstance.type.meow) {
        node.tags.push({
          label: 'meow',
          textColor: 0x000000,
          backgroundColor: 0xff984f
        })
      }
    })
  })
}

这个示例将向任何将 meow: true 选项添加到其定义中的组件添加一个 meow 标签

export default {
  meow: true
}

以下是一个示例结果

颜色

Vue Devtools API 中的颜色以数字而不是字符串的形式编码。你可以在 JavaScript 中使用 0x 来编写十六进制值

0x000000 // black
0xffffff // white
0xff984f // orange
0x41b86a // green

示例






 
 




api.on.visitComponentTree((payload, context) => {
  const node = payload.treeNode
  if (payload.componentInstance.type.meow) {
    node.tags.push({
      label: 'meow',
      textColor: 0x000000,
      backgroundColor: 0xff984f
    })
  }
})

通知变更

默认情况下,Vue Devtools 将确定何时更新组件树(从而再次调用钩子)。我们可以使用 api.notifyComponentUpdate 函数强制刷新

api.notifyComponentUpdate(componentInstance)

这样,如果我们知道特定组件的标签应该更改,我们可以通知 Vue Devtools,然后我们的钩子回调函数将再次被调用。

组件状态

可以使用 inspectComponent 钩子向组件状态检查器添加新数据

api.on.inspectComponent((payload, context) => {
  // ...
})

每个新字段都应该添加到 payload.instanceData.state 数组中


 
 
 
 
 

 
 
 
 
 


api.on.inspectComponent((payload, context) => {
  payload.instanceData.state.push({
    type: 'My Awesome Plugin state',
    key: '$hello',
    value: data.message
  })

  payload.instanceData.state.push({
    type: 'My Awesome Plugin state',
    key: 'time counter',
    value: data.counter
  })
})

请注意,我们如何使用 setupDevtoolsdata 参数(见下文)将一些数据从我们的库传递到 Vue Devtools 插件中。

type 将用于在状态检查器中创建一个新部分。建议使用一个变量

 








 





 







const stateType = 'My Awesome Plugin state'

export function setupDevtools (app, data) {
  setupDevtoolsPlugin({
    id: 'my-awesome-devtools-plugin',
    // ...
  }, api => {
    api.on.inspectComponent((payload, context) => {
      payload.instanceData.state.push({
        type: stateType,
        key: '$hello',
        value: data.message
      })

      payload.instanceData.state.push({
        type: stateType,
        key: 'time counter',
        value: data.counter
      })
    })
  })
}

然后,使用此变量在 Vue Devtools 插件选项中使用 componentStateTypes 声明您的数据类型







 
 
 






export function setupDevtools (app) {
  setupDevtoolsPlugin({
    id: 'my-awesome-devtools-plugin',
    label: 'My Awesome Plugin',
    packageName: 'my-awesome-plugin',
    homepage: 'https://vuejs.ac.cn',
    componentStateTypes: [
      stateType
    ],
    app
  }, api => {
    // ...
  })
}

以下是一个示例结果

如果您不传递任何参数,您可以再次使用 notifyComponentUpdate 强制当前组件的状态刷新

api.notifyComponentUpdate()

编辑组件状态

您可以将一些状态字段标记为 editable 以允许用户编辑

payload.instanceData.state.push({
  type: stateType,
  key: '$hello',
  value: data.message,
  editable: true
})

然后,您可以使用 editComponentState 钩子处理编辑提交

api.on.editComponentState(payload => {
  // ...
})

然后,您需要检查正在编辑的字段是否来自您的插件,使用 payload.type


 




api.on.editComponentState(payload => {
  if (payload.type === stateType) {
    // ...
  }
})

这样,如果用户正在编辑其他内容(例如数据属性),我们就不会做任何事情。

可以以下列方式修改已编辑的字段

  • 分配一个新值,
  • 添加一个新属性(对象)或项目(数组),
  • 删除属性或项目。

然后,您可以使用 payload.set 辅助函数来应用编辑。payload.set 的参数应该是包含我们可编辑状态的对象



 



api.on.editComponentState(payload => {
  if (payload.type === stateType) {
    payload.set(data)
  }
})

在本例中,我们执行 payload.set(data),因为我们正在发送 data.message 可编辑状态




 



payload.instanceData.state.push({
  type: stateType,
  key: '$hello',
  value: data.message,
  editable: true
})

您也可以使用单独的对象来保存您的可编辑状态(示例)。

自定义检查器

Vue Devtools 默认情况下只有一个检查器:组件检查器。Vue Devtools 插件可以在它旁边引入新的检查器以显示更多调试信息。

要设置自定义检查器,您的插件必须

  1. 使用 addInspector 注册自定义检查器。
  2. 处理 getInspectorTree 钩子以填充检查器树(在 devtools 的左侧或顶部)。
  3. 处理 getInspectorState 钩子以发送状态(在 devtools 的右侧或底部)。

注册自定义检查器

让我们从使用 addInspector 注册我们的自定义检查器开始

api.addInspector({
  id: 'my-awesome-plugin',
  label: 'Awesome!',
  icon: 'pets',
})

建议使用一个变量来存储检查器 ID,因为它将在以后的钩子中需要

 







 






const inspectorId = 'my-awesome-plugin'

export function setupDevtools (app, data) {
  setupDevtoolsPlugin({
    id: 'my-awesome-devtools-plugin',
    // ...
  }, api => {
    api.addInspector({
      id: inspectorId,
      label: 'Awesome!',
      icon: 'pets',
    })
  })
}

您可以使用任何 Material Icon 代码作为图标,例如 note_adddrag_indicator

以下是一个示例结果

发送检查器树

使用 getInspectorTree,我们可以在检查器中显示一个节点树

api.on.getInspectorTree((payload, context) => {
  // ...
})

但在此之前,我们需要检查钩子是否针对正确的检查器(我们的)调用


 




api.on.getInspectorTree((payload, context) => {
  if (payload.inspectorId === inspectorId) {
    // ...
  }
})

然后,我们可以将节点树放入 payload.rootNodes



 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 



api.on.getInspectorTree((payload, context) => {
  if (payload.inspectorId === inspectorId) {
    payload.rootNodes = [
      {
        id: 'root',
        label: 'Awesome root',
        children: [
          {
            id: 'child-1',
            label: 'Child 1',
            tags: [
              {
                label: 'awesome',
                textColor: 0xffffff,
                backgroundColor: 0x000000
              }
            ]
          },
          {
            id: 'child-2',
            label: 'Child 2'
          }
        ]
      },
      {
        id: 'root2',
        label: 'Amazing root'
      }
    ]
  }
})

以下是一个示例结果

发送检查器状态

使用 getInspectorState 钩子根据选定的节点发送状态(检查 payload.nodeId

api.on.getInspectorState((payload, context) => {
  if (payload.inspectorId === inspectorId) {
    if (payload.nodeId === 'child-1') {
      payload.state = {
        'my section': [
          {
            key: 'cat',
            value: 'meow',
          }
        ]
      }
    } else if (payload.nodeId === 'child-2') {
      payload.state = {
        'my section': [
          {
            key: 'dog',
            value: 'waf',
          }
        ]
      }
    }
  }
})

不要忘记使用 payload.inspectorId 检查检查器是否正确!

以下是在选择“子节点 1”时的示例结果

刷新检查器

Vue Devtools 不会自动刷新您的自定义检查器。要发送更新,您有两个不同的可用函数

示例

// Update tree
api.sendInspectorTree(inspectorId)

// Update state
api.sendInspectorState(inspectorId)

调用这些函数将分别调用 getInspectorTreegetInspectorState 钩子。

时间线

Vue Devtools 在时间线上附带了一些内置层,例如鼠标和键盘事件、组件事件和性能火焰图。您可以按照以下步骤添加自定义时间线层

  1. 使用 addTimelineLayer 注册层。
  2. 使用 addTimelineEvent 发送事件。

注册一个层

使用 addTimelineLayer 函数注册您的自定义时间线层

 






 
 
 
 
 



const timelineLayerId = 'my-awesome-plugin'

export function setupDevtools (app, data) {
  setupDevtoolsPlugin({
    id: 'my-awesome-devtools-plugin',
    // ...
  }, api => {
    api.addTimelineLayer({
      id: timelineLayerId,
      color: 0xff984f,
      label: 'Awesome!'
    })
  })
}

以下是一个示例结果

发送一个事件

使用 addTimelineEvent 函数添加新事件


 
 
 
 
 
 
 
 
 
 


window.addEventListener('click', event => {
  api.addTimelineEvent({
    layerId: timelineLayerId,
    event: {
      time: api.now(),
      data: {
        mouseX: event.clientX,
        mouseY: event.clientY
      }
    }
  })
})

您可以将 enableEarlyProxy 插件选项设置为 true,以便能够在 Vue devtools 打开并连接之前发送时间线事件


 
 
 
 



setupDevtoolsPlugin({
  id: 'my-awesome-devtools-plugin',
  app,
  enableEarlyProxy: true
}, api => {
  // `api` will be a proxy waiting for the real API to be available
})

事件组

您可以将事件分组在一起。它将以药丸形矩形显示它们,并在事件检查器中显示组的总持续时间。

使用相同的值在 event 上设置 groupId 选项以创建一个组。以下是一个包含 3 个事件的组的示例

const groupId = 'group-1'

devtoolsApi.addTimelineEvent({
  layerId: timelineLayerId,
  event: {
    time: api.now(),
    data: {
      label: 'group test'
    },
    title: 'group test',
    groupId
  }
})

devtoolsApi.addTimelineEvent({
  layerId: timelineLayerId,
  event: {
    time: api.now() + 10,
    data: {
      label: 'group test (event 2)',
    },
    title: 'group test',
    groupId
  }
})

devtoolsApi.addTimelineEvent({
  layerId: timelineLayerId,
  event: {
    time: api.now() + 20,
    data: {
      label: 'group test (event 3)',
    },
    title: 'group test',
    groupId
  }
})

以下是时间线上的结果,其中选择了组的第一个事件

以及事件检查器视图中的“组”选项卡

注意“组信息”部分,其中包含有关该组的更多信息。

来自包的事件

让我们使用 addTimelineEvent API 来跟踪我们的 Vue 插件公开的异步方法。我们将称之为 $doSomething





 
 
 
 
 
 
 
 
 
 
 







import { setupDevtools } from './devtools'

export default {
  install (app, options = {}) {
    app.mixin({
      methods: {
        $doSomething () {
          return new Promise(resolve => {
            setTimeout(() => {
              resolve('done')
            }, 1000)
          })
        }
      }
    })

    if (process.env.NODE_ENV === 'development' || __VUE_PROD_DEVTOOLS__) {
      setupDevtools(app, data)
    }
  }
}

我们现在可以从用户应用程序中的任何组件调用此方法

this.$doSomething()

我们希望从我们的库代码中跟踪此方法,因此我们需要从我们的 setupDevtools 函数返回一组辅助函数。devtoolsApi 变量将保存 Vue Devtools API,devtools 变量将是一个包含辅助函数的对象,我们将返回它


 

 
 
 





 




 


export function setupDevtools (app, data) {
  let devtoolsApi

  const devtools = {
    // Our helpers here
  }

  setupDevtoolsPlugin({
    id: 'my-awesome-devtools-plugin',
    // ...
  }, api => {
    devtoolsApi = api

    // ...
  })

  return devtools
}

回到我们的库代码中,我们可以创建一个 devtools 变量来检索 setupDevtools 的结果,即包含我们的辅助函数的对象





 






 




import { setupDevtools } from './devtools'

export default {
  install (app, options = {}) {
    let devtools

    app.mixin({
      // ...
    })

    if (process.env.NODE_ENV === 'development' || __VUE_PROD_DEVTOOLS__) {
      devtools = setupDevtools(app)
    }
  }
}

让我们实现一个 trackStart 辅助函数,当我们想要开始跟踪调用时创建一个事件,并在调用完成时创建另一个事件



 


 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 














export function setupDevtools (app, data) {
  let devtoolsApi
  let trackId = 0

  const devtools = {
    trackStart: (label: string) => {
      const groupId = 'track' + trackId++

      // Start
      devtoolsApi.addTimelineEvent({
        layerId: timelineLayerId,
        event: {
          time: api.now(),
          data: {
            label
          },
          title: label,
          groupId
        }
      })

      return () => {
        // End
        devtoolsApi.addTimelineEvent({
          layerId: timelineLayerId,
          event: {
            time: api.now(),
            data: {
              label,
              done: true
            },
            title: label,
            groupId
          }
        })
      }
    }
  }

  setupDevtoolsPlugin({
    id: 'my-awesome-devtools-plugin',
    // ...
  }, api => {
    devtoolsApi = api

    // ...
  })

  return devtools
}

trackId 用于为每次调用 startTrack 生成一个唯一的组 ID。通过使用相同的 ID,我们将创建一个包含两个事件的组,即使它们并非同时创建。

我们可以像这样使用 trackStart 辅助函数

const trackEnd = devtools.trackStart('some-label')
// Later
trackEnd()

我们现在可以在我们的库代码中使用 trackStart 函数






 


 











let devtools

app.mixin({
  methods: {
    $doSomething () {
      const trackEnd = devtools ? devtools.trackStart('$doSomething') : null
      return new Promise(resolve => {
        setTimeout(() => {
          if (trackEnd) trackEnd()
          resolve('done')
        }, 1000)
      })
    }
  }
})

if (process.env.NODE_ENV === 'development' || __VUE_PROD_DEVTOOLS__) {
  devtools = setupDevtools(app, data)
}

警告

我们必须注意检查我们的 devtools 变量是否已定义,因为我们在生产中剥离了 devtools 代码!请参阅 生产中的树摇

以下是一个示例结果

以及事件检查器


感谢您遵循 Vue Devtools 插件开发指南!您可以在 API 参考 中找到有关 API 的更详细说明。祝您构建愉快!😸