Skip to content

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

В VitePress каждый файл Markdown компилируется в HTML, а затем обрабатывается как Vue однофайловый компонент (Single-File Component, SFC). Это означает, что вы можете использовать любые возможности 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>

Вывод

123

<script> и <style>

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

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> предпочтительнее, когда необходим стиль с локальной областью видимости на странице.

У вас также есть доступ к 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
{{ Это будет отображаться как есть }}
:::

Вывод

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

Разблокировка в блоках кода

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

с -vue, например, js-vue:

Ввод

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

Вывод

js
Привет 2

Обратите внимание, что это может помешать правильной подсветке синтаксиса некоторых токенов.

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

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

shell
# .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>

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

На данный момент VitePress поддерживает SSG для teleport только в body. Для других целей вы можете обернуть их во встроенный компонент <ClientOnly> или внедрить разметку teleport в правильное место в вашем итоговом 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