Skip to content

Использование Vue в Markdown

В VitePress каждый Markdown-файл компилируется в HTML, а затем обрабатывается как однофайловый компонент Vue. Это означает, что вы можете использовать любые возможности Vue внутри Markdown, включая динамический шаблонизатор, использование компонентов Vue или произвольную логику компонентов Vue на странице, добавив тег <script>.

Стоит отметить, что VitePress использует компилятор Vue для автоматического обнаружения и оптимизации чисто статических частей контента в формате Markdown. Статичное содержимое оптимизируется в отдельные узлы-заполнители и исключается из полезной нагрузки JavaScript страницы при первых посещениях. Они также пропускаются при гидратации на стороне клиента. Короче говоря, вы платите только за динамические части на каждой конкретной странице.

Совместимость с SSR

Всё, что используется в Vue, должно быть совместимо с SSR. Подробности и общие обходные пути см. в главе Совместимость с SSR.

Шаблонизация

Интерполяция

Каждый файл Markdown сначала компилируется в HTML, а затем передается в качестве компонента Vue в конвейер процесса Vite. Это означает, что вы можете использовать интерполяцию в стиле Vue в тексте:

Разметка

md
{{ 1 + 1 }}

Результат

2

Директивы

Директивы также работают (обратите внимание, что по замыслу необработанный HTML также допустим в Markdown):

Разметка

html
<span v-for="i in 3">{{ i }}</span>

Результат

1 2 3 

<script> и <style>

Теги корневого уровня <script> и <style> в Markdown-файлах работают так же, как и в Vue SFC, включая <script setup>, <style module> и т. д. Главное отличие заключается в отсутствии тега <template>: всё остальное содержимое корневого уровня — Markdown. Также обратите внимание, что все теги должны располагаться после метаданных:

html
---
hello: world
---

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

  const count = ref(0)
</script>

## Содержание в формате Markdown. Счётчик: {{ count }}

<button :class="$style.button" @click="count++">Увеличить</button>

<style module>
  .button {
    color: red;
    font-weight: bold;
  }
</style>

Избегайте <style scoped> в Markdown

При использовании в Markdown <style scoped> требует добавления специальных атрибутов к каждому элементу на текущей странице, что значительно увеличивает размер страницы. <style module> предпочтительнее, когда на странице требуется локально копируемый стиль.

У вас также есть доступ к runtime API VitePress, например, к хелперу useData, который предоставляет доступ к метаданным текущей страницы:

Разметка

html
<script setup>
  import { useData } from 'vitepress'

  const { page } = useData()
</script>

<pre>{{ page }}</pre>

Результат

json
{
  "path": "/using-vue.html",
  "title": "Использование Vue в Markdown",
  "frontmatter": {},
  ...
}

Использование компонентов

Вы можете импортировать и использовать компоненты Vue непосредственно в файлах Markdown.

Импорт в Markdown

Если компонент используется только на нескольких страницах, рекомендуется явно импортировать его туда, где он используется. Это позволяет правильно разделить их по коду и загружать только при показе соответствующих страниц:

md
<script setup>
import CustomComponent from '../components/CustomComponent.vue'
</script>

# Документация

Это .md с использованием пользовательского компонента

<CustomComponent />

## Другая документация

...

Глобальная регистрация компонентов

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

ВАЖНО

Убедитесь, что имя пользовательского компонента содержит дефис или написано в PascalCase. В противном случае он будет рассматриваться как встроенный элемент и заключен в тег <p>, что приведет к несоответствию гидратации, поскольку <p> не позволяет размещать внутри него блочные элементы.

Использование компонентов в заголовках

Вы можете использовать компоненты Vue в заголовках, но обратите внимание на разницу между следующими синтаксисами:

MarkdownИтоговый HTMLРазобранный заголовок
 # текст <Tag/> 
<h1>текст <Tag/></h1>текст
 # текст `<Tag/>` 
<h1>текст <code>&lt;Tag/&gt;</code></h1>текст <Tag/>

HTML, обёрнутый <code>, будет отображаться как есть; только тот HTML, который не обёрнут, будет разобран Vue.

ПРИМЕЧАНИЕ

Вывод HTML осуществляется с помощью Markdown-it, а парсинг заголовков обрабатывается VitePress (и используется как для боковой панели, так и для заголовка документа).

Экранирование

Вы можете избежать интерполяций Vue, обернув их в <span> или другие элементы с помощью директивы v-pre:

Разметка

md
Это <span v-pre>{{ будет отображаться как есть}}</span>.

Результат

Это {{ будет отображаться как есть}}

В качестве альтернативы вы можете обернуть весь абзац в пользовательский контейнер v-pre:

md
::: v-pre
{{ Это будет отображаться как есть }}
:::

Результат

{{ Это будет отображаться как есть }}

Unescape в блоках кода

По умолчанию все изолированные блоки кода автоматически оборачиваются v-pre, поэтому внутри них не будет обрабатываться синтаксис Vue. Чтобы включить интерполяцию в стиле Vue внутри фигурных скобок, вы можете добавить к языку суффикс -vue, например js-vue:

Разметка

md
```js-vue
Привет, {{ 1 + 1 }}
```

Результат

js
Привет, 2

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

Использование препроцессоров CSS

VitePress имеет встроенную поддержку для препроцессоров CSS: файлы .scss, .sass, .less, .styl и .stylus. Для них не нужно устанавливать специфические для Vite плагины, но сам соответствующий препроцессор должен быть установлен:

# .scss и .sass
npm install -D sass

# .less
npm install -D less

# .styl и .stylus
npm install -D stylus

Затем вы можете использовать следующее в Markdown и компонентах темы:

vue
<style lang="sass">
.title
  font-size: 20px
</style>

Использование телепортов

В настоящее время Vitepress поддерживает SSG только для телепортов к элементу body. Для других целей вы можете обернуть их внутри встроенного компонента <ClientOnly> или внедрить разметку телепортации в нужное место HTML конечной страницы через хук postRender.

Исходный код
vue
<script setup lang="ts">
import { ref } from 'vue'
const showModal = ref(false)
</script>

<template>
  <button class="modal-button" @click="showModal = true">
    Показать модальное окно
  </button>

  <Teleport to="body">
    <Transition name="modal">
      <div v-show="showModal" class="modal-mask">
        <div class="modal-container">
          <p>Привет из модального окна!</p>
          <div class="model-footer">
            <button class="modal-button" @click="showModal = false">
              Закрыть
            </button>
          </div>
        </div>
      </div>
    </Transition>
  </Teleport>
</template>

<style scoped>
.modal-mask {
  position: fixed;
  z-index: 200;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.3s ease;
}

.modal-container {
  width: 300px;
  margin: auto;
  padding: 20px 30px;
  background-color: var(--vp-c-bg);
  border-radius: 2px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
  transition: all 0.3s ease;
}

.model-footer {
  margin-top: 8px;
  text-align: right;
}

.modal-button {
  padding: 4px 8px;
  border-radius: 4px;
  border-color: var(--vp-button-alt-border);
  color: var(--vp-button-alt-text);
  background-color: var(--vp-button-alt-bg);
}

.modal-button:hover {
  border-color: var(--vp-button-alt-hover-border);
  color: var(--vp-button-alt-hover-text);
  background-color: var(--vp-button-alt-hover-bg);
}

.modal-enter-from,
.modal-leave-to {
  opacity: 0;
}

.modal-enter-from .modal-container,
.modal-leave-to .modal-container {
  transform: scale(1.1);
}
</style>
md
<ClientOnly>
  <Teleport to="#modal">
    <div>
      // ...
    </div>
  </Teleport>
</ClientOnly>

Опубликовано под лицензией MIT.