javascript

vue‑why‑render 0.8: граф реактивности, WASM‑ядро и VS Code расширение

  • понедельник, 26 октября 2026 г. в 00:00:20
https://habr.com/ru/articles/1098942/

Месяц назад я написал статью о том, как сделал отладчик рендеров для Vue — инструмент, который называет точную причину каждой перерисовки. Рассказывал про renderTriggered, про ловушки с process.env.NODE_ENV, кириллицей в байтовых смещениях и вычислением чужих computed.

С тех пор вышла версия 0.8 — с тремя большими фичами, которые я не планировал делать так быстро. Расскажу, что нового и зачем.

Что было

Если не читали первую статью — вкратце. vue-why-render подключается к Vue 3 приложению двумя строками:

import { scan } from 'vue-why-render'
scan(app, { panel: true })

И показывает на каждом перерисованном компоненте: имя, счётчик рендеров, длительность и причину. Не target: Proxy, а prop badge: {text} → {text} — new reference, same value. Для Pinia — store cart.items: Array(1) → Array(0) · action clear(). Для роутера — route.params.id: "1" → "2".

Плюс панель с пятью вкладками, бюджеты рендеров, тесты (expectRenders(Card).toBe(0)), отчёты в JSON/Markdown. Ноль зависимостей, MIT.

Что нового в 0.8

1. Граф реактивности (Deps)

Раньше vue‑why‑render отвечал на вопрос «почему этот компонент перерисовался?». Теперь он отвечает на вопрос «кто от чего зависит?».

Новая вкладка Deps строит живую карту зависимостей: компонент ↔ реактивный источник. Здесь я как раз подключил renderTracked — в первой статье я объяснял, почему не стал его использовать (слишком много вызовов, замедляет приложение). Решение: собирать данные лениво, только для открытой вкладки Deps, и дедуплицировать на лету.

Два режима:

  • Components → какие ref, prop, store и computed читает этот компонент?

  • Sources → какие компоненты зависят от этого ref?

На каждом ребре — счётчик чтений и триггеров. Если UserAvatar читает user.name 47 раз, но это вызвало 2 рендера — остальные чтения безвредны. Если CartBadge дёргается от cart.items на каждый keystroke — вот проблема.

Это рентген для реактивности Vue.

2. Rust/WASM ядро

В первой статье всё работало на чистом JS. Для небольших приложений — нормально. Но когда компонентов сотни или тысячи, три операции начинают заметно тормозить:

  • Построение дерева компонентов

  • Дедупликация причин рендера

  • Сравнение (дифф) двух отчётов

Я переписал их на Rust и скомпилировал в WebAssembly.

Как это устроено:

WASM‑модуль подгружается лениво, отдельным чанком. Бандл приложения не растёт — пока vue‑why‑render не нужен, ничего лишнего не грузится. Если WASM недоступен (SSR, jsdom, строгий CSP) — автоматически включается JS‑фолбэк. Конфигурировать ничего не нужно.

scan(app, { panel: true })
// Внутри:
// 1. Пробуем загрузить .wasm чанк
// 2. Если ok — используем Rust-реализацию
// 3. Если нет — переключаемся на JS
// Потребитель не замечает разницы

Для обычного приложения разница в скорости незаметна. Для крупного — заметна.

3. VS Code расширение

Браузерные DevTools — это хорошо, но переключаться между Chrome и VS Code надоело. Поэтому я сделал WebSocket‑мост: сканер в браузере стримит данные прямо в редактор.

Что появляется в VS Code:

  • Дерево компонентов в сайдбаре — отсортировано по рендерам, иконки 🔥 для горячих компонентов

  • Дерево зависимостей — тот же граф реактивности

  • CodeLens на .vue файлах — количество рендеров и средняя длительность прямо над <script setup>

  • Статус‑бар — живой счётчик рендеров

  • Команды — pause, resume, reset из палитры

Не нужно переключаться между браузером и редактором. Данные там, где пишется код.

Что ещё добавилось

Vue DevTools интеграция — помимо собственной панели, vue‑why‑render теперь регистрируется в Vue DevTools. Второй инспектор с деревом реактивных источников (цветные теги: ref, prop, store, computed) и второй слой на таймлайне, логирующий каждый триггер как событие.

Nuxt модуль — одна строка в конфиге:

export default defineNuxtConfig({
  modules: ['vue-why-render/nuxt']
})

Модуль уже в каталоге nuxt.com/modules.

Итого

За месяц пакет набрал 668 загрузок на npm, попал в каталог модулей Nuxt, и получил три фичи, которые я честно не планировал делать в ближайшие полгода.

Если нашли баг или хотите фичу — issues открыты.