File size: 20,959 Bytes
4d70170 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 |
# Plugin API References
::: warning
The API is only available in Vue Devtools 6+
:::
## Plugin setup
### setupDevtoolsPlugin
Registers a devtools plugin. Describes the plugin and provides access to the devtools API.
```js
setupDevtoolsPlugin (pluginDescriptor, setupFn)
```
The plugin descriptor is an object describing the devtools plugin to the Vue devtools user.
It has the following properties:
- `id`: a unique id between all possible plugins. It's recommended to use a Reverse Domain Name notation, for example: `org.vuejs.router`, or the npm package name.
- `app`: the current application instance. The devtools is scoped to a specific application, so you have to specify on which application instance the devtools plugin is going to work.
- `label`: the label displayed to the user. It's recommended to use a user-friendly name from your plugin, for example: `'Vue Router'`. Do not put `'devtools'` or `'plugin'` in the name, since the user will be seeing this devtools plugin while using your Vue plugin already.
- `packageName` (optional): The `npm` package name associated with the devtools plugin, for example `'vue-router'`.
- `homepage` (optional): URL to your documentation.
- `logo` (optional): URL to a logo of your Vue plugin.
- `componentStateTypes` (optional): an array of custom component state section names you are going to add to the Component inspector. If you add new state to the component inspector, you should declare their sections here so the devtools can display the plugin icon.
- `disableAppScope` (optional): if set to `true`, the hooks registered with this plugin will not be scoped to the associated app. In that case, you might need to use the `app` payload property to check what the current app is inside each hook.
- `disablePluginScope` (optional): if set to `true`, the hooks registered with this plugin will not be scoped to the current plugin. In that case, you might need to use the `pluginId` payload property (depending on the hook) to check what the related plugin is inside each hook.
- `enableEarlyProxy` (optional): if set to `true`, the plugin will run even if the Vue devtools are not connected yet using a proxy of the Plugin API and a buffer queue. This is useful if you need to add timeline events before the user opens the devtools.
- `settings` (optional): an object describing the plugin settings. Learn more about plugin settings [here](./plugins-guide.md#plugin-settings).
Example:
```js
const stateType = 'routing properties'
setupDevtoolsPlugin({
id: 'org.vuejs.router',
app,
label: 'Vue Router',
packageName: 'vue-router',
homepage: 'https://router.vuejs.org/',
logo: 'https://vuejs.org/images/icons/favicon-96x96.png',
componentStateTypes: [
stateType
]
}, (api) => {
// Use the API here
})
```
## Component inspector
### on.visitComponentTree
Use this hook to add tags in the component tree.
The `payload` argument:
- `app`: app instance currently active in the devtools
- `componentInstance`: the current component instance data in the tree
- `treeNode`: the tree node that will be sent to the devtools
- `filter`: the current value of the seach input above the tree in the component inspector
Example:
```js
api.on.visitComponentTree((payload) => {
const node = payload.treeNode
if (node.name === 'MyApp') {
node.tags.push({
label: 'root',
textColor: 0x000000,
backgroundColor: 0xFF984F
})
}
else {
node.tags.push({
label: 'test',
textColor: 0xFFAAAA,
backgroundColor: 0xFFEEEE,
tooltip: `It's a test!`
})
}
})
```
### on.inspectComponent
Use this hook to add new information to the state of the selected component.
The `payload` argument:
- `app`: app instance currently active in the devtools
- `componentInstance`: the current component instance data in the tree
- `instanceData`: the state that will be sent to the devtools
To add new state, you can push new fields into the `instanceData.state` array:
#### Basic field
- `type`: name of the section under which the field will appear
- `key`: name of the field
- `value`: value of the field
- `editable` (optional): boolean to enable edition
#### Custom value
By default, the devtools will display your field depending on whether it's an object, an array, etc. You can customize the field display by putting a `{ _custom: {} }` object to the value.
The `_custom` object has the following properties:
- `type`: Displays the type of the value. Examples: `'router'`, `'component'`, `'service'`...
- `display`: Text displayed instead of the value. Example: `'5 minutes'`
- `tooltip`: Tooltip when hovering the value text (`display`)
- `value`: Actual value
- `abstract`: No value is displayed. Useful for indexes. For example, `Set` objects have abstract index child fields: `0`, `1`...
- `readOnly`: mark this value has not editable
- `fields`: an object of configure immediate child fields
- `abstract`
- `actions`: an array of buttons to add to the field
- `icon`: material icon identifier
- `tooltip`: button tooltip
- `action`: function to be executed
When you add new sections with the `type` property, you should declare them in the `componentStateTypes` array in the plugin descriptor when you call the `setupDevtoolsPlugin`.
Example:
```js
api.on.inspectComponent((payload) => {
if (payload.instanceData) {
payload.instanceData.state.push({
type: stateType,
key: 'foo',
value: 'bar'
})
payload.instanceData.state.push({
type: stateType,
key: 'time',
value: {
_custom: {
type: null,
readOnly: true,
display: `${time}s`,
tooltip: 'Elapsed time',
value: time,
actions: [
{
icon: 'input',
tooltip: 'Log to console',
action: () => console.log('current time:', time)
}
]
}
}
})
}
})
```
### on.editComponentState
If you mark a field as `editable: true`, you should also use this hook to apply the new value sent by the devtools.
You have to put a condition in the callback to target only your field type:
```js
api.on.editComponentState((payload) => {
if (payload.type === stateType) {
// Edit logic here
}
})
```
The `payload` argument:
- `app`: app instance currently active in the devtools
- `type`: the current field type
- `path`: an array of string that represents the property edited by the user. For example, if the user edits the `myObj.myProp.hello` property, the `path` will be `['myObj', 'myProp', 'hello']`.
- `state`: object describing the edit with those properties:
- `value`: new value
- `newKey`: string that is set if the key of the value changed, usually when it's in an object
- `remove`: if `true`, the value should be removed from the object or array
- `set`: an helper function that makes it easy to apply the edit on a state object
Example:
```js
api.on.editComponentState((payload) => {
if (payload.type === stateType) {
payload.set(myState)
}
})
```
Here is a full example of an editable custom component field:
```js
const myState = {
foo: 'bar'
}
api.on.inspectComponent((payload) => {
if (payload.instanceData) {
payload.instanceData.state.push({
type: stateType,
key: 'foo',
value: myState.foo,
editable: true
})
}
})
api.on.editComponentState((payload) => {
if (payload.type === stateType) {
payload.set(myState)
}
})
```
As you can see, you should use an object to hold the field value so that it can be assigned to.
### notifyComponentUpdate
If your state has changed, you can tell the devtools to refresh the selected component state with the `notifyComponentUpdate` method:
```js
setInterval(() => {
api.notifyComponentUpdate()
}, 5000)
```
You can also pass a specific component instance:
```js
api.notifyComponentUpdate(vm)
```
## Custom inspector
Custom inspectors are useful to display debugging information about your library using an inspectable tree.
### addInspector
This function registers a new custom inspector.
The options are:
- `id`: unique custom inspector id
- `label`: label displayed in the `Inspector` sub menu
- `icon` (optional): [Material icon code](https://material.io/resources/icons/), for example `'star'`
- `treeFilterPlaceholder` (optional): placeholder of the filter input above the tree
- `stateFilterPlaceholder` (optional): placeholder of the filter input in the state inspector
- `noSelectionText` (optional): text displayed in the inspector pane when no node is selected
- `actions`: an array of buttons to add to the header of the inspector
- `icon`: material icon identifier
- `tooltip`: button tooltip
- `action`: function to be executed
- `nodeActions`: an array of buttons to add to the selected node pane
- `icon`: material icon identifier
- `tooltip`: button tooltip
- `action`: function to be executed
Example:
```js
const INSPECTOR_ID = 'test-inspector'
api.addInspector({
id: INSPECTOR_ID,
label: 'Test inspector',
icon: 'tab_unselected',
treeFilterPlaceholder: 'Search for test...',
actions: [
{
icon: 'star',
tooltip: 'Test custom action',
action: () => console.log('Meow! 🐱')
}
],
nodeActions: [
{
icon: 'star',
tooltip: 'Test node custom action',
action: nodeId => console.log('Node action:', nodeId)
}
]
})
```
::: tip
It's recommended to use a variable to put the `id`, so that you can reuse it afterwards.
:::
### on.getInspectorTree
This hook is called when the devtools wants to load the tree of any custom inspector.
You have to put a condition in the callback to target only your inspector:
```js
api.on.getInspectorTree((payload) => {
if (payload.inspectorId === 'test-inspector') {
// Your logic here
}
})
```
The `payload` argument:
- `app`: app instance currently active in the devtools
- `inspectorId`: id of the current custom inspector
- `filter`: string of the user input in the search field
- `rootNodes`: array of root nodes of the tree you want to display in the devtools
Each node can have those properties:
- `id`: a unique node id
- `label`: the text displayed in the tree
- `children` (optional): an array of child nodes
- `tags` (optional): an array of tag objects:
- `label`: text displayed in the tag
- `textColor`: text color, for example: `0x000000` for black
- `backgroundColor`: background color, for example: `0xffffff` for white
- `tooltip` (optional): HTML for a tooltip over the tag
Example:
```js
api.on.getInspectorTree((payload) => {
if (payload.inspectorId === 'test-inspector') {
payload.rootNodes = [
{
id: 'root',
label: `Root (${time})`,
children: [
{
id: 'child',
label: `Child ${payload.filter}`,
tags: [
{
label: 'active',
textColor: 0x000000,
backgroundColor: 0xFF984F
},
{
label: 'test',
textColor: 0xFFFFFF,
backgroundColor: 0x000000
}
]
}
]
}
]
}
})
```
### on.getInspectorState
This hook is called when the devtools needs to load the state for the currently selected node in a custom inspector.
You have to put a condition in the callback to target only your inspector:
```js
api.on.getInspectorState((payload) => {
if (payload.inspectorId === 'test-inspector') {
// Your logic here
}
})
```
The `payload` argument:
- `app`: app instance currently active in the devtools
- `inspectorId`: id of the current custom inspector
- `nodeId`: id of the currently selected node
- `state`: state sent to the devtools
The state is an object, which keys are the section names in the state inspector, and the value is an array of fields:
```js
payload.state = {
'section 1': [
// fields
],
'section 2': [
// fields
]
}
```
Each field is an object with:
- `type`: name of the section under which the field will appear
- `key`: name of the field
- `value`: value of the field
- `editable` (optional): boolean to enable edition
You can also use a [Custom value](#custom-value).
Example:
```js
api.on.getInspectorState((payload) => {
if (payload.inspectorId === 'test-inspector') {
if (payload.nodeId === 'root') {
payload.state = {
'root info': [
{
key: 'foo',
value: myState.foo,
editable: true
},
{
key: 'time',
value: time
}
]
}
}
else {
payload.state = {
'child info': [
{
key: 'answer',
value: {
_custom: {
display: '42!!!',
value: 42,
tooltip: 'The answer'
}
}
}
]
}
}
}
})
```
### on.editInspectorState
If you mark a field as `editable: true`, you should also use this hook to apply the new value sent by the devtools.
You have to put a condition in the callback to target only your inspector:
```js
api.on.editInspectorState((payload) => {
if (payload.inspectorId === 'test-inspector') {
// Edit logic here
}
})
```
The `payload` argument:
- `app`: app instance currently active in the devtools
- `inspectorId`: id of the current custom inspector
- `nodeId`: id of the currently selected node
- `type`: the current field type
- `path`: an array of string that represents the property edited by the user. For example, if the user edits the `myObj.myProp.hello` property, the `path` will be `['myObj', 'myProp', 'hello']`.
- `state`: object describing the edit with those properties:
- `value`: new value
- `newKey`: string that is set if the key of the value changed, usually when it's in an object
- `remove`: if `true`, the value should be removed from the object or array
- `set`: an helper function that makes it easy to apply the edit on a state object
Example:
```js
api.on.editInspectorState((payload) => {
if (payload.inspectorId === 'test-inspector') {
if (payload.nodeId === 'root') {
payload.set(myState)
}
}
})
```
### sendInspectorTree
If you need to update the tree to the user, call this function to ask for a refresh.
Example:
```js
setInterval(() => {
api.sendInspectorTree('test-inspector')
}, 5000)
```
### sendInspectorState
If you need to update the currently selected node state to the user, call this function to ask for a refresh.
Example:
```js
setInterval(() => {
api.sendInspectorState('test-inspector')
}, 5000)
```
### selectInspectorNode
Select a specific node in the inspector tree. The arguments are:
- `inspectorId`: the id of your inspector
- `nodeId`: the id of the node to be selected
Example:
```js
api.selectInspectorNode('test-inspector', 'some-node-id')
```
## Timeline
### now
Returns the current time with the maximum available precision.
```js
api.now()
```
### addTimelineLayer
Register a new timeline layer with this method. The options are:
- `id`: unique id of the layer. It's recommended to use a variable to store it.
- `label`: text displayed in the layer list
- `color`: color of the layer background and event graphics
- `skipScreenshots` (optional): don't trigger a screenshot for the layer events
- `groupsOnly` (optional): only display groups of events (they will be drawn as rectangles)
- `ignoreNoDurationGroups` (optional): skip groups with no duration (useful when `groupsOnly` is `true`)
Example:
```js
api.addTimelineLayer({
id: 'test-layer',
label: 'Test layer',
color: 0x92A2BF
})
```
### addTimelineEvent
Use this function to send a new event on the timeline.
- `layerId`: id of the layer
- `event`: event object
- `time`: time in millisecond when the event happened
- `data`: state displayed when selecting the event
- `title` (optional): text displayed in the event list
- `subtitle` (optional): secondary text displayed in the event list
- `logType` (optional): either `'default'`, `'warning'` or `'error'`
- `meta` (optional): object where you can store metadata about the object that will not be displayed when it's selected
- `groupId` (optional): id used to group multiple event together
Example:
```js
api.addTimelineEvent({
layerId: 'test-layer',
event: {
time: api.now(),
data: {
info: 'window.keyup',
key: event.key
},
groupId: event.key,
title: 'Group test',
meta: {
foo: 'bar'
}
}
})
```
### on.inspectTimelineEvent
This hook is called when a timline event is selected. It's useful if you want to send additional information to the devtools in a lazy way.
You have to put a condition in the callback to target only your timeline layer:
```js
api.on.inspectTimelineEvent((payload) => {
if (payload.layerId === 'test-layer') {
// Your logic here
}
})
```
Example:
```js
api.on.inspectTimelineEvent((payload) => {
if (payload.layerId === 'test-layer') {
// Async operation example
return new Promise((resolve) => {
setTimeout(() => {
payload.data = {
...payload.data,
hey: 'hello'
}
resolve()
}, 1000)
})
}
})
```
### on.timelineCleared
This hook is called when the timeline is cleared by the user. Note that clearing the timeline affects all apps and layers simultaneously.
```js
api.on.timelineCleared(() => {
console.log('timeline is cleared!')
})
```
## Settings
Plugin settings allow the user to customize the plugin behavior. Learn more about plugin settings [here](./plugins-guide.md#plugin-settings).
### getSettings
Get the current plugin settings.
Example:
```js
api.getSettings()
```
### on.setPluginSettings
Hook called when the user changes the plugin settings.
Payload properties:
- `key`: settings item
- `newValue`: new value for the changed settings
- `oldValue`: its old value (deep clone)
- `settings`: the whole current settings state object
```js
// Plugin settings change
api.on.setPluginSettings((payload) => {
console.log(
'plugin settings changed',
payload.settings,
// Info about the change
payload.key,
payload.newValue,
payload.oldValue
)
})
```
## Utilities
### getComponentInstances
Component instances on the Vue app.
- `app`: the target Vue app instance
Example:
```js
let componentInstances = []
api.on.getInspectorTree(async (payload) => {
if (payload.inspectorId === 'test-inspector') { // e.g. custom inspector
componentInstances = await api.getComponentInstances(app)
for (const instance of instances) {
payload.rootNodes.push({
id: instance.uid.toString(),
label: `Component ${instance.uid}`
})
}
// something todo ...
}
})
```
### getComponentBounds
Computes the component bounds on the page.
Example:
```js
api.on.inspectComponent(async (payload) => {
if (payload.instanceData) {
const bounds = await api.getComponentBounds(payload.componentInstance)
payload.instanceData.state.push({
type: stateType,
key: 'bounds',
value: bounds
? {
left: bounds.left,
top: bounds.top,
width: bounds.width,
height: bounds.height
}
: null
})
}
})
```
### getComponentName
Retrieves the component name.
Example:
```js
api.on.inspectComponent(async (payload) => {
if (payload.instanceData) {
const componentName = await api.getComponentName(payload.componentInstance)
payload.instanceData.state.push({
type: stateType,
key: 'component name',
value: componentName
})
}
})
```
### highlightElement
Highlight the element of the component.
- `instance`: the target component instance
Example:
```js
const componentInstances = [] // keeped component instance of the Vue app (e.g. `getComponentInstances`)
api.on.getInspectorState((payload) => {
if (payload.inspectorId === 'test-inspector') { // e.g. custom inspector
// find component instance from custom inspector node
const instance = componentInstances.find(instance => instance.uid.toString() === payload.nodeId)
if (instance) {
api.highlightElement(instance)
}
// something todo ...
}
})
```
### unhighlightElement
Unhighlight the element.
- `instance`: the target component instance
Example:
```js
const componentInstances = [] // keeped component instance of the Vue app (e.g. `getComponentInstances`)
api.on.getInspectorState((payload) => {
if (payload.inspectorId === 'test-inspector') { // e.g. custom inspector
// find component instance from custom inspector node
const instance = componentInstances.find(instance => instance.uid.toString() === payload.nodeId)
if (instance) {
api.unhighlightElement(instance)
}
// something todo ...
}
})
```
|