Extending and debugging
The reactivity inspector
xray runs inside the page itself, with no browser extension. Switched on, it outlines
every element that has directives, shows the scope of each one and flashes the element every time
a reactive effect writes into it. You can watch the reactivity happening.
The inspector exists only in the full build.
Switching it on
V.xray(); // on and off
V.xray(true); // force it on
V.xray(false); // force it off
The shortcut Ctrl + Shift + X is installed on the first call. To have the shortcut available from the start, without switching anything on:
V.enableXrayShortcut();
What it shows
| Tab | What it brings |
|---|---|
| State | Every scope on the page, with the variables visible in each. Simple values are editable right there |
| Components | Mounted instances, with props, state and the host element |
| Stores | The global stores and the contents of each |
| Events | Events fired by directives, with the element they came from |
| Network | Requests, with method, URL, status and duration |
| Performance | The count of effects per element and how many times each one has re-run |
Clicking an element on the page selects the matching scope. Clicking an element in the panel highlights it and scrolls the page to it.
How to read the performance tab
The count adds up, for each element, the effects created by its own directives plus the effects of the interpolated texts that are its direct children. A high number on a small element usually means too much interpolation in one place, and it is worth breaking the block up — each stretch then re-runs on its own, instead of all of them together.
The cost in production
The module registers nothing when it is imported: no listener, no style and no timer exists before
the first call. Even so, in production the safer road is to serve voodoo.min.js,
which does not carry the inspector.
Debugging without the inspector
Detailed warnings
V.config.devtools = true;
With this option on, the anchor comments created by v-if and v-for get
names, which makes the tree far more readable in the browser inspector. Unregistered components
start warning in the console as well. The same thing exists as an attribute on the script tag:
data-devtools.
The global error handler
V.onError((err, context) => {
console.error('[app]', context, err);
sendToMonitoring(err, context);
});
The context says where it came from: directive v-click,
expression "...", hook mounted, request GET /api/x,
event click ("save()") and so on. It is the hook for shipping production errors to a
monitoring service.
Inspecting from the console
V.scope.data; // the root scope
V.getScope(document.querySelector('#list')); // the element's scope, if it created one
V.findScope(document.querySelector('li')); // the effective scope, walking up the ancestors
V.instances; // a Set of the mounted components
V.components; // a Map of the registered definitions
V.directives; // a Map of the registered directives
V.magics; // a Map of the magic variables
V.stores; // every store
Logging from the HTML
<div v-data="{ n: 0 }">
<button @click="n++; $log('n is now', n)">Add and log</button>
<p>Open the browser console to see the messages.</p>
</div>
$log writes to the console with the prefix [Voodoo].
Forcing and stopping the processing
V.start(); // walks and initializes from the body
V.start(document.querySelector('#area'));
V.walk(element, scope); // initializes a stretch with a specific scope
V.refresh(element); // reinitializes a root
V.destroy(element); // unmounts, stopping effects and removing listeners
V.stopObserving(); // switches the MutationObserver off
The devtools event bus
Any module — yours included — can report activity to the panel. The Network tab lists everything
that arrives through network, even when the request did not go through the
V.http client.
V.devtools.emit('network', {
method: 'GET',
url: '/api/users',
status: 200,
ok: true,
duration: 128,
source: 'my-plugin'
});
const off = V.devtools.on('network', (event) => console.log(event.url));
off();
The types are network, event, navigation,
locale and update. Emitting with no listener registered costs one
Map lookup and nothing else, so reporting is cheap.