DeferRender: маленький Vue-компонент для контроля момента рендера

от автора

в
Время чтения: 1 мин.

В веб-разработке есть класс багов, которые сложно объяснить, но легко увидеть:

  • transition не срабатывает
  • размеры элементов считаются неправильно
  • layout “скачет” при первом появлении
  • логика, завязанная на DOM, ведёт себя нестабильно

Во многих случаях причина одна —
👉 код выполняется раньше, чем браузер завершил первый рендер

В этой статье разберём простой паттерн, который решает эту проблему — DeferRender.


🚧 Проблема: неправильный момент выполнения

Когда Vue монтирует компонент:

  1. создаётся DOM
  2. применяются стили
  3. браузер делает layout
  4. происходит paint

Но твой код может выполниться между этими этапами.

Например:

  • transition включается до первого paint → не срабатывает
  • scrollHeight читается до layout → возвращает 0
  • вычисления делаются до того, как элемент реально появился на экране

👉 В итоге поведение становится непредсказуемым


💡 Решение: отложить рендер

Вот минимальная реализация:

<script setup lang="ts">
import { ref, onMounted } from 'vue'const ready = ref(false)onMounted(() => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
ready.value = true
})
})
})
</script>
<template>
<template v-if="ready">
<slot />
</template>
<template v-else>
<slot name="placeholder" />
</template>
</template>

🔍 Как это работает

Ключевая часть:

requestAnimationFrame(() => {
requestAnimationFrame(() => {
ready.value = true
})
})

Что происходит:

  • первый requestAnimationFrame — ждём следующий кадр
  • второй — ещё один кадр

👉 За это время браузер успевает:

  • применить стили
  • посчитать layout
  • выполнить первый paint

И только после этого отображается основной контент.


🎯 Что это даёт

1. Корректный старт для transition

Проблема не в самих анимациях, а в том, когда они запускаются.

Без defer:

элемент появился → сразу финальное состояние → transition не запускается

С defer:

элемент уже отрисован → состояние меняется → transition может выполниться

2. Корректные размеры элементов

Если ты работаешь с:

  • getBoundingClientRect
  • scrollHeight
  • useElementSize

👉 значения будут корректными, потому что layout уже посчитан


3. Предсказуемое поведение UI

Особенно полезно для:

  • dropdown / accordion
  • sticky-блоков
  • компонентов с вычисляемыми размерами
  • сложных layout-комбинаций

4. Контроль placeholder → контент

<DeferRender>
<template #placeholder>
<Skeleton />
</template> <RealContent />
</DeferRender>

👉 здесь важно не “плавное появление”, а контролируемый момент переключения


⚠️ Почему не nextTick

nextTick ждёт обновление Vue, но:

  • не гарантирует завершение layout
  • не гарантирует paint

👉 requestAnimationFrame работает на уровне браузера, а не фреймворка


🧠 Когда использовать

Используй DeferRender, если:

  • transition ведёт себя нестабильно
  • размеры элементов вычисляются неправильно
  • UI зависит от layout сразу после mount
  • есть визуальные глитчи при первом рендере

Не используй, если:

  • компонент простой и статичный
  • нет зависимости от времени рендера

🧩 Итог

DeferRender — это:

⏳ способ сдвинуть рендер на момент, когда браузер уже готов

Он не добавляет анимации и не меняет внешний вид напрямую,
но устраняет проблемы, вызванные неправильным таймингом.

И это один из тех случаев, когда
пара вызовов requestAnimationFrame даёт больше пользы, чем сложная логика.


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Сколько будет 2 + 2?