Перейти к основному содержимому

[Medium] Статический хойстинг Vue3

1. Что такое Static Hoisting в Vue 3?

Объясните static hoisting в Vue 3.

В Vue 3 static hoisting (статический хойстинг) — это оптимизация на этапе компиляции.

Определение

Во время компиляции шаблона Vue анализирует, какие узлы полностью статические (без реактивных зависимостей). Эти статические узлы выносятся в константы на уровне модуля и создаются однократно. При последующих перерисовках Vue переиспользует их вместо повторного создания и сравнения.

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

Компилятор анализирует AST шаблона и выносит поддеревья, которые никогда не изменяются. При обновлениях пересоздаются только динамические части.

Пример до/после

Шаблон до компиляции:

<template>
<div>
<h1>Статический заголовок</h1>
<p>Статическое содержимое</p>
<div>{{ dynamicContent }}</div>
</div>
</template>

Скомпилированный JavaScript (упрощённо):

// статические узлы вынесены однократно
const _hoisted_1 = /*#__PURE__*/ h('h1', null, 'Статический заголовок');
const _hoisted_2 = /*#__PURE__*/ h('p', null, 'Статическое содержимое');

function render() {
return h('div', null, [
_hoisted_1, // переиспользуется
_hoisted_2, // переиспользуется
h('div', null, dynamicContent.value), // динамический
]);
}

Преимущества

  1. Снижение стоимости создания VNode
  2. Меньше работы при diff
  3. Лучшая производительность рендеринга
  4. Автоматическая оптимизация (дополнительный код не требуется)

2. Как работает Static Hoisting

Как static hoisting работает внутри?

Процесс компиляции

  1. Обнаружение динамических привязок
    • {{ }}, v-bind, v-if, v-for, динамические props и т.д.
  2. Пометка статических узлов
    • Если узел и его дочерние элементы статические -> кандидат для выноса
  3. Вынос статических узлов
    • Перемещение статических узлов/констант за пределы render()

Пример 1: полностью статическое поддерево

<template>
<div>
<h1>Заголовок</h1>
<p>Это статический текст</p>
<div>Статический блок</div>
</div>
</template>

Скомпилировано (упрощённо):

const _hoisted_1 = h('h1', null, 'Заголовок');
const _hoisted_2 = h('p', null, 'Это статический текст');
const _hoisted_3 = h('div', null, 'Статический блок');

function render() {
return h('div', null, [_hoisted_1, _hoisted_2, _hoisted_3]);
}

Пример 2: смешанные статические и динамические элементы

<template>
<div>
<h1>Статический заголовок</h1>
<p>{{ message }}</p>
<div class="static-class">Статическое содержимое</div>
<span :class="dynamicClass">Динамическое содержимое</span>
</div>
</template>

Скомпилировано (упрощённо):

const _hoisted_1 = h('h1', null, 'Статический заголовок');
const _hoisted_2 = { class: 'static-class' };
const _hoisted_3 = h('div', _hoisted_2, 'Статическое содержимое');

function render() {
return h('div', null, [
_hoisted_1,
h('p', null, message.value),
_hoisted_3,
h('span', { class: dynamicClass.value }, 'Динамическое содержимое'),
]);
}

Пример 3: вынос статических props

<template>
<div>
<div class="container" id="main">Содержимое</div>
<button disabled>Кнопка</button>
</div>
</template>

Скомпилировано (упрощённо):

const _hoisted_1 = { class: 'container', id: 'main' };
const _hoisted_2 = { disabled: true };
const _hoisted_3 = h('div', _hoisted_1, 'Содержимое');
const _hoisted_4 = h('button', _hoisted_2, 'Кнопка');

3. Директива v-once

Директива v-once

Если разработчик хочет явно пометить поддерево как однократно рендерящееся, используйте v-once.

Что делает v-once

v-once указывает Vue отрендерить элемент/поддерево один раз. Даже если выражения динамические, они вычисляются только при первом рендере и больше никогда не обновляются.

Базовое использование

<template>
<div>
<!-- рендерится один раз -->
<div v-once>
<h1>{{ title }}</h1>
<p>{{ content }}</p>
</div>

<!-- обычная реактивная область -->
<div>
<h1>{{ title }}</h1>
<p>{{ content }}</p>
</div>
</div>
</template>

<script setup>
import { ref } from 'vue';

const title = ref('Начальный заголовок');
const content = ref('Начальное содержимое');

setTimeout(() => {
title.value = 'Новый заголовок';
content.value = 'Новое содержимое';
}, 1000);
</script>

v-once vs static hoisting

ХарактеристикаStatic Hoistingv-once
СрабатываниеАвтоматический анализ компилятораРучная директива
Лучше дляПолностью статических узловДинамических выражений, которые должны рендериться один раз
ПроизводительностьЛучше для статических секцийХорошо для однократного динамического рендера
Момент принятия решенияЭтап компиляцииНамерение разработчика

Типичные случаи использования

<template>
<!-- Случай 1: однократное отображение данных -->
<div v-once>
<p>Создано: {{ createdAt }}</p>
<p>Автор: {{ creator }}</p>
</div>

<!-- Случай 2: тяжёлое, но стабильное поддерево -->
<div v-once>
<div class="header">
<h1>Заголовок</h1>
<nav>Навигация</nav>
</div>
</div>

<!-- Случай 3: однократный снимок в элементе списка -->
<div v-for="item in items" :key="item.id">
<div v-once>
<h2>{{ item.title }}</h2>
<p>{{ item.description }}</p>
</div>
</div>
</template>

4. Частые вопросы на собеседованиях

Частые вопросы на собеседованиях

Вопрос 1: внутреннее устройство static hoisting

Объясните static hoisting и как он улучшает производительность.

Нажмите, чтобы увидеть ответ

Static hoisting — это оптимизация на этапе компиляции:

  1. Компилятор сканирует шаблон на предмет статических и динамических узлов
  2. Статические узлы выносятся за пределы render() как константы
  3. Фаза обновления переиспользует вынесенные узлы и пропускает их diff

Прирост производительности достигается за счёт:

  • меньшего количества выделений VNode
  • меньшего объёма обхода patch/diff
  • меньшего количества повторных создания объектов

Вопрос 2: static hoisting vs v-once

Объясните различия и случаи использования.

Нажмите, чтобы увидеть ответ
  • Static hoisting: автоматический, для полностью статических фрагментов шаблона
  • v-once: ручной, для динамических выражений, которые не должны обновляться после первого рендера

Используйте static hoisting по умолчанию (автоматически). Используйте v-once только когда вы намеренно хотите поведение «рендер только при первом отображении».

Вопрос 3: когда прирост производительности заметен

В каких сценариях static hoisting наиболее эффективен?

Нажмите, чтобы увидеть ответ

Наиболее заметен, когда:

  1. Компонент содержит много статической разметки
  2. Компонент часто обновляется, но только малая часть динамическая
  3. Множество экземпляров имеют похожую статическую структуру

Чем выше соотношение статического к динамическому и частота обновлений, тем больше выигрыш.

5. Лучшие практики

Лучшие практики

Рекомендуется

<!-- 1) Позвольте компилятору автоматически выносить статическое содержимое -->
<template>
<div>
<h1>Заголовок</h1>
<p>Статическое содержимое</p>
<div>{{ dynamicContent }}</div>
</div>
</template>

<!-- 2) Используйте v-once только для намеренного однократного рендеринга -->
<template>
<div v-once>
<p>Создано: {{ createdAt }}</p>
<p>Автор: {{ creator }}</p>
</div>
</template>

<!-- 3) Разделяйте стабильную разметку и динамическую область, где возможно -->
<template>
<div class="container">
<header>Статический заголовок</header>
<main>{{ content }}</main>
</div>
</template>

Избегайте

<!-- 1) Не используйте v-once на содержимом, которое должно обновляться -->
<template>
<div v-once>
<p>{{ shouldUpdateContent }}</p>
</div>
</template>

<!-- 2) Не применяйте v-once бездумно к динамическим узлам списка -->
<template>
<div v-for="item in items" :key="item.id" v-once>
<p>{{ item.content }}</p>
</div>
</template>

6. Итоги для собеседования

Итоги для собеседования

Быстрое запоминание

Static hoisting:

  • автоматическая оптимизация на этапе компиляции
  • вынос статических узлов/констант
  • снижение стоимости создания VNode и diff

v-once:

  • контролируемое разработчиком поведение однократного рендера
  • может включать динамические выражения
  • после первого рендера обновления не происходят

Пример ответа

В: Что такое static hoisting в Vue 3?

Это оптимизация компилятора, при которой полностью статические узлы шаблона выносятся из функции render в константы. Они создаются один раз и переиспользуются при обновлениях, снижая накладные расходы на создание VNode и diff.

В: Чем он отличается от v-once?

Static hoisting автоматический и применяется к полностью статическому содержимому. v-once — ручной и может включать динамические выражения, но этот блок рендерится только один раз и после этого никогда не обновляется.

Справочные материалы