[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), // динамический
]);
}
Преимущества
- Снижение стоимости создания VNode
- Меньше работы при diff
- Лучшая производительность рендеринга
- Автоматическая оптимизация (дополнительный код не требуется)
2. Как работает Static Hoisting
Как static hoisting работает внутри?
Процесс компиляции
- Обнаружение динамических привязок
{{ }},v-bind,v-if,v-for, динамические props и т.д.
- Пометка статических узлов
- Если узел и его дочерние элементы статические -> кандидат для выноса
- Вынос статических узлов
- Перемещение статических узлов/констант за пределы
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 Hoisting | v-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 — это оптимизация на этапе компиляции:
- Компилятор сканирует шаблон на предмет статических и динамических узлов
- Статические узлы выносятся за пределы
render()как константы - Фаза обновления переиспользует вынесенные узлы и пропускает их diff
Прирост производительности достигается за счёт:
- меньшего количества выделений VNode
- меньшего объёма обхода patch/diff
- меньшего количества повторных создания объектов
Вопрос 2: static hoisting vs v-once
Объясните различия и случаи использования.
Нажмите, чтобы увидеть ответ
- Static hoisting: автоматический, для полностью статических фрагментов шаблона
v-once: ручной, для динамических выражений, которые не должны обновляться после первого рендера
Используйте static hoisting по умолчанию (автоматически).
Используйте v-once только когда вы намеренно хотите поведение «рендер только при первом отображении».
Вопрос 3: когда прирост производительности заметен
В каких сценариях static hoisting наиболее эффективен?
Нажмите, чтобы увидеть ответ
Наиболее заметен, когда:
- Компонент содержит много статической разметки
- Компонент часто обновляется, но только малая часть динамическая
- Множество экземпляров имеют похожую статическую структуру
Чем выше соотношение статического к динамическому и частота обновлений, тем больше выигрыш.
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— ручной и может включать динамические выражения, но этот блок рендерится только один раз и после этого никогда не обновляется.