логотип PurpleSchool
логотип PurpleSchool

Понимание и работа с компилятором Vue

Автор

Олег Марков

Введение

Если вы пишете интерфейсы на Vue, наверняка сталкивались с такими понятиями как шаблоны, рантайм, рендер-функции и SFC (Single-File Components). За всеми этими привычными механизмами находится важная “деталь под капотом” — компилятор Vue. Это ключевая часть, которая преобразует ваши шаблоны в исполняемый JavaScript-код, выполняемый уже в браузере.

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

Как устроен компилятор Vue

Компилятор Vue отвечает за преобразование шаблонов (обычно записанных в HTML-подобном синтаксисе) в рендер-функции на JavaScript. Этот процесс осуществляется на этапе сборки или «на лету» в браузере, если вы не собираете ваш проект заранее. Давайте взглянем на основные этапы работы компилятора.

Основные этапы компиляции

Процесс компиляции делится на три крупных этапа:

  1. Парсинг (Parsing)
    Ваш шаблон переводится в промежуточное AST (Abstract Syntax Tree) — древообразную структуру, которая описывает, из каких элементов состоит компонент.

  2. Трансформация (Transformation)
    AST модифицируется: добавляются обработчики событий, вычисляются директивы (v-for, v-if и т.д.), оптимизируются узлы.

  3. Генерация кода (Code Generation)
    На основе AST формируется JavaScript-функция, которая при запуске создает виртуальное DOM-дерево.

Посмотрите на схематичный разбор:

// Исходный шаблон
<div>{{ message }}</div>

// После компиляции — результат:
function render(ctx) {
  // ctx — прокси-компонента
  return h('div', null, ctx.message)
}

Как видите, ваша разметка превращается в вызов функции, которую интерпретирует рантайм Vue.

Online и Offline-компиляция

Компиляция шаблонов в Vue бывает двух типов:

  • Runtime Compilation (компиляция в рантайме)
    Шаблон компилируется прямо в браузере “на лету”. Такой подход менее производительный и увеличивает размер бандла, но полезен для сценариев, где шаблоны неизвестны заранее (например, CMS или конструкторах).

  • Pre-Compilation (предварительная компиляция)
    Шаблоны компилируются во время сборки (чаще всего через tools вроде vue-loader для webpack или vite-plugin-vue для Vite). В результате в браузер попадает только JavaScript с уже готовыми рендер-функциями.

Советую использовать pre-compilation для production-приложений. Это делает приложения быстрее и уменьшает их размер.

Single-File Components (SFC) и роль компилятора

Когда вы работаете с .vue файлами — Single-File Components — компилятор Vue принимает на себя задачу извлечения шаблона из блока <template>, его обработки и преобразования в рендер-функцию.

В связке с такими инструментами как vue-loader, процесс примерно таков:

  1. vue-loader выделяет блоки <template>, <script>, <style>;
  2. <template> отправляется в компилятор Vue (например, @vue/compiler-sfc);
  3. Результат — JS-код с функцией рендера, замещающий исходный шаблон.

Вот пример, чтобы вам было проще понять:

<template>
  <button @click="increment">{{ count }}</button>
</template>

<script>
export default {
  data() {
    return { count: 0 }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>

После компиляции template превращается примерно в:

function render(ctx, cache, props, setup, data, options) {
  // Функция создает элемент button и подключает событие click
  return h('button', { onClick: options.increment }, [ toDisplayString(data.count) ])
}

Настройка и использование компилятора Vue

Вы не работаете с компилятором напрямую при обычной разработке, потому что интеграция обычно настраивается в сборщиках (Vite, Webpack). Тем не менее, знания об этом этапе помогут вам глубже понимать, как работает ваш проект.

Пример настройки с Vite

Смотрите, как можно подключить и использовать компилятор Vue с Vite:

  1. Установите зависимости:
npm install vue@next @vitejs/plugin-vue
  1. Настройте Vite в vite.config.js:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
})

Компилятор будет использоваться автоматически для обработки .vue файлов.

Использование sfc компилятора напрямую

Иногда бывает полезно скомпилировать шаблон в рендер-функцию прямо в коде. Для этого используйте пакет @vue/compiler-dom или @vue/compiler-sfc.

import { compile } from '@vue/compiler-dom'

const template = '<p>{{ greeting }}, {{ name }}!</p>'
const { code } = compile(template)

console.log(code)
// Выведет JS-реализацию рендер-функции этого шаблона

Это бывает полезно для написания собственных инструментов, тестирования или изучения того, как Vue ведёт “под капотом”.

AST и плагины трансформации

AST (Abstract Syntax Tree) — центральная точка, где можно влиять на процесс компиляции. Можно писать плагины-трансформаторы, которые изменяют AST, например, для кастомных директив, оптимизации под специфику приложения, или автоматического внедрения определённых паттернов.

Почему важно знать про AST?

  • Позволяет проводить статический анализ шаблонов;
  • Помогает выявлять потенциальные ошибки ещё до запуска приложения;
  • Открывает путь к кастомизации и расширению функциональности Vue через плагины.

Пример анализа AST с помощью @vue/compiler-dom:

import { baseParse } from '@vue/compiler-dom'

// Базовый парсинг шаблона
const ast = baseParse('<div v-if="isVisible">Hello</div>')

console.log(ast)
// Покажет дерево разбора, где виден узел с v-if

Отличие Vue 2 и Vue 3 в компиляции

В Vue 2 процесс компиляции сильно отличался. Шаблон обычно компилировался на лету в браузере, а для сборки production был отдельный пакет без компилятора (vue.runtime.js). Рендер-функции были доступны, но работать с ними было чуть сложнее.

В Vue 3 произошёл значительный рефакторинг:

  • Компилятор вынесен в отдельные пакеты (@vue/compiler-dom, @vue/compiler-sfc);
  • Поддержка новых синтаксических конструкций (например, );
  • Больший упор на pre-compile и tree-shaking;
  • Улучшенная обработка типов (особенно если в проекте используется TypeScript).

Если вы начали с Vue 3 — принимайте как данность, что шаблоны чаще всего не компилируются на лету. Но если переходите с Vue 2, то стоит быть внимательнее к разнице между runtime-only и full build пакетами.

Runtime + Compiler vs. Runtime only

Vue поставляется в разных вариантах сборки:

  • Runtime + Compiler (vue.global.js, vue.esm-browser.js)
    Позволяет передавать шаблоны как строки в функцию createApp. Подходит для legacy-проектов или динамических шаблонов.

  • Runtime only (vue.runtime.global.js, vue.runtime.esm-browser.js)
    Более лёгкая сборка, не содержит компилятора, работает только с готовыми рендер-функциями. Идеал для production.

Вот как это выглядит на практике:

// Возможность передачи строки-шаблона (работает только с Runtime + Compiler)
const app = Vue.createApp({
  template: '<span>Hello, world</span>'
})

// Если у вас Runtime only, используйте render-функцию
const app2 = Vue.createApp({
  render() {
    return h('span', null, 'Hello, world')
  }
})

Если вы попробуете использовать строку-шаблон с runtime-only версией, получите ошибку типа "Failed to mount component: template or render function not defined".

Встраиваемые и пользовательские шаблонные компиляторы

Иногда разработчики хотят разработать собственный препроцессор шаблонов. Например, поддерживать JSX или Markdown вместо обычного HTML. Для этого можно воспользоваться открытым API компилятора SFC. Используйте @vue/compiler-sfc для парсинга, модификации, сборки или обработки блоков <template>.

Пример парсинга и изменения SFC:

import { parse, compileTemplate } from '@vue/compiler-sfc'

// Исходный компонент
const sfcContent = `
<template>
  <h1>{{ title }}</h1>
</template>
<script>
export default {
  data() {
    return { title: "Hi" }
  }
}
</script>
`

const parsed = parse(sfcContent)
const templateBlock = parsed.descriptor.template

// Компилируем шаблон
const renderResult = compileTemplate({
  source: templateBlock.content,
  filename: 'MyComponent.vue'
})

console.log(renderResult.code)
// Вы увидите готовую рендер-функцию

Это открывает широкие горизонты для инструментов статического анализа, линтинга или расширения фреймворка.

Диагностика и оптимизация производительности

Понимая, как шаблоны превращаются в рендер-функции, вы можете писать более эффективные компоненты.

Как можно влиять на оптимизацию:

  • Используйте pre-compile (см. выше);
  • Избегайте сложных вычислений прямо в шаблонах;
  • Используйте директиву v-once, если элемент или блок не изменяется;
  • Старайтесь вносить тяжелые вычисления на уровень computed;
  • Помните про ключи key в циклах — компилятор их оптимизирует;
  • Используйте инструменты анализа (Vue Devtools, Vite plugin inspect).

Влияние директив на компиляцию

Директивы вроде v-if, v-for, v-slot напрямую влияют на структуру AST. Например, вложенный v-for превращается в циклы на уровне рендер-функции.

Пример с v-for:

<ul>
  <li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>

Компиляция этого шаблона даст примерно такой результат на уровне рендер-функции:

function render(_ctx, _cache) {
  return h('ul', null, _ctx.items.map(item =>
    h('li', { key: item.id }, item.name)
  ))
}

Если не указать :key, компилятор выдаст предупреждение, потому что это нарушает эффективное сравнение виртуального DOM.

А как посмотреть, что сгенерировал компилятор?

С помощью командной строки — можно использовать пакет @vue/compiler-dom для генерации JS-функции из шаблона:

npx @vue/compiler-dom --code "<div>{{ msg }}</div>"

Или сделать то же самое в node.js, как я показывал выше.

Возможные ошибки и диагностика

Ошибки компиляции шаблонов — не редкость. Люди часто сталкиваются с:

  • Неправильным синтаксисом (например, забыли закрыть тег);
  • Использованием переменных не из контекста компонента;
  • Использованием неподдерживаемых HTML/JSX-конструкций;
  • Неуникальными ключами в циклах.

Внимательно читайте сообщения об ошибках в консоли сборщика — они часто подробно описывают причину.

Заключение

Понимание внутреннего устройства компилятора Vue и его роли в создании приложения помогает не только быстрее решать проблемы и оптимизировать проект, но и открывает возможности для продвинутой кастомизации, работы с новым синтаксисом или разработки собственных инструментов. Используя pre-compilation, правильно настраивая сборку и следя за структурой шаблонов, вы получаете максимальную производительность и гибкость. Не бойтесь экспериментировать с AST, использовать разные плагины и инструменты компилятора — экосистема Vue это отлично поддерживает.

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

1. Как проверить, какую версию сборки Vue использует мой проект — с компилятором или только runtime?

Ответ:
Проверьте способ импорта Vue в вашем bundler (например, в webpack или Vite). Если импортируется 'vue', то обычно используется версия с компилятором. Можно добавить в рантайме лог:
js console.log(Vue.compile) // Если функция определена, у вас версия с компилятором Кроме того, для Vite/webpack посмотрите в настройках alias, каким файлом замещается vue.


2. Что делать, если шаблон недоступен (например, поступает с сервера) и нужно компилировать его на лету?

Ответ:
Используйте версию Vue, включающую компилятор (runtime + compiler). Затем вызывайте Vue.compile для преобразования строки в функцию: js const render = Vue.compile('<span>{{ message }}</span>'); new Vue({ data: { message: 'Hi' }, render }).$mount('#app'); В Vue 3 используйте отдельный пакет @vue/runtime-dom и @vue/compiler-dom.


3. Как добавить поддержку нового синтаксиса или кастомных директив на этапе компиляции?

Ответ:
Вам нужен доступ к AST через @vue/compiler-dom или @vue/compiler-sfc. Используйте хуки трансформации. Подробно про AST API читайте в документации.


4. Почему появляется ошибка "Failed to mount component: template or render function not defined"?

Ответ:
Это происходит, если вы используете runtime-only версию Vue, но не скомпилировали шаблон в рендер-функцию. Проверьте, чтобы сборщик преобразовывал шаблоны в рендер-функции, либо подключайте версию с компилятором.


5. Как просмотреть сгенерированную рендер-функцию моего компонента?

Ответ:
Используйте @vue/compiler-dom или @vue/compiler-sfc для совмещения шаблона в JS-функцию: js import { compile } from '@vue/compiler-dom' const { code } = compile('<div>{{ msg }}</div>') console.log(code) Это полезно для изучения процесса или для отладки.

Стрелочка влевоПонимание и применение Composition API в Vue 3Когда и как использовать $emit и call во VueСтрелочка вправо

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Управление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueРабота с teleport для управления DOM во VueПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиРуководство по nextTick для работы с DOMСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний