Merge branch 'main' into patch-1

pull/3603/head
Breno A 3 months ago committed by GitHub
commit 94ca447ff4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -30,7 +30,7 @@ jobs:
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v2
uses: pnpm/action-setup@v3
- name: Set node version to ${{ matrix.node_version }}
uses: actions/setup-node@v4

@ -1,3 +1,42 @@
# [1.1.0](https://github.com/vuejs/vitepress/compare/v1.0.2...v1.1.0) (2024-04-09)
### Bug Fixes
- **client:** hashchange should only be triggered for same page navigations ([#3768](https://github.com/vuejs/vitepress/issues/3768)) ([2a9fc2a](https://github.com/vuejs/vitepress/commit/2a9fc2a26b829bb3f28067ac6f4a41bc1e8b7a1e))
- **client:** emit correct `Event` instance in hashchange event
- **theme:** remove small layout shift on `On this page` button ([#3767](https://github.com/vuejs/vitepress/issues/3767)) ([5f28e74](https://github.com/vuejs/vitepress/commit/5f28e74abfc984cdc7e0d9d9f7b7e15cb2b46923))
### Features
- **client:** add `hash` property to `useData()`
- **theme:** update Inter to version 4 ([#3693](https://github.com/vuejs/vitepress/issues/3693)) ([#3694](https://github.com/vuejs/vitepress/issues/3694)) ([ffafa31](https://github.com/vuejs/vitepress/commit/ffafa31b9204f996f4b819684214fa631c224575))
## [1.0.2](https://github.com/vuejs/vitepress/compare/v1.0.1...v1.0.2) (2024-04-01)
### Bug Fixes
- **theme:** text containing html not showing properly in mobile nav menu ([3c8b4c7](https://github.com/vuejs/vitepress/commit/3c8b4c706051592dd2cca0ae57e293254cbb51ce))
## [1.0.1](https://github.com/vuejs/vitepress/compare/v1.0.0...v1.0.1) (2024-03-22)
### Bug Fixes
- **build:** vendor vue-demi to avoid resolution issues with yarn berry ([#3680](https://github.com/vuejs/vitepress/issues/3680)) ([5d3cb96](https://github.com/vuejs/vitepress/commit/5d3cb96ac364413aa9eb494bc91744bd8f4a2c79))
# [1.0.0](https://github.com/vuejs/vitepress/compare/v1.0.0-rc.45...v1.0.0) (2024-03-21)
### Bug Fixes
- **build:** resolve pattern relative to srcDir instead of root in createContentLoader ([#3638](https://github.com/vuejs/vitepress/issues/3638)) ([59183e9](https://github.com/vuejs/vitepress/commit/59183e9cef112a004c8a8e2b365478af657858b0))
- **localSearch:** remove empty titles that may appear in search results ([#3665](https://github.com/vuejs/vitepress/issues/3665)) ([f7aef3c](https://github.com/vuejs/vitepress/commit/f7aef3ca23dae39e226c85d7bb2579dbf7c758f3))
- **theme:** fixed sidebar expand caret showing when no children are present ([#3657](https://github.com/vuejs/vitepress/issues/3657)) ([e13f932](https://github.com/vuejs/vitepress/commit/e13f93292ce1a2b1d5ba161fddfe947a1824a2b0))
- **theme:** ignore inner-page items in next/prev link ([#3663](https://github.com/vuejs/vitepress/issues/3663)) ([b50a8a1](https://github.com/vuejs/vitepress/commit/b50a8a132577693817a15ab43fc4cc22670a8a65))
- **theme:** local nav separator not visible on pages having no outline ([1909041](https://github.com/vuejs/vitepress/commit/190904171500ad22998c8666080fd58c867a59b5))
### Features
- **theme:** allow selectively disabling external link icon on navbar items ([#3607](https://github.com/vuejs/vitepress/issues/3607)) ([5f6297c](https://github.com/vuejs/vitepress/commit/5f6297cb3df98926154235f31570e75820d4ea16))
# [1.0.0-rc.45](https://github.com/vuejs/vitepress/compare/v1.0.0-rc.44...v1.0.0-rc.45) (2024-03-06)
### Bug Fixes

@ -3,12 +3,15 @@ import { shared } from './shared'
import { en } from './en'
import { zh } from './zh'
import { pt } from './pt'
import { ru } from './ru'
export default defineConfig({
...shared,
locales: {
root: { label: 'English', ...en },
zh: { label: '简体中文', ...zh },
pt: { label: 'Português', ...pt }
pt: { label: 'Português', ...pt },
ru: { label: 'Русский', ...ru },
ko: { label: '한국어', lang: 'ko-KR', link: 'https://vitepress.vuejs.kr/' }
}
})

@ -0,0 +1,208 @@
import { createRequire } from 'module'
import { defineConfig, type DefaultTheme } from 'vitepress'
const require = createRequire(import.meta.url)
const pkg = require('vitepress/package.json')
export const ru = defineConfig({
lang: 'ru-RU',
description: 'Генератор статических сайтов на основе Vite и Vue.',
themeConfig: {
nav: nav(),
sidebar: {
'/ru/guide/': { base: '/ru/guide/', items: sidebarGuide() },
'/ru/reference/': { base: '/ru/reference/', items: sidebarReference() }
},
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
text: 'Редактировать страницу'
},
footer: {
message: 'Опубликовано под лицензией MIT.',
copyright: '© 2019 настоящее время, Эван Ю'
},
outline: { label: 'Содержание страницы' },
docFooter: {
prev: 'Предыдущая страница',
next: 'Следующая страница'
},
lastUpdated: {
text: 'Обновлено'
},
darkModeSwitchLabel: 'Оформление',
lightModeSwitchTitle: 'Переключить на светлую тему',
darkModeSwitchTitle: 'Переключить на тёмную тему',
sidebarMenuLabel: 'Меню',
returnToTopLabel: 'Вернуться к началу',
langMenuLabel: 'Изменить язык'
}
})
function nav(): DefaultTheme.NavItem[] {
return [
{
text: 'Руководство',
link: '/ru/guide/what-is-vitepress',
activeMatch: '/ru/guide/'
},
{
text: 'Справочник',
link: '/ru/reference/site-config',
activeMatch: '/ru/reference/'
},
{
text: pkg.version,
items: [
{
text: 'Изменения',
link: 'https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md'
},
{
text: 'Вклад',
link: 'https://github.com/vuejs/vitepress/blob/main/.github/contributing.md'
}
]
}
]
}
function sidebarGuide(): DefaultTheme.SidebarItem[] {
return [
{
text: 'Введение',
collapsed: false,
items: [
{ text: 'Что такое VitePress?', link: 'what-is-vitepress' },
{ text: 'Первые шаги', link: 'getting-started' },
{ text: 'Маршрутизация', link: 'routing' },
{ text: 'Развёртывание', link: 'deploy' }
]
},
{
text: 'Написание',
collapsed: false,
items: [
{ text: 'Расширения Markdown', link: 'markdown' },
{ text: 'Обработка ресурсов', link: 'asset-handling' },
{ text: 'Метаданные', link: 'frontmatter' },
{ text: 'Использование Vue в Markdown', link: 'using-vue' },
{ text: 'Интернационализация', link: 'i18n' }
]
},
{
text: 'Настройка',
collapsed: false,
items: [
{ text: 'Пользовательская тема', link: 'custom-theme' },
{
text: 'Расширение темы по умолчанию',
link: 'extending-default-theme'
},
{
text: 'Загрузка данных в режиме реального времени',
link: 'data-loading'
},
{ text: 'Совместимость с SSR', link: 'ssr-compat' },
{ text: 'Подключение к CMS', link: 'cms' }
]
},
{
text: 'Экспериментально',
collapsed: false,
items: [
{ text: 'Режим MPA', link: 'mpa-mode' },
{ text: 'Генерация карты сайта', link: 'sitemap-generation' }
]
},
{ text: 'Конфигурация и API', base: '/ru/reference/', link: 'site-config' }
]
}
function sidebarReference(): DefaultTheme.SidebarItem[] {
return [
{
text: 'Справочник',
items: [
{ text: 'Конфигурация сайта', link: 'site-config' },
{ text: 'Конфигурация метаданных', link: 'frontmatter-config' },
{ text: 'Runtime API', link: 'runtime-api' },
{ text: 'Командная строка', link: 'cli' },
{
text: 'Тема по умолчанию',
base: '/ru/reference/default-theme-',
items: [
{ text: 'Обзор', link: 'config' },
{ text: 'Навигация', link: 'nav' },
{ text: 'Сайдбар', link: 'sidebar' },
{ text: 'Главная страница', link: 'home-page' },
{ text: 'Подвал', link: 'footer' },
{ text: 'Макет', link: 'layout' },
{ text: 'Значки', link: 'badge' },
{ text: 'Страница команды', link: 'team-page' },
{
text: 'Предыдущая и следующая страницы',
link: 'prev-next-links'
},
{ text: 'Ссылка для редактирования', link: 'edit-link' },
{ text: 'Последнее обновление', link: 'last-updated' },
{ text: 'Поиск', link: 'search' },
{ text: 'Carbon Ads (реклама)', link: 'carbon-ads' }
]
}
]
}
]
}
export const search: DefaultTheme.AlgoliaSearchOptions['locales'] = {
ru: {
placeholder: 'Поиск в документации',
translations: {
button: {
buttonText: 'Поиск',
buttonAriaLabel: 'Поиск'
},
modal: {
searchBox: {
resetButtonTitle: 'Сбросить поиск',
resetButtonAriaLabel: 'Сбросить поиск',
cancelButtonText: 'Отменить поиск',
cancelButtonAriaLabel: 'Отменить поиск'
},
startScreen: {
recentSearchesTitle: 'История поиска',
noRecentSearchesText: 'Нет истории поиска',
saveRecentSearchButtonTitle: 'Сохранить в истории поиска',
removeRecentSearchButtonTitle: 'Удалить из истории поиска',
favoriteSearchesTitle: 'Избранное',
removeFavoriteSearchButtonTitle: 'Удалить из избранного'
},
errorScreen: {
titleText: 'Невозможно получить результаты',
helpText: 'Вам может потребоваться проверить подключение к Интернету'
},
footer: {
selectText: 'выбрать',
navigateText: 'перейти',
closeText: 'закрыть',
searchByText: 'поставщик поиска'
},
noResultsScreen: {
noResultsText: 'Нет результатов для',
suggestedQueryText: 'Вы можете попытаться узнать',
reportMissingResultsText:
'Считаете, что поиск даёт ложные результаты?',
reportMissingResultsLinkText: 'Нажмите на кнопку «Обратная связь»'
}
}
}
}
}

@ -1,6 +1,7 @@
import { defineConfig } from 'vitepress'
import { search as zhSearch } from './zh'
import { search as ptSearch } from './pt'
import { search as ruSearch } from './ru'
export const shared = defineConfig({
title: 'VitePress',
@ -55,7 +56,7 @@ export const shared = defineConfig({
appId: '8J64VVRP8K',
apiKey: 'a18e2f4cc5665f6602c5631fd868adfd',
indexName: 'vitepress',
locales: { ...zhSearch, ...ptSearch }
locales: { ...zhSearch, ...ptSearch, ...ruSearch }
}
},

@ -156,7 +156,7 @@ Don't enable options like _Auto Minify_ for HTML code. It will remove comments f
uses: actions/checkout@v4
with:
fetch-depth: 0 # Not needed if lastUpdated is not enabled
# - uses: pnpm/action-setup@v2 # Uncomment this if you're using pnpm
# - uses: pnpm/action-setup@v3 # Uncomment this if you're using pnpm
# - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun
- name: Setup Node
uses: actions/setup-node@v4
@ -287,3 +287,51 @@ Refer [Creating and Deploying a VitePress App To Edgio](https://docs.edg.io/guid
### Kinsta Static Site Hosting
You can deploy your Vitepress website on [Kinsta](https://kinsta.com/static-site-hosting/) by following these [instructions](https://kinsta.com/docs/vitepress-static-site-example/).
### Stormkit
You can deploy your VitePress project to [Stormkit](https://www.stormkit.io) by following these [instructions](https://stormkit.io/blog/how-to-deploy-vitepress).
### Nginx
Here is a example of an Nginx server block configuration. This setup includes gzip compression for common text-based assets, rules for serving your VitePress site's static files with proper caching headers as well as handling `cleanUrls: true`.
```nginx
server {
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
listen 80;
server_name _;
index index.html;
location / {
# content location
root /app;
# exact matches -> reverse clean urls -> folders -> not found
try_files $uri $uri.html $uri/ =404;
# non existent pages
error_page 404 /404.html;
# a folder without index.html raises 403 in this setup
error_page 403 /404.html;
# adjust caching headers
# files in the assets folder have hashes filenames
location ~* ^/assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
}
```
This configuration assumes that your built VitePress site is located in the `/app` directory on your server. Adjust the `root` directive accordingly if your site's files are located elsewhere.
::: warning Do not default to index.html
The try_files resolution must not default to index.html like in other Vue applications. This would result in an invalid page state.
:::
Further information can be found in the [official nginx documentation](https://nginx.org/en/docs/), in these issues [#2837](https://github.com/vuejs/vitepress/discussions/2837), [#3235](https://github.com/vuejs/vitepress/issues/3235) as well as in this [blog post](https://blog.mehdi.cc/articles/vitepress-cleanurls-on-nginx-environment#readings) by Mehdi Merah.

@ -71,8 +71,12 @@ $ npx vitepress init
$ pnpm vitepress init
```
```sh [yarn]
$ yarn vitepress init
```
```sh [bun]
$ bunx vitepress init
$ bun vitepress init
```
:::
@ -82,7 +86,7 @@ You will be greeted with a few simple questions:
<<< @/snippets/init.ansi
::: tip Vue as Peer Dependency
If you intend to perform customization that uses Vue components or APIs, you should also explicitly install `vue` as a peer dependency.
If you intend to perform customization that uses Vue components or APIs, you should also explicitly install `vue` as a dependency.
:::
## File Structure
@ -182,11 +186,15 @@ $ npx vitepress dev docs
```
```sh [pnpm]
$ pnpm exec vitepress dev docs
$ pnpm vitepress dev docs
```
```sh [yarn]
$ yarn vitepress dev docs
```
```sh [bun]
$ bunx vitepress dev docs
$ bun vitepress dev docs
```
:::
@ -199,7 +207,7 @@ The dev server should be running at `http://localhost:5173`. Visit the URL in yo
- To better understand how markdown files are mapped to generated HTML, proceed to the [Routing Guide](./routing).
- To discover more about what you can do on the page, such as writing markdown content or using Vue Component, refer to the "Writing" section of the guide. A great place to start would be to learn about [Markdown Extensions](./markdown).
- To discover more about what you can do on the page, such as writing markdown content or using Vue Components, refer to the "Writing" section of the guide. A great place to start would be to learn about [Markdown Extensions](./markdown).
- To explore the features provided by the default documentation theme, check out the [Default Theme Config Reference](../reference/default-theme-config).

@ -34,11 +34,11 @@ VitePress aims to provide a great Developer Experience (DX) when working with Ma
## Performance
Unlike many traditional SSGs, a website generated by VitePress is in fact a [Single Page Application](https://en.wikipedia.org/wiki/Single-page_application) (SPA).
Unlike many traditional SSGs where each navigation results in a full page reload, a website generated by VitePress serves static HTML on the initial visit, but becomes a [Single Page Application](https://en.wikipedia.org/wiki/Single-page_application) (SPA) for subsequent navigation within the site. This model, in our opinion, provides an optimal balance for performance:
- **Fast Initial Load**
The initial visit to any page will be served the static, pre-rendered HTML for blazing fast loading speed and optimal SEO. The page then loads a JavaScript bundle that turns the page into a Vue SPA ("hydration"). The hydration process is extremely fast: on [PageSpeed Insights](https://pagespeed.web.dev/report?url=https%3A%2F%2Fvitepress.dev%2F), typical VitePress sites achieve near-perfect performance scores even on low-end mobile devices with a slow network.
The initial visit to any page will be served the static, pre-rendered HTML for fast loading speed and optimal SEO. The page then loads a JavaScript bundle that turns the page into a Vue SPA ("hydration"). Contrary to common assumptions of SPA hydration being slow, this process is actually extremely fast thanks to Vue 3's raw performance and compiler optimizations. On [PageSpeed Insights](https://pagespeed.web.dev/report?url=https%3A%2F%2Fvitepress.dev%2F), typical VitePress sites achieve near-perfect performance scores even on low-end mobile devices with a slow network.
- **Fast Post-load Navigation**

@ -7,13 +7,16 @@ titleTemplate: Vite & Vue Powered Static Site Generator
hero:
name: VitePress
text: Vite & Vue Powered Static Site Generator
tagline: Simple, powerful, and fast. Meet the modern SSG framework you've always wanted.
tagline: Markdown to Beautiful Docs in Minutes
actions:
- theme: brand
text: Get Started
text: What is VitePress?
link: /guide/what-is-vitepress
- theme: alt
text: Quickstart
link: /guide/getting-started
- theme: alt
text: View on GitHub
text: GitHub
link: https://github.com/vuejs/vitepress
image:
src: /vitepress-logo-large.webp

@ -6,7 +6,7 @@
},
"files": [
{
"location": ".vitepress/config/{en,zh,pt}.ts",
"location": ".vitepress/config/{en,zh,pt,ru}.ts",
"pattern": ".vitepress/config/@lang.ts",
"type": "universal"
},
@ -28,6 +28,10 @@
{
"label": "Português",
"lang": "pt"
},
{
"label": "Русский",
"lang": "ru"
}
],
"outDir": ".vitepress/dist/_translations",

@ -3,14 +3,14 @@
"private": true,
"type": "module",
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build",
"preview": "vitepress preview",
"dev": "vitepress dev",
"lunaria:build": "lunaria build",
"lunaria:open": "open-cli .vitepress/dist/_translations/index.html"
"lunaria:open": "open-cli .vitepress/dist/_translations/index.html",
"preview": "vitepress preview"
},
"devDependencies": {
"@lunariajs/core": "^0.0.28",
"@lunariajs/core": "^0.0.32",
"markdown-it-mathjax3": "^4.3.2",
"open-cli": "^8.0.0",
"vitepress": "workspace:*"

@ -156,7 +156,7 @@ Não ative opções como _Auto Minify_ para código HTML. Isso removerá coment
uses: actions/checkout@v4
with:
fetch-depth: 0 # Não necessário se lastUpdated não estiver habilitado
# - uses: pnpm/action-setup@v2 # Descomente isso se estiver usando pnpm
# - uses: pnpm/action-setup@v3 # Descomente isso se estiver usando pnpm
# - uses: oven-sh/setup-bun@v1 # Descomente isso se estiver usando Bun
- name: Setup Node
uses: actions/setup-node@v4

@ -71,8 +71,12 @@ $ npx vitepress init
$ pnpm vitepress init
```
```sh [yarn]
$ yarn vitepress init
```
```sh [bun]
$ bunx vitepress init
$ bun vitepress init
```
:::
@ -182,11 +186,15 @@ $ npx vitepress dev docs
```
```sh [pnpm]
$ pnpm exec vitepress dev docs
$ pnpm vitepress dev docs
```
```sh [yarn]
$ yarn vitepress dev docs
```
```sh [bun]
$ bunx vitepress dev docs
$ bun vitepress dev docs
```
:::

@ -7,13 +7,16 @@ titleTemplate: Gerador de Site Estático desenvolvido com Vite & Vue
hero:
name: VitePress
text: Gerador de Site Estático Vite & Vue
tagline: Simples, poderoso e rápido. Conheça o moderno framework SSG que você sempre quis.
tagline: Markdown para Lindos Documentos em Minutos
actions:
- theme: brand
text: O que é VitePress?
link: /pt/guide/what-is-vitepress
- theme: alt
text: Iniciar
link: /pt/guide/getting-started
- theme: alt
text: Ver no GitHub
text: GitHub
link: https://github.com/vuejs/vitepress
image:
src: /vitepress-logo-large.webp

@ -135,7 +135,7 @@ export default {
export type Sidebar = SidebarItem[] | SidebarMulti
export interface SidebarMulti {
[path: string]: SidebarItem[]
[path: string]: SidebarItem[] | { items: SidebarItem[]; base: string }
}
export type SidebarItem = {
@ -162,6 +162,19 @@ export type SidebarItem = {
* If `false`, group is collapsible but expanded by default
*/
collapsed?: boolean
/**
* Base path for the children items.
*/
base?: string
/**
* Customize text that appears on the footer of previous/next page.
*/
docFooterText?: string
rel?: string
target?: string
}
```

@ -38,6 +38,10 @@ interface VitePressData<T = any> {
isDark: Ref<boolean>
dir: Ref<string>
localeIndex: Ref<string>
/**
* Current location hash
*/
hash: Ref<string>
}
interface PageData {

@ -471,6 +471,13 @@ export default {
}
```
### metaChunk <Badge type="warning" text="experimental" />
- Type: `boolean`
- Default: `false`
When set to `true`, extract pages metadata to a separate JavaScript chunk instead of inlining it in the initial HTML. This makes each page's HTML payload smaller and makes the pages metadata cacheable, thus reducing server bandwidth when you have many pages in the site.
### mpa <Badge type="warning" text="experimental" />
- Type: `boolean`

@ -0,0 +1,80 @@
<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>

@ -0,0 +1,63 @@
# Обработка ресурсов {#asset-handling}
## Ссылки на статические ресурсы {#referencing-static-assets}
Все файлы Markdown компилируются в компоненты Vue и обрабатываются [Vite](https://vitejs.dev/guide/assets.html). Вы можете, **и должны**, ссылаться на любые ресурсы, используя относительные URL:
```md
![Изображение](./image.png)
```
Вы можете ссылаться на статические ресурсы в ваших файлах разметки, компоненты `*.vue` в теме, стили и обычные файлы `.css`, используя абсолютные пути (основанные на корне проекта) или относительные пути (основанные на вашей файловой системе). Последнее похоже на поведение, к которому вы привыкли, если использовали Vite, Vue CLI или `file-loader` в webpack.
Распространенные типы файлов изображений, мультимедиа и шрифтов определяются и включаются в качестве ресурсов автоматически.
::: tip Связанные файлы не рассматриваются как ресурсы
PDF-файлы или другие документы, на которые есть ссылки в файлах с разметкой, не рассматриваются автоматически как ресурсы. Чтобы сделать связанные файлы доступными, вы должны вручную поместить их в каталог [`public`](#the-public-directory) вашего проекта.
:::
Все ссылающиеся ресурсы, включая те, которые используют абсолютные пути, будут скопированы в выходной каталог с хэшированным именем файла в производственной сборке. Ресурсы, на которые никогда не ссылались, не будут скопированы. Изображения размером менее 4 КБ будут вставляться в формате base64 — это можно настроить с помощью опции конфигурации [`vite`](../reference/site-config#vite).
Все **статические** ссылки на пути, включая абсолютные пути, должны быть основаны на структуре ваших рабочих каталогов.
## Директория `public` {#the-public-directory}
Иногда вам может понадобиться предоставить статические ресурсы, на которые нет прямых ссылок ни в одном из компонентов Markdown или темы, или вы можете захотеть предоставить определённые файлы с оригинальным именем. Примерами таких файлов являются `robots.txt`, `favicon.ico` и иконки PWA.
Вы можете поместить эти файлы в директорию `public` в [директории с исходными файлами](./routing#source-directory). Например, если корень вашего проекта — `./docs`, и вы используете стандартное расположение исходного каталога, то ваш публичный каталог будет `./docs/public`.
Ресурсы, размещённые в `public`, будут скопированы в корень выходного каталога как есть.
Обратите внимание, что вы должны ссылаться на файлы, размещённые в `public`, используя корневой абсолютный путь — например, `public/icon.png` всегда должен упоминаться в исходном коде как `/icon.png`.
## Базовый URL {#base-url}
Если ваш сайт развёрнут на URL-адресе, не являющемся корневым, вам нужно установить параметр `base` в файле `.vitepress/config.js`. Например, если вы планируете развернуть свой сайт на `https://foo.github.io/bar/`, то параметр `base` следует установить на `'/bar/'` (он всегда должен начинаться и заканчиваться слэшем).
Все пути к статическим ресурсам автоматически обрабатываются с учётом различных значений конфигурации `base`. Например, если в вашей разметке есть абсолютная ссылка на ресурс в директории `public`:
```md
![Изображение](/image-inside-public.png)
```
В этом случае вам **не** нужно обновлять его при изменении значения конфигурации `base`.
Однако если вы создаете компонент темы, который динамически ссылается на активы, например, изображение, атрибут `src` которого основан на значении конфигурации темы:
```vue
<img :src="theme.logoPath" />
```
В этом случае рекомендуется обернуть путь с помощью [хелпера `withBase`](../reference/runtime-api#withbase), предоставляемого VitePress:
```vue
<script setup>
import { withBase, useData } from 'vitepress'
const { theme } = useData()
</script>
<template>
<img :src="withBase(theme.logoPath)" />
</template>
```

@ -0,0 +1,58 @@
---
outline: deep
---
# Подключение к CMS {#connecting-to-a-cms}
## Общий рабочий процесс {#general-workflow}
Подключение VitePress к CMS в значительной степени зависит от [динамических маршрутов](./routing#dynamic-routes). Прежде чем приступить к работе, убедитесь, что вы понимаете, как это работает.
Поскольку каждая CMS работает по-своему, здесь мы можем предоставить лишь общую схему работы, которую вам нужно будет адаптировать под свой конкретный сценарий.
1. Если ваша CMS требует аутентификации, создайте файл `.env` для хранения токенов API и загрузите его таким образом:
```js
// posts/[id].paths.js
import { loadEnv } from 'vitepress'
const env = loadEnv('', process.cwd())
```
2. Получите необходимые данные из CMS и преобразуйте их в соответствующие пути:
```js
export default {
async paths() {
// при необходимости используйте соответствующую клиентскую библиотеку CMS
const data = await (
await fetch('https://my-cms-api', {
headers: {
// токен, если необходимо
}
})
).json()
return data.map((entry) => {
return {
params: { id: entry.id /* заголовок, автор, дата и т. д. */ },
content: entry.content
}
})
}
}
```
3. Отрисуйте содержимое страницы:
```md
# {{ $params.title }}
- Автор: {{ $params.author }}, {{ $params.date }}
<!-- @content -->
```
## Руководства по интеграции {#integration-guides}
Если вы написали руководство по интеграции VitePress с конкретной CMS, воспользуйтесь ссылкой «Редактировать эту страницу», чтобы добавить его сюда!

@ -0,0 +1,222 @@
# Пользовательская тема {#using-a-custom-theme}
## Разрешение темы {#theme-resolving}
Вы можете включить пользовательскую тему, создав файл `.vitepress/theme/index.js` или `.vitepress/theme/index.ts` («файл входа темы»):
```
.
├─ docs # корень проекта
│ ├─ .vitepress
│ │ ├─ theme
│ │ │ └─ index.js # файл входа темы
│ │ └─ config.js # файл конфигурации
│ └─ index.md
└─ package.json
```
VitePress всегда будет использовать пользовательскую тему вместо темы по умолчанию, если обнаружит наличие входного файла темы. Однако вы можете [расширить тему по умолчанию](./extending-default-theme), чтобы выполнить расширенные настройки поверх нее.
## Интерфейс темы {#theme-interface}
Пользовательская тема VitePress определяется как объект со следующим интерфейсом:
```ts
interface Theme {
/**
* Корневой компонент макета для каждой страницы
* @required
*/
Layout: Component
/**
* Улучшение экземпляра приложения Vue
* @optional
*/
enhanceApp?: (ctx: EnhanceAppContext) => Awaitable<void>
/**
* Расширяем другую тему, вызывая её `enhanceApp` перед нашей
* @optional
*/
extends?: Theme
}
interface EnhanceAppContext {
app: App // Экземпляр приложения Vue
router: Router // Экземпляр маршрутизатора VitePress
siteData: Ref<SiteData> // Метаданные на уровне сайта
}
```
Файл входа темы должен экспортировать тему по умолчанию:
```js
// .vitepress/theme/index.js
// Вы можете напрямую импортировать файлы Vue в файле входа темы.
// VitePress предварительно настроен с помощью @vitejs/plugin-vue.
import Layout from './Layout.vue'
export default {
Layout,
enhanceApp({ app, router, siteData }) {
// ...
}
}
```
Экспорт по умолчанию является единственным контрактом для пользовательской темы, и только свойство `Layout` является обязательным. Таким образом, технически тема VitePress может быть простой, как один компонент Vue.
Внутри компонент макета работает так же, как и обычное приложение Vite + Vue 3. Обратите внимание, что тема также должна быть [SSR-совместимой](./ssr-compat).
## Создание макета {#building-a-layout}
Самый базовый компонент макета должен содержать компонент [`<Content />`](../reference/runtime-api#content):
```vue
<!-- .vitepress/theme/Layout.vue -->
<template>
<h1>Пользовательский макет!</h1>
<!-- здесь будет отображаться содержимое в формате Markdown -->
<Content />
</template>
```
Приведённая выше схема просто отображает разметку каждой страницы в виде HTML. Добавим обработку 404 ошибки в качестве первого улучшения:
```vue{1-4,9-12}
<script setup>
import { useData } from 'vitepress'
const { page } = useData()
</script>
<template>
<h1>Пользовательский макет!</h1>
<div v-if="page.isNotFound">
Пользовательская страница 404!
</div>
<Content v-else />
</template>
```
Хелпер [`useData()`](../reference/runtime-api#usedata) предоставляет нам все данные во время выполнения, необходимые для условной отрисовки различных макетов. Среди различных данных, к которым мы можем получить доступ, являются метаданные текущей страницы. Мы можем использовать это, чтобы позволить конечному пользователю управлять макетом на каждой странице. Например, пользователь может указать, что страница должна использовать специальный макет главной страницы:
```md
---
layout: home
---
```
И мы можем настроить нашу тему, чтобы справиться с этим:
```vue{3,12-14}
<script setup>
import { useData } from 'vitepress'
const { page, frontmatter } = useData()
</script>
<template>
<h1>Пользовательский макет!</h1>
<div v-if="page.isNotFound">
Пользовательская страница 404!
</div>
<div v-if="frontmatter.layout === 'home'">
Пользовательская домашняя страница!
</div>
<Content v-else />
</template>
```
Конечно, вы можете разделить макет на большее количество компонентов:
```vue{3-5,12-15}
<script setup>
import { useData } from 'vitepress'
import NotFound from './NotFound.vue'
import Home from './Home.vue'
import Page from './Page.vue'
const { page, frontmatter } = useData()
</script>
<template>
<h1>Пользовательский макет!</h1>
<NotFound v-if="page.isNotFound" />
<Home v-if="frontmatter.layout === 'home'" />
<Page v-else /> <!-- <Page /> рендерит <Content /> -->
</template>
```
Обратитесь к [Справочнику Runtime API](../reference/runtime-api), чтобы узнать обо всём, что доступно в компонентах темы. Кроме того, вы можете использовать [загрузку данных в режиме реального времени](./data-loading) для создания макета, управляемого данными — например, страницы со списком всех записей в блоге текущего проекта.
## Распространение пользовательской темы {#distributing-a-custom-theme}
Самый простой способ распространить пользовательскую тему — предоставить её в виде [репозитория шаблонов на GitHub](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository).
Если вы хотите распространить тему в виде пакета npm, выполните следующие действия:
1. Экспортируйте объект темы в качестве экспорта по умолчанию в записи пакета.
2. Если есть возможность, экспортируйте определение типа конфигурации темы как `ThemeConfig`.
3. Если ваша тема требует настройки конфигурации VitePress, экспортируйте эту конфигурацию в подпапку пакета (например. `my-theme/config`), чтобы пользователь мог расширить её.
4. Документируйте параметры конфигурации темы (как через файл конфигурации, так и через метаданные).
5. Предоставьте чёткие инструкции по использованию вашей темы (см. ниже).
## Использование пользовательской темы {#consuming-a-custom-theme}
Чтобы использовать внешнюю тему, импортируйте и реэкспортируйте её из элемента пользовательской темы:
```js
// .vitepress/theme/index.js
import Theme from 'awesome-vitepress-theme'
export default Theme
```
Если тема требует расширения:
```js
// .vitepress/theme/index.js
import Theme from 'awesome-vitepress-theme'
export default {
extends: Theme,
enhanceApp(ctx) {
// ...
}
}
```
Если тема требует специальных настроек VitePress, вам нужно будет также расширить их в своем собственном конфиге:
```ts
// .vitepress/config.ts
import baseConfig from 'awesome-vitepress-theme/config'
export default {
// расширить базовый конфиг темы (если необходимо)
extends: baseConfig
}
```
Наконец, если тема предоставляет типы для своего конфига темы:
```ts
// .vitepress/config.ts
import baseConfig from 'awesome-vitepress-theme/config'
import { defineConfigWithTheme } from 'vitepress'
import type { ThemeConfig } from 'awesome-vitepress-theme'
export default defineConfigWithTheme<ThemeConfig>({
extends: baseConfig,
themeConfig: {
// Тип `ThemeConfig`
}
})
```

@ -0,0 +1,258 @@
# Загрузка данных в режиме реального времени {#build-time-data-loading}
VitePress предоставляет функцию **загрузчиков данных**, которая позволяет загружать произвольные данные и импортировать их со страниц или компонентов. Загрузка данных выполняется **только во время сборки**: Полученные данные будут сериализованы в виде JSON в финальной сборке JavaScript.
Загрузчики данных могут использоваться для получения удалённых данных или генерирования метаданных на основе локальных файлов. Например, вы можете использовать загрузчики данных для анализа всех локальных страниц API и автоматического создания индекса всех записей API.
## Пример использования {#basic-usage}
Файл загрузчика данных должен заканчиваться либо `.data.js`, либо `.data.ts`. Файл должен предоставлять экспорт объекта по умолчанию с помощью метода `load()`:
```js
// example.data.js
export default {
load() {
return {
hello: 'мир'
}
}
}
```
Модуль загрузчика выполняется только в Node.js, поэтому вы можете импортировать любые API Node и зависимости npm по мере необходимости.
Затем вы можете импортировать данные из этого файла в страницы `.md` и компоненты `.vue` с помощью экспорта с именем `data`:
```vue
<script setup>
import { data } from './example.data.js'
</script>
<pre>{{ data }}</pre>
```
Результат:
```json
{
"hello": "мир"
}
```
Вы заметите, что сам загрузчик данных не экспортирует `data`. Это VitePress вызывает метод `load()` за кулисами и неявно раскрывает результат через именованный экспорт `data`.
Это работает, даже если загрузчик асинхронный:
```js
export default {
async load() {
// получение удалённых данных
return (await fetch('...')).json()
}
}
```
## Данные из локальных файлов {#data-from-local-files}
Если вам нужно генерировать данные на основе локальных файлов, используйте опцию `watch` в загрузчике данных, чтобы изменения, внесённые в эти файлы, вызывали горячие обновления.
Опция `watch` удобна ещё и тем, что вы можете использовать [шаблоны glob](https://github.com/mrmlnc/fast-glob#pattern-syntax) для соответствия нескольким файлам. Шаблоны могут быть относительными к самому файлу загрузчика, а функция `load()` будет получать совпадающие файлы в виде абсолютных путей.
В следующем примере показана загрузка CSV-файлов и их преобразование в JSON с помощью [csv-parse](https://github.com/adaltas/node-csv/tree/master/packages/csv-parse/). Поскольку этот файл выполняется только во время сборки, вы не будете передавать CSV-парсер клиенту!
```js
import fs from 'node:fs'
import { parse } from 'csv-parse/sync'
export default {
watch: ['./data/*.csv'],
load(watchedFiles) {
// watchedFiles будет представлять собой массив абсолютных путей к найденным файлам.
// Формируем массив метаданных записи блога, которые можно использовать для визуализации списка в макете темы
return watchedFiles.map((file) => {
return parse(fs.readFileSync(file, 'utf-8'), {
columns: true,
skip_empty_lines: true
})
})
}
}
```
## `createContentLoader` {#createcontentloader}
При создании сайта, ориентированного на контент, нам часто приходится создавать страницы типа «архив», или «индекс», на которых мы перечисляем все доступные записи в нашей коллекции контента. Например, записи в блоге или страницы API. Мы **можем** реализовать это напрямую с помощью API загрузчика данных, но поскольку это очень распространённый случай использования, VitePress также предоставляет функцию `createContentLoader`, чтобы упростить эту задачу:
```js
// posts.data.js
import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/*.md' /* параметры */)
```
Эта функция принимает шаблон glob относительно [исходного каталога](./routing#source-directory) и возвращает объект `{ watch, load }` загрузчика данных, который может быть использован в качестве экспорта по умолчанию в файле загрузчика данных. В нем также реализовано кэширование на основе временных меток изменения файлов для повышения производительности dev.
Обратите внимание, что загрузчик работает только с файлами Markdown — совпадающие файлы, не относящиеся к Markdown, будут пропущены.
Загруженные данные будут представлять собой массив с типом `ContentData[]`:
```ts
interface ContentData {
// отображаемый URL-адрес страницы, например: /posts/hello.html (не включает `base`)
// выполните итерацию вручную или используйте пользовательскую `трансформацию` для нормализации путей
url: string
// метаданные страницы
frontmatter: Record<string, any>
// следующие параметры присутствуют только в том случае, если соответствующие опции включены
// мы рассмотрим их ниже
src: string | undefined
html: string | undefined
excerpt: string | undefined
}
```
По умолчанию указываются только `url` и `frontmatter`. Это связано с тем, что загруженные данные будут вложены в клиентский пакет в виде JSON, поэтому нам нужно быть осторожными с их размером. Вот пример использования этих данных для создания минимальной индексной страницы блога:
```vue
<script setup>
import { data as posts } from './posts.data.js'
</script>
<template>
<h1>Все записи блога</h1>
<ul>
<li v-for="post of posts">
<a :href="post.url">{{ post.frontmatter.title }}</a>
<span>by {{ post.frontmatter.author }}</span>
</li>
</ul>
</template>
```
### Параметры {#options}
Данные по умолчанию могут не соответствовать всем требованиям — вы можете изменить данные с помощью параметров:
```js
// posts.data.js
import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/*.md', {
includeSrc: true, // включить исходный текст в формате Markdown?
render: true, // включать в себя полный HTML страницы?
excerpt: true, // включить отрывок?
transform(rawData) {
// составляйте карты, сортируйте или фильтруйте исходные данные по своему усмотрению.
// конечный результат — это то, что будет отправлено клиенту.
return rawData
.sort((a, b) => {
return +new Date(b.frontmatter.date) - +new Date(a.frontmatter.date)
})
.map((page) => {
page.src // исходный текст в формате Markdown
page.html // отображение полной страницы HTML
page.excerpt // отображаемый отрывок HTML (содержимое выше первого `---`)
return {
/* ... */
}
})
}
})
```
Посмотрите, как он используется в [блоге Vue.js](https://github.com/vuejs/blog/blob/main/.vitepress/theme/posts.data.ts).
API `createContentLoader` также можно использовать внутри [хуков сборки](../reference/site-config#build-hooks):
```js
// .vitepress/config.js
export default {
async buildEnd() {
const posts = await createContentLoader('posts/*.md').load()
// генерируем файлы на основе метаданных сообщений, например, RSS-канал
}
}
```
**Types**
```ts
interface ContentOptions<T = ContentData[]> {
/**
* Включаем src?
* @default false
*/
includeSrc?: boolean
/**
* Преобразовываем src в HTML и включаем в данные?
* @default false
*/
render?: boolean
/**
* Если `boolean`, то следует ли разбирать и включать отрывок? (отображается как HTML)
*
* Если `function`, то управляйте тем, как отрывок извлекается из содержимого.
*
* Если `string`, определите пользовательский разделитель, который будет использоваться для извлечения
* отрывка. Разделителем по умолчанию является `---`, если `excerpt` имеет значение `true`.
*
* @see https://github.com/jonschlinkert/gray-matter#optionsexcerpt
* @see https://github.com/jonschlinkert/gray-matter#optionsexcerpt_separator
*
* @default false
*/
excerpt?:
| boolean
| ((
file: {
data: { [key: string]: any }
content: string
excerpt?: string
},
options?: any
) => void)
| string
/**
* Преобразуйте данные. Обратите внимание, что при импорте из компонентов или файлов
* с разметкой данные будут вложены в клиентский пакет в виде JSON.
*/
transform?: (data: ContentData[]) => T | Promise<T>
}
```
## Загрузчики типизированных данных {#typed-data-loaders}
При использовании TypeScript вы можете ввести свой загрузчик и экспортировать `data` следующим образом:
```ts
import { defineLoader } from 'vitepress'
export interface Data {
// тип данных
}
declare const data: Data
export { data }
export default defineLoader({
// тип проверенных опций загрузчика
watch: ['...'],
async load(): Promise<Data> {
// ...
}
})
```
## Конфигурация {#configuration}
Чтобы получить информацию о конфигурации внутри загрузчика, вы можете использовать код, подобный этому:
```ts
import type { SiteConfig } from 'vitepress'
const config: SiteConfig = (globalThis as any).VITEPRESS_CONFIG
```

@ -0,0 +1,293 @@
---
outline: deep
---
# Развёртывание вашего сайта VitePress {#deploy-your-vitepress-site}
Следующие руководства основаны на некоторых общих предположениях:
- Сайт VitePress находится в директории `docs` вашего проекта.
- Вы используете выходной каталог сборки по умолчанию (`.vitepress/dist`).
- VitePress установлен как локальная зависимость в вашем проекте, и вы установили следующие скрипты в вашем `package.json`:
```json
{
"scripts": {
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
}
}
```
## Создание и локальное тестирование {#build-and-test-locally}
1. Выполните эту команду, чтобы собрать документацию:
```sh
$ npm run docs:build
```
2. После сборки просмотрите её локально, запустив команду:
```sh
$ npm run docs:preview
```
Команда `preview` загрузит локальный статический веб-сервер, который будет обслуживать выходной каталог `.vitepress/dist` по адресу `http://localhost:4173`. Вы можете использовать это, чтобы убедиться, что всё выглядит хорошо, прежде чем отправлять в производство.
3. Вы можете настроить порт сервера, передав `--port` в качестве аргумента.
```json
{
"scripts": {
"docs:preview": "vitepress preview docs --port 8080"
}
}
```
Теперь метод `docs:preview` запустит сервер по адресу `http://localhost:8080`.
## Установка публичного базового пути {#setting-a-public-base-path}
По умолчанию предполагается, что сайт будет развёрнут по корневому пути домена (`/`). Если ваш сайт будет обслуживаться по подпути, например, `https://mywebsite.com/blog/`, то в конфигурации VitePress необходимо установить для опции [`base`](../reference/site-config#base) значение `'/blog/'`.
**Пример:** Если вы используете Github (или GitLab) Pages и развёртываете на `user.github.io/repo/`, то установите `base` на `/repo/`.
## Заголовки кэша HTTP {#http-cache-headers}
Если вы контролируете HTTP-заголовки на своем рабочем сервере, вы можете настроить заголовки `cache-control` для достижения лучшей производительности при повторных посещениях.
В производственной сборке используются хэшированные имена файлов для статических ресурсов (JavaScript, CSS и другие импортированные ресурсы, не находящиеся в `public`). Если вы просмотрите предварительную версию с помощью сетевой вкладки devtools вашего браузера, вы увидите файлы типа `app.4f283b18.js`.
Этот хэш `4f283b18` генерируется из содержимого этого файла. Один и тот же хэшированный URL гарантированно обслуживает одно и то же содержимое файла — если содержимое меняется, то и URL тоже. Это означает, что вы можете смело использовать самые сильные заголовки кэша для этих файлов. Все такие файлы будут помещены в каталог `assets/` в выходном каталоге, поэтому вы можете настроить для них следующий заголовок:
```
Cache-Control: max-age=31536000,immutable
```
::: details Пример файла Netlify `_headers`
```
/assets/*
cache-control: max-age=31536000
cache-control: immutable
```
Примечание: файл `_headers` должен быть помещён в [директорию `public`](./asset-handling#the-public-directory) — в нашем случае `docs/public/_headers` — так, чтобы он был скопирован в выходной каталог.
[Netlify custom headers documentation](https://docs.netlify.com/routing/headers/)
:::
::: details Пример конфигурации Vercel в файле `vercel.json`
```json
{
"headers": [
{
"source": "/assets/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "max-age=31536000, immutable"
}
]
}
]
}
```
Примечание: Файл `vercel.json` должен быть помещен в корень вашего **репозитория**.
[Документация Vercel по конфигурации заголовков](https://vercel.com/docs/concepts/projects/project-configuration#headers)
:::
## Руководства по платформам {#platform-guides}
### Netlify / Vercel / Cloudflare Pages / AWS Amplify / Render {#netlify-vercel-cloudflare-pages-aws-amplify-render}
Создайте новый проект и измените эти настройки с помощью панели управления:
- **Build Command:** `npm run docs:build`
- **Output Directory:** `docs/.vitepress/dist`
- **Node Version:** `18` (или выше)
::: warning ПРЕДУПРЕЖДЕНИЕ
Не включайте такие опции, как _Auto Minify_ для HTML-кода. Он удалит из вывода комментарии, которые имеют значение для Vue. При их удалении могут возникать ошибки несоответствия гидратации.
:::
### GitHub Pages {#github-pages}
1. Создайте файл с именем `deploy.yml` в директории `.github/workflows` вашего проекта с примерно таким содержанием:
```yaml
# Пример рабочего процесса для создания и развёртывания сайта VitePress на GitHub Pages
#
name: Deploy VitePress site to Pages
on:
# Выполняется при пушах, направленных в ветку `main`. Измените это значение на `master`, если вы
# используете ветку `master` в качестве ветки по умолчанию.
push:
branches: [main]
# Позволяет запустить этот рабочий процесс вручную на вкладке «Actions».
workflow_dispatch:
# Устанавливает разрешения GITHUB_TOKEN, чтобы разрешить развёртывание на страницах GitHub.
permissions:
contents: read
pages: write
id-token: write
# Разрешите только одно одновременное развёртывание, пропуская запуски, стоящие в очереди.
# Однако НЕ отменяйте текущие запуски, поскольку мы хотим дать возможность завершить производственные развёртывания.
concurrency:
group: pages
cancel-in-progress: false
jobs:
# Сборка
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Не требуется, если функция lastUpdated не включена
# - uses: pnpm/action-setup@v3 # Раскомментируйте, если вы используете pnpm
# - uses: oven-sh/setup-bun@v1 # Раскомментируйте, если вы используете Bun
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm # или pnpm / yarn
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Install dependencies
run: npm ci # или pnpm install / yarn install / bun install
- name: Build with VitePress
run: npm run docs:build # или pnpm docs:build / yarn docs:build / bun run docs:build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/.vitepress/dist
# Развёртывание
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
name: Deploy
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
```
::: warning ПРЕДУПРЕЖДЕНИЕ
Убедитесь, что опция `base` в вашем VitePress настроена правильно. Дополнительные сведения см. в секции [Установка публичного базового пути](#setting-a-public-base-path).
:::
2. В настройках вашего репозитория в разделе «Pages» выберите пункт меню «GitHub Actions» в секции «Build and deployment > Source».
3. Внесите свои изменения в ветку `main` и дождитесь завершения процесса GitHub Actions. Вы должны увидеть, что ваш сайт развёрнут по адресу `https://<username>.github.io/[repository]/` или `https://<custom-domain>/` в зависимости от ваших настроек. Ваш сайт будет автоматически разворачиваться при каждом внесении изменений в ветке `main`.
### GitLab Pages {#gitlab-pages}
1. Установите `outDir` в конфигурации VitePress на `../public`. Настройте опцию `base` на `'/<репозиторий>/'`, если вы хотите развернуть ваш проект по адресу на `https://<имя пользователя>.gitlab.io/<репозиторий>/`.
2. Создайте файл с именем `.gitlab-ci.yml` в корне вашего проекта с приведённым ниже содержимым. Это позволит создавать и развёртывать ваш сайт каждый раз, когда вы вносите изменения в его содержимое:
```yaml
image: node:18
pages:
cache:
paths:
- node_modules/
script:
# - apk add git # Отметьте это, если вы используете небольшие докер-образы, такие как alpine, и у вас включен lastUpdated
- npm install
- npm run docs:build
artifacts:
paths:
- public
only:
- main
```
### Статические веб-приложения Azure {#azure-static-web-apps}
1. Следуйте [официальной документации](https://docs.microsoft.com/ru-ru/azure/static-web-apps/build-configuration).
2. Установите эти значения в вашем конфигурационном файле (и удалите те, которые вам не нужны, например, `api_location`):
- **`app_location`**: `/`
- **`output_location`**: `docs/.vitepress/dist`
- **`app_build_command`**: `npm run docs:build`
### Firebase {#firebase}
1. Создайте `firebase.json` и `.firebaserc` в корне вашего проекта:
`firebase.json`:
```json
{
"hosting": {
"public": "docs/.vitepress/dist",
"ignore": []
}
}
```
`.firebaserc`:
```json
{
"projects": {
"default": "<YOUR_FIREBASE_ID>"
}
}
```
2. После запуска `npm run docs:build` выполните эту команду для развёртывания:
```sh
firebase deploy
```
### Surge {#surge}
1. После запуска `npm run docs:build` выполните эту команду для развёртывания:
```sh
npx surge docs/.vitepress/dist
```
### Heroku {#heroku}
1. Следуйте документации и руководству, приведённому в [`heroku-buildpack-static`](https://elements.heroku.com/buildpacks/heroku/heroku-buildpack-static).
2. Создайте файл `static.json` в корне вашего проекта со следующим содержимым:
```json
{
"root": "docs/.vitepress/dist"
}
```
### Edgio {#edgio}
См. [Создание и развёртывание приложения VitePress в Edgio](https://docs.edg.io/applications/v6/sites_frameworks/getting_started/vitepress).
### Хостинг статических файлов Kinsta {#kinsta-static-site-hosting}
Вы можете развернуть свой сайт Vitepress на [Kinsta](https://kinsta.com/static-site-hosting/), следуя этим [инструкциям](https://kinsta.com/docs/vitepress-static-site-example/).
### Stormkit
Вы можете развернуть свой проект VitePress на [Stormkit](https://www.stormkit.io), следуя следующим [инструкциям](https://stormkit.io/blog/how-to-deploy-vitepress).

@ -0,0 +1,342 @@
---
outline: deep
---
# Расширение темы по умолчанию {#extending-the-default-theme}
Тема VitePress по умолчанию оптимизирована для документации и может быть настроена по своему усмотрению. Полный список опций можно найти в главе [Настройки темы по умолчанию](../reference/default-theme-config).
Однако есть ряд случаев, когда одной лишь конфигурации будет недостаточно. Например:
1. Вам нужно изменить стили CSS;
2. Вам нужно изменить экземпляр приложения Vue, например, чтобы зарегистрировать глобальные компоненты;
3. Вам нужно внедрить пользовательский контент в тему через слоты макета.
Эти расширенные настройки потребуют использования пользовательской темы, которая «расширяет» тема по умолчанию.
::: tip СОВЕТ
Прежде чем приступить к работе, обязательно прочитайте главу [Пользовательская тема](./custom-theme), чтобы понять, как работают пользовательские темы.
:::
## Настройка CSS {#customizing-css}
CSS темы по умолчанию можно настроить, переопределив переменные CSS корневого уровня:
```js
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import './custom.css'
export default DefaultTheme
```
```css
/* .vitepress/theme/custom.css */
:root {
--vp-c-brand-1: #646cff;
--vp-c-brand-2: #747bff;
}
```
См. [переменные CSS темы по умолчанию](https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css), которые можно переопределить.
## Использование различных шрифтов {#using-different-fonts}
VitePress использует [Inter](https://rsms.me/inter/) в качестве шрифта по умолчанию, и будет включать шрифты в вывод сборки. Шрифт также автоматически загружается в производство. Однако это может быть нежелательно, если вы хотите использовать другой основной шрифт.
Чтобы не включать Inter в вывод сборки, импортируйте тему из `vitepress/theme-without-fonts`:
```js
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme-without-fonts'
import './my-fonts.css'
export default DefaultTheme
```
```css
/* .vitepress/theme/custom.css */
:root {
--vp-font-family-base: /* normal text font */ --vp-font-family-mono:
/* code font */;
}
```
::: warning ПРЕДУПРЕЖДЕНИЕ
Если вы используете дополнительные компоненты, такие как [Страница команды](../reference/default-theme-team-page), убедитесь, что они также импортированы из `vitepress/theme-without-fonts`!
:::
Если ваш шрифт — это локальный файл, на который ссылаются через `@font-face`, он будет обработан как ресурс и включён в каталог `.vitepress/dist/assets` с хэшированным именем файла. Чтобы предварительно загрузить этот файл, используйте хук сборки [transformHead](../reference/site-config#transformhead):
```js
// .vitepress/config.js
export default {
transformHead({ assets }) {
// настраиваем regex соответствующим образом, чтобы он соответствовал вашему шрифту
const myFontFile = assets.find((file) => /font-name\.\w+\.woff2/)
if (myFontFile) {
return [
[
'link',
{
rel: 'preload',
href: myFontFile,
as: 'font',
type: 'font/woff2',
crossorigin: ''
}
]
]
}
}
}
```
## Регистрация глобальных компонентов {#registering-global-components}
```js
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
/** @type {import('vitepress').Theme} */
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
// регистрируем пользовательские глобальные компоненты
app.component('MyGlobalComponent' /* ... */)
}
}
```
Если вы используете TypeScript:
```ts
// .vitepress/theme/index.ts
import type { Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
// регистрируем пользовательские глобальные компоненты
app.component('MyGlobalComponent' /* ... */)
}
} satisfies Theme
```
Поскольку мы используем Vite, вы также можете использовать [глобальную функцию импорта](https://vitejs.dev/guide/features.html#glob-import) Vite для автоматической регистрации каталога компонентов.
## Слоты макета {#layout-slots}
Компонент `<Layout/>` темы по умолчанию имеет несколько слотов, которые можно использовать для вставки содержимого в определённые места страницы. Вот пример внедрения компонента в структуру before:
```js
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import MyLayout from './MyLayout.vue'
export default {
extends: DefaultTheme,
// переопределяем макет с помощью компонента-обёртки, который
// вводит слоты
Layout: MyLayout
}
```
```vue
<!--.vitepress/theme/MyLayout.vue-->
<script setup>
import DefaultTheme from 'vitepress/theme'
const { Layout } = DefaultTheme
</script>
<template>
<Layout>
<template #aside-outline-before>
Верхнее содержимое моей пользовательской боковой панели
</template>
</Layout>
</template>
```
Также можно использовать функцию рендеринга.
```js
// .vitepress/theme/index.js
import { h } from 'vue'
import DefaultTheme from 'vitepress/theme'
import MyComponent from './MyComponent.vue'
export default {
extends: DefaultTheme,
Layout() {
return h(DefaultTheme.Layout, null, {
'aside-outline-before': () => h(MyComponent)
})
}
}
```
Полный список слотов, доступных в макете темы по умолчанию:
- Когда `layout: 'doc'` (по умолчанию) включен через метаданные:
- `doc-top`
- `doc-bottom`
- `doc-footer-before`
- `doc-before`
- `doc-after`
- `sidebar-nav-before`
- `sidebar-nav-after`
- `aside-top`
- `aside-bottom`
- `aside-outline-before`
- `aside-outline-after`
- `aside-ads-before`
- `aside-ads-after`
- Когда `layout: 'home'` включен через метаданные:
- `home-hero-before`
- `home-hero-info-before`
- `home-hero-info`
- `home-hero-info-after`
- `home-hero-actions-after`
- `home-hero-image`
- `home-hero-after`
- `home-features-before`
- `home-features-after`
- Когда `layout: 'page'` включен через метаданные:
- `page-top`
- `page-bottom`
- На странице «Не найдено (404)»:
- `not-found`
- Всегда:
- `layout-top`
- `layout-bottom`
- `nav-bar-title-before`
- `nav-bar-title-after`
- `nav-bar-content-before`
- `nav-bar-content-after`
- `nav-screen-content-before`
- `nav-screen-content-after`
## Использование View Transitions API {#using-view-transitions-api}
### Переключение внешнего вида {#on-appearance-toggle}
Вы можете расширить стандартную тему, чтобы обеспечить пользовательский переход при переключении цветового режима. Пример:
```vue
<!-- .vitepress/theme/Layout.vue -->
<script setup lang="ts">
import { useData } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import { nextTick, provide } from 'vue'
const { isDark } = useData()
const enableTransitions = () =>
'startViewTransition' in document &&
window.matchMedia('(prefers-reduced-motion: no-preference)').matches
provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
if (!enableTransitions()) {
isDark.value = !isDark.value
return
}
const clipPath = [
`circle(0px at ${x}px ${y}px)`,
`circle(${Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
)}px at ${x}px ${y}px)`
]
await document.startViewTransition(async () => {
isDark.value = !isDark.value
await nextTick()
}).ready
document.documentElement.animate(
{ clipPath: isDark.value ? clipPath.reverse() : clipPath },
{
duration: 300,
easing: 'ease-in',
pseudoElement: `::view-transition-${isDark.value ? 'old' : 'new'}(root)`
}
)
})
</script>
<template>
<DefaultTheme.Layout />
</template>
<style>
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
}
::view-transition-old(root),
.dark::view-transition-new(root) {
z-index: 1;
}
::view-transition-new(root),
.dark::view-transition-old(root) {
z-index: 9999;
}
.VPSwitchAppearance {
width: 22px !important;
}
.VPSwitchAppearance .check {
transform: none !important;
}
</style>
```
Результат (**предупреждение!**: мигающие цвета, резкие движения, яркий свет):
<details>
<summary>Демонстрация</summary>
![Демонстрация переходов](/appearance-toggle-transition.webp)
</details>
Более подробно о переходах между представлениями читайте в [документации Chrome](https://developer.chrome.com/docs/web-platform/view-transitions/).
### Изменение маршрута {#on-route-change}
Скоро будет.
## Переопределение внутренних компонентов {#overriding-internal-components}
Вы можете использовать [псевдонимы](https://vitejs.dev/config/shared-options.html#resolve-alias) Vite, чтобы заменить стандартные компоненты темы на свои собственные:
```ts
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vitepress'
export default defineConfig({
vite: {
resolve: {
alias: [
{
find: /^.*\/VPNavBar\.vue$/,
replacement: fileURLToPath(
new URL('./components/CustomNavBar.vue', import.meta.url)
)
}
]
}
}
})
```
Чтобы узнать точное название компонента, обратитесь к [нашему исходному коду](https://github.com/vuejs/vitepress/tree/main/src/client/theme-default/components). Поскольку компоненты являются внутренними, есть небольшая вероятность того, что их название будет обновлено между минорными выпусками.

@ -0,0 +1,48 @@
# Метаданные {#frontmatter}
## Использование {#usage}
VitePress поддерживает метаданные YAML во всех Markdown-файлах, разбирая их с помощью [gray-matter](https://github.com/jonschlinkert/gray-matter). Метаданные должны находиться в верхней части Markdown-файла (перед любыми элементами, включая теги `<script>`) и иметь вид корректного YAML, заданного между тройными пунктирными линиями. Пример:
```md
---
title: Документация с VitePress
editLink: true
---
```
Многие параметры конфигурации сайта или темы по умолчанию имеют соответствующие опции в блоке метаданных. Вы можете использовать метаданные, чтобы переопределить определённое поведение только для текущей страницы. Подробности см. в [Справочнике по настройке метаданных](../reference/frontmatter-config).
Вы также можете определить собственные метаданные, которые будут использоваться в динамических выражениях Vue на странице.
## Доступ к метаданным {#accessing-frontmatter-data}
Доступ к метаданным можно получить через специальную глобальную переменную `$frontmatter`:
Вот пример того, как можно использовать его в файле Markdown:
```md
---
title: Документация с VitePress
editLink: true
---
# {{ $frontmatter.title }}
Содержание руководства
```
Вы также можете получить доступ к метаданным текущей страницы в `<script setup>` с помощью [хелпера `useData()`](../reference/runtime-api#usedata).
## Альтернативные форматы метаданных {#alternative-frontmatter-formats}
VitePress также поддерживает синтаксис метаданных JSON, начинающийся и заканчивающийся фигурными скобками:
```json
---
{
"title": "Веду блог как хакер",
"editLink": true
}
---
```

@ -0,0 +1,216 @@
# Первые шаги {#getting-started}
## Попробуйте онлайн {#try-it-online}
Вы можете попробовать VitePress прямо в браузере на [StackBlitz](https://vitepress.new).
## Установка {#installation}
### Требования {#prerequisites}
- [Node.js](https://nodejs.org/) версии 18 или выше.
- Терминал для доступа к VitePress через интерфейс командной строки (CLI).
- Текстовый редактор с поддержкой синтаксиса [Markdown](https://ru.wikipedia.org/wiki/Markdown).
- Рекомендуется использовать [VSCode](https://code.visualstudio.com/), а также [официальное расширение Vue](https://marketplace.visualstudio.com/items?itemName=Vue.volar).
VitePress можно использовать самостоятельно или установить в существующий проект. В обоих случаях вы можете установить его с помощью:
::: code-group
```sh [npm]
$ npm add -D vitepress
```
```sh [pnpm]
$ pnpm add -D vitepress
```
```sh [yarn]
$ yarn add -D vitepress
```
```sh [bun]
$ bun add -D vitepress
```
:::
::: details Получаете предупреждения об отсутствующих зависимостях?
Если вы используете PNPM, вы заметите предупреждение об отсутствующем пакете `@docsearch/js`. Это не мешает работе VitePress. Если вы хотите подавить это предупреждение, добавьте следующее в ваш `package.json`:
```json
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"@algolia/client-search",
"search-insights"
]
}
}
```
:::
::: tip ПРИМЕЧАНИЕ
VitePress — это пакет, предназначенный только для ESM. Не используйте `require()` для импорта, и убедитесь, что ближайший `package.json` содержит `"type": "module"`, или измените расширение соответствующих файлов, например, `.vitepress/config.js` на `.mjs`/`.mts`. Более подробную информацию см. в [Руководстве по устранению неполадок Vite](https://vitejs.dev/guide/troubleshooting.html#this-package-is-esm-only). Кроме того, внутри асинхронных контекстов CJS можно использовать `await import('vitepress')` вместо этого.
:::
### Мастер настройки {#setup-wizard}
VitePress поставляется с мастером настройки командной строки, который поможет вам создать базовый проект. После установки запустите мастер, запустив его:
::: code-group
```sh [npm]
$ npx vitepress init
```
```sh [pnpm]
$ pnpm vitepress init
```
```sh [yarn]
$ yarn vitepress init
```
```sh [bun]
$ bun vitepress init
```
:::
Вас встретят несколькими простыми вопросами:
<<< @/snippets/init.ansi
::: tip Vue как зависимость
Если вы собираетесь выполнять кастомизацию с использованием компонентов или API Vue, вам также следует явно установить `vue` в качестве зависимости.
:::
## Структура файлов {#file-structure}
Если вы создаете отдельный сайт VitePress, вы можете разместить его в текущей директории (`./`). Однако если вы устанавливаете VitePress в существующий проект вместе с другим исходным кодом, рекомендуется поместить сайт во вложенную директорию (например, `./docs`), чтобы он был отделён от остальной части проекта.
Если предположить, что вы решили разместить проект VitePress в `./docs`, то сгенерированная структура файлов должна выглядеть следующим образом:
```
.
├─ docs
│ ├─ .vitepress
│ │ └─ config.js
│ ├─ api-examples.md
│ ├─ markdown-examples.md
│ └─ index.md
└─ package.json
```
Директория `docs` считается **корнем проекта** сайта VitePress. Директория `.vitepress` — это зарезервированное место для конфигурационного файла VitePress, кэша dev-сервера, результатов сборки и дополнительного кода настройки темы.
::: tip СОВЕТ
По умолчанию VitePress хранит кэш своего dev-сервера в файле `.vitepress/cache`, а выходные данные сборки — в файле `.vitepress/dist`. Если вы используете Git, вам следует добавить их в файл `.gitignore`. Эти места также могут быть [сконфигурированы](../reference/site-config#outdir).
:::
### Конфигурационный файл {#the-config-file}
Файл конфигурации (`.vitepress/config.js`) позволяет настраивать различные аспекты сайта VitePress, самыми основными из которых являются название и описание сайта:
```js
// .vitepress/config.js
export default {
// параметры сайта
title: 'Заголовок сайта',
description: 'Описание сайта.',
themeConfig: {
// параметры темы
}
}
```
Вы также можете настроить поведение темы с помощью опции `themeConfig`. Загляните в главу [Настройка сайта](../reference/site-config) для получения подробной информации обо всех параметрах конфигурации.
### Исходные файлы {#source-files}
Файлы Markdown за пределами директории `.vitepress` считаются **исходными файлами**.
VitePress использует **маршрутизацию на основе файлов**: Каждый файл `.md` компилируется в соответствующий файл `.html` с тем же путем. Например, `index.md` будет скомпилирован в `index.html`, и его можно будет посетить по корневому пути `/` результирующего сайта VitePress.
VitePress также предоставляет возможность генерировать чистые URL-адреса, переписывать пути и динамически генерировать страницы. Всё это будет рассмотрено в [Руководстве по маршрутизации](./routing).
## Скрипты запуска {#up-and-running}
Мастер настройки также должен был внедрить следующие скрипты npm в ваш `package.json`, если вы разрешили ему это сделать в процессе установки:
```json
{
...
"scripts": {
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
},
...
}
```
Скрипт `docs:dev` запустит локальный dev-сервер с мгновенными горячими обновлениями. Запустите его с помощью следующей команды:
::: code-group
```sh [npm]
$ npm run docs:dev
```
```sh [pnpm]
$ pnpm run docs:dev
```
```sh [yarn]
$ yarn docs:dev
```
```sh [bun]
$ bun run docs:dev
```
:::
Вместо npm-скриптов вы также можете вызывать VitePress напрямую с помощью:
::: code-group
```sh [npm]
$ npx vitepress dev docs
```
```sh [pnpm]
$ pnpm vitepress dev docs
```
```sh [yarn]
$ yarn vitepress dev docs
```
```sh [bun]
$ bun vitepress dev docs
```
:::
Более подробная информация об использовании командной строки описана в главе [Командная строка](../reference/cli).
Dev-сервер должен быть запущен по адресу `http://localhost:5173`. Перейдите по URL-адресу в браузере, чтобы увидеть новый сайт в действии!
## Что дальше? {#what-s-next}
- Чтобы лучше понять, как Markdown-файлы сопоставляются с генерируемым HTML, перейдите к [Руководству по маршрутизации](./routing).
- Чтобы узнать больше о том, что вы можете делать на странице, например, писать содержимое в формате Markdown или использовать компоненты Vue, обратитесь к разделу «Написание». Начать стоит с изучения главы [Расширения Markdown](./markdown).
- Чтобы изучить возможности, предоставляемые темой документации по умолчанию, ознакомьтесь с главой [Настройка темы по умолчанию](../reference/default-theme-config).
- Если вы хотите ещё больше изменить внешний вид своего сайта, изучите главы [Расширение темы по умолчанию](./extending-default-theme) или [Пользовательская тема](./custom-theme).
- Как только ваш сайт документации обретёт форму, обязательно прочитайте [Руководство по развёртыванию](./deploy).

@ -0,0 +1,113 @@
# Интернационализация {#internationalization}
Чтобы использовать встроенные функции i18n, необходимо создать следующую структуру каталогов:
```
docs/
├─ es/
│ ├─ foo.md
├─ ru/
│ ├─ foo.md
├─ foo.md
```
Затем в `docs/.vitepress/config.ts`:
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
// общие свойства и другие вещи верхнего уровня...
locales: {
root: {
label: 'English',
lang: 'en'
},
ru: {
label: 'Русский',
lang: 'ru', // необязательный, будет добавлен как атрибут `lang` в тег `html`
link: '/ru/guide' // по умолчанию /ru/ -- отображается в меню переводов на панели навигации, может быть внешним
// другие свойства, специфичные для локали...
}
}
})
```
Следующие свойства могут быть переопределены для каждой локали (включая корневую):
```ts
interface LocaleSpecificConfig<ThemeConfig = any> {
lang?: string
dir?: string
title?: string
titleTemplate?: string | boolean
description?: string
head?: HeadConfig[] // будут объединены с существующими записями в заголовке, дублирующие метатеги будут автоматически удалены
themeConfig?: ThemeConfig // будут неглубоко объединены, общие вещи можно поместить в запись themeConfig верхнего уровня
}
```
Подробнее о настройке текстов-заготовок темы по умолчанию см. в интерфейсе [`DefaultTheme.Config`](https://github.com/vuejs/vitepress/blob/main/types/default-theme.d.ts). Не переопределяйте `themeConfig.algolia` или `themeConfig.carbonAds` на локальном уровне. Использование многоязычного поиска описано в главе [Поиск Algolia](../reference/default-theme-search#algolia-search-i18n).
**Совет:** Конфигурационный файл можно хранить и в `docs/.vitepress/config/index.ts`. Это может помочь вам организовать работу, создав конфигурационный файл для каждой локали, а затем объединить и экспортировать их из `index.ts`.
## Отдельный каталог для каждой локали {#separate-directory-for-each-locale}
Пример многоязычной структуры:
```
docs/
├─ en/
│ ├─ foo.md
├─ es/
│ ├─ foo.md
├─ ru/
├─ foo.md
```
Однако по умолчанию VitePress не будет перенаправлять `/` на `/en/`. Для этого вам нужно будет настроить свой сервер. Например, в Netlify вы можете добавить файл `docs/public/_redirects` следующим образом:
```
/* /es/:splat 302 Language=es
/* /ru/:splat 302 Language=ru
/* /en/:splat 302
```
**Совет:** Если вы используете описанный выше подход, вы можете использовать куки `nf_lang`, чтобы сохранить выбор языка пользователя:
```ts
// docs/.vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import Layout from './Layout.vue'
export default {
extends: DefaultTheme,
Layout
}
```
```vue
<!-- docs/.vitepress/theme/Layout.vue -->
<script setup lang="ts">
import DefaultTheme from 'vitepress/theme'
import { useData } from 'vitepress'
import { watchEffect } from 'vue'
const { lang } = useData()
watchEffect(() => {
if (inBrowser) {
document.cookie = `nf_lang=${lang.value}; expires=Mon, 1 Jan 2030 00:00:00 UTC; path=/`
}
})
</script>
<template>
<DefaultTheme.Layout />
</template>
```
## Поддержка RTL (экспериментально) {#rtl-support-experimental}
Для поддержки языков с написанием справа налево укажите `dir: 'rtl'` в конфиге и используйте какой-нибудь плагин RTLCSS PostCSS, например <https://github.com/MohammadYounes/rtlcss>, <https://github.com/vkalinichev/postcss-rtl> или <https://github.com/elchininet/postcss-rtlcss>. Вам нужно настроить плагин PostCSS на использование `:where([dir="ltr"])` и `:where([dir="rtl"])` в качестве префиксов, чтобы избежать проблем со спецификой CSS.

@ -0,0 +1,933 @@
# Расширения Markdown {#markdown-extensions}
VitePress поставляется со встроенными расширениями Markdown.
## Якоря заголовков {#header-anchors}
К заголовкам автоматически применяются якорные ссылки. Отрисовку якорей можно настроить с помощью опции `markdown.anchor`.
### Пользовательские якоря {#custom-anchors}
Чтобы указать пользовательский тег якоря для заголовка, а не использовать автоматически сгенерированный, добавьте суффикс к заголовку:
```
# Использование пользовательских якорей {#мой-якорь}
```
Это позволит вам ссылаться на заголовок как `#мой-якорь` вместо стандартного `#использование-пользовательских-якорей`.
## Ссылки {#links}
Особое внимание уделяется как внутренним, так и внешним ссылкам.
### Внутренние ссылки {#internal-links}
Внутренние ссылки преобразуются в ссылки маршрутизатора для навигации SPA. Кроме того, каждый `index.md`, содержащийся в каждом подкаталоге, будет автоматически преобразован в `index.html`, с соответствующим URL `/`.
Например, при следующей структуре каталогов:
```
.
├─ index.md
├─ foo
│ ├─ index.md
│ ├─ one.md
│ └─ two.md
└─ bar
├─ index.md
├─ three.md
└─ four.md
```
И при условии, что вы находитесь в `foo/one.md`:
```md
[Home](/) <!-- отправляет пользователя в корневой index.md -->
[foo](/foo/) <!-- отправляет пользователя на страницу index.html из каталога foo -->
[foo heading](./#heading) <!-- привязывает пользователя к заголовку в индексном файле foo -->
[bar - three](../bar/three) <!-- вы можете опустить расширение -->
[bar - three](../bar/three.md) <!-- вы можете добавить .md -->
[bar - four](../bar/four.html) <!-- или вы можете добавить .html -->
```
### Суффикс страницы {#page-suffix}
Страницы и внутренние ссылки по умолчанию генерируются с суффиксом `.html`.
### Внешние ссылки {#external-links}
Исходящие ссылки автоматически получают значение `target="_blank" rel="noreferrer"`:
- [vuejs.org](https://vuejs.org)
- [VitePress on GitHub](https://github.com/vuejs/vitepress)
## Метаданные {#frontmatter}
[Метаданные YAML](https://jekyllrb.com/docs/front-matter/) поддерживаются из коробки:
```yaml
---
title: Веду блог как хакер
lang: ru-RU
---
```
Эти данные будут доступны остальной части страницы, а также всем пользовательским и тематическим компонентам.
Более подробную информацию можно найти в главе [Метаданные](../reference/frontmatter-config).
## Таблицы в стиле GitHub {#github-style-tables}
**Разметка**
```md
| Таблицы | это | круто |
| ---------------- | :----------------------: | ----: |
| столбец 3 | выровнен по правому краю | $1600 |
| столбец 2 | отцентрован | $12 |
| полосатые строки | как полоски у зебры | $1 |
```
**Результат**
| Таблицы | это | круто |
| ---------------- | :----------------------: | -----: |
| столбец 3 | выровнен по правому краю | \$1600 |
| столбец 2 | отцентрован | \$12 |
| полосатые строки | как полоски у зебры | \$1 |
## Эмодзи :tada: {#emoji}
**Разметка**
```
:tada: :100:
```
**Результат**
:tada: :100:
[Список всех эмодзи](https://github.com/markdown-it/markdown-it-emoji/blob/master/lib/data/full.mjs).
## Оглавление {#table-of-contents}
**Разметка**
```
[[toc]]
```
**Результат**
[[toc]]
Отрисовка TOC может быть настроена с помощью опции `markdown.toc`.
## Пользовательские контейнеры {#custom-containers}
Пользовательские контейнеры можно определить по их типам, заголовкам и содержимому.
### Заголовок по умолчанию {#default-title}
**Разметка**
```md
::: info
Это информация.
:::
::: tip
Это совет.
:::
::: warning
Это предупреждение.
:::
::: danger
Это сигнал об опасности.
:::
::: details
Это блок-спойлер.
:::
```
**Результат**
::: info
Это информация.
:::
::: tip
Это совет.
:::
::: warning
Это предупреждение.
:::
::: danger
Это сигнал об опасности.
:::
::: details
Это блок-спойлер.
:::
### Пользовательский заголовок {#custom-title}
Вы можете задать собственный заголовок, добавив текст сразу после «типа» контейнера.
**Разметка**
````md
::: danger СТОП
Опасная зона, остановитесь
:::
::: details Нажмите на меня, чтобы просмотреть код
```js
console.log('Привет, VitePress!')
```
:::
````
**Результат**
::: danger СТОП
Опасная зона, остановитесь
:::
::: details Нажмите на меня, чтобы просмотреть код
```js
console.log('Привет, VitePress!')
```
:::
Кроме того, вы можете установить пользовательские заголовки глобально, добавив следующее содержимое в конфигурацию сайта, полезное, если вы пишете не на английском языке:
```ts
// config.ts
export default defineConfig({
// ...
markdown: {
container: {
tipLabel: 'СОВЕТ',
warningLabel: 'ПРЕДУПРЕЖДЕНИЕ',
dangerLabel: 'ОПАСНОСТЬ',
infoLabel: 'ИНФОРМАЦИЯ',
detailsLabel: 'Подробная информация'
}
}
// ...
})
```
### `raw` {#raw}
Это специальный контейнер, который можно использовать для предотвращения конфликтов стилей и маршрутизаторов с VitePress. Это особенно полезно при документировании библиотек компонентов. Вы также можете посмотреть [whyframe](https://whyframe.dev/docs/integrations/vitepress) для лучшей изоляции.
**Syntax**
```md
::: raw
Заворачивается в <div class="vp-raw">
:::
```
Класс `vp-raw` можно использовать и непосредственно на элементах. Изоляция стиля в настоящее время осуществляется по желанию:
- Установите `postcss` с помощью предпочитаемого менеджера пакетов:
```sh
$ npm add -D postcss
```
- Создайте файл с именем `docs/postcss.config.mjs` и добавьте в него следующее:
```js
import { postcssIsolateStyles } from 'vitepress'
export default {
plugins: [postcssIsolateStyles()]
}
```
Он использует [`postcss-prefix-selector`](https://github.com/postcss/postcss-load-config) под капотом. Вы можете передать ему параметры следующим образом:
```js
postcssIsolateStyles({
includeFiles: [/vp-doc\.css/] // по умолчанию /base\.css/
})
```
## Оповещения в стиле GitHub {#github-flavored-alerts}
VitePress также поддерживает [Оповещения в стиле GitHub](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) для отображения в виде призывов. Они будут отображаться так же, как и [пользовательские контейнеры](#custom-containers).
```md
> [!NOTE]
> Выделяет информацию, на которую пользователи должны обратить внимание, даже при беглом просмотре.
> [!TIP]
> Дополнительная информация, которая поможет пользователю добиться большего успеха.
> [!IMPORTANT]
> Важнейшая информация, необходимая пользователям для достижения успеха.
> [!WARNING]
> Критический контент, требующий немедленного внимания пользователей из-за потенциальных рисков.
> [!CAUTION]
> Негативные потенциальные последствия того или иного действия.
```
> [!NOTE]
> Выделяет информацию, на которую пользователи должны обратить внимание, даже при беглом просмотре.
> [!TIP]
> Дополнительная информация, которая поможет пользователю добиться большего успеха.
> [!IMPORTANT]
> Важнейшая информация, необходимая пользователям для достижения успеха.
> [!WARNING]
> Критический контент, требующий немедленного внимания пользователей из-за потенциальных рисков.
> [!CAUTION]
> Негативные потенциальные последствия того или иного действия.
## Подсветка синтаксиса в блоках кода {#syntax-highlighting-in-code-blocks}
VitePress использует [Shiki](https://github.com/shikijs/shiki) для выделения синтаксиса языка в блоках кода Markdown с помощью цветного текста. Shiki поддерживает широкий спектр языков программирования. Всё, что вам нужно сделать, это добавить правильный псевдоним языка к начальным значкам блока кода:
**Разметка**
````
```js
export default {
name: 'MyComponent',
// ...
}
```
````
````
```html
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
```
````
**Результат**
```js
export default {
name: 'MyComponent'
// ...
}
```
```html
<ul>
<li v-for="todo in todos" :key="todo.id">{{ todo.text }}</li>
</ul>
```
[Список всех поддерживаемых языков](https://shiki.style/languages).
Вы также можете настроить тему подсветки синтаксиса в конфигурации приложения. Более подробную информацию см. в секции [`markdown`](../reference/site-config#markdown).
## Выделение строк в блоках кода {#line-highlighting-in-code-blocks}
**Разметка**
````
```js{4}
export default {
data () {
return {
msg: 'Подсвечено!'
}
}
}
```
````
**Результат**
```js{4}
export default {
data () {
return {
msg: 'Подсвечено!'
}
}
}
```
Помимо одной строки, вы можете указать несколько отдельных строк, диапазонов или и то, и другое:
Диапазоны строк, например: `{5-8}`, `{3-10}`, `{10-17}`
- Несколько одиночных строк, например: `{4,7,9}`
- Диапазоны строк и отдельные строки, например: `{4,7-13,16,23-27,40}`
**Разметка**
````
```js{1,4,6-8}
export default { // Подсвечено
data () {
return {
msg: `Подсвечено!
Эта строка не выделена,
но эта и две следующих - да.`,
motd: 'VitePress - это потрясающе',
lorem: 'ipsum'
}
}
}
```
````
**Результат**
```js{1,4,6-8}
export default { // Подсвечено
data () {
return {
msg: `Подсвечено!
Эта строка не выделена,
но эта и две следующих - да.`,
motd: 'VitePress - это потрясающе',
lorem: 'ipsum',
}
}
}
```
Кроме того, можно выделять непосредственно в строке, используя комментарий `// [!code highlight]`.
**Разметка**
````
```js
export default {
data () {
return {
msg: 'Подсвечено!' // [!!code highlight]
}
}
}
```
````
**Результат**
```js
export default {
data() {
return {
msg: 'Подсвечено!' // [!code highlight]
}
}
}
```
## Фокус в блоках кода {#focus-in-code-blocks}
Добавление комментария `// [!code focus]` к строке сфокусирует её и размоет остальные части кода.
Кроме того, вы можете задать количество строк для фокусировки с помощью `// [!code focus:<lines>]`.
**Разметка**
````
```js
export default {
data () {
return {
msg: 'Фокус!' // [!!code focus]
}
}
}
```
````
**Результат**
```js
export default {
data() {
return {
msg: 'Фокус!' // [!code focus]
}
}
}
```
## Подсветка различий в блоках кода {#colored-diffs-in-code-blocks}
Добавление в строку комментариев `// [!code --]` или `// [!code ++]` создаст diff этой строки, сохраняя цвета блока кода.
**Разметка**
````
```js
export default {
data () {
return {
msg: 'Удалено' // [!!code --]
msg: 'Добавлено' // [!!code ++]
}
}
}
```
````
**Результат**
```js
export default {
data () {
return {
msg: 'Удалено' // [!code --]
msg: 'Добавлено' // [!code ++]
}
}
}
```
## Ошибки и предупреждения в блоках кода {#errors-and-warnings-in-code-blocks}
Добавление в строку комментариев `// [!code warning]` или `// [!code error]` окрасит её соответствующим образом.
**Разметка**
````
```js
export default {
data () {
return {
msg: 'Ошибка', // [!!code error]
msg: 'Предупреждение' // [!!code warning]
}
}
}
```
````
**Результат**
```js
export default {
data() {
return {
msg: 'Ошибка', // [!code error]
msg: 'Предупреждение' // [!code warning]
}
}
}
```
## Номера строк {#line-numbers}
Вы можете включить нумерацию строк для каждого блока кода в конфигурации:
```js
export default {
markdown: {
lineNumbers: true
}
}
```
Более подробную информацию см. в секции [`markdown`](../reference/site-config#markdown).
Вы можете добавить метки `:line-numbers` / `:no-line-numbers` в ваши ограждённые блоки кода, чтобы переопределить значение, установленное в конфиге.
Вы также можете настроить номер начальной строки, добавив `=` после `:line-numbers`. Например, `:line-numbers=2` означает, что номера строк в блоках кода будут начинаться с `2`.
**Разметка**
````md
```ts {1}
// опция line-numbers по умолчанию отключена
const line2 = 'Строка 2'
const line3 = 'Строка 3'
```
```ts:line-numbers {1}
// опция line-numbers включена
const line2 = 'Строка 2'
const line3 = 'Строка 3'
```
```ts:line-numbers=2 {1}
// опция line-numbers включена, нумерация начинается с 2
const line3 = 'Строка 3'
const line4 = 'Строка 4'
```
````
**Результат**
```ts {1}
// опция line-numbers по умолчанию отключена
const line2 = 'Строка 2'
const line3 = 'Строка 3'
```
```ts:line-numbers {1}
// опция line-numbers включена
const line2 = 'Строка 2'
const line3 = 'Строка 3'
```
```ts:line-numbers=2 {1}
// опция line-numbers включена, нумерация начинается с 2
const line3 = 'Строка 3'
const line4 = 'Строка 4'
```
## Импорт фрагментов кода {#import-code-snippets}
Вы можете импортировать фрагменты кода из существующих файлов, используя следующий синтаксис:
```md
<<< @/filepath
```
[Выделение строк](#line-highlighting-in-code-blocks) тоже поддерживается:
```md
<<< @/filepath{highlightLines}
```
**Разметка**
```md
<<< @/snippets/snippet.js{2}
```
**Файл с кодом**
<<< @/snippets/snippet.js
**Результат**
<<< @/snippets/snippet.js{2}
::: tip СОВЕТ
Значение `@` соответствует корню источника. По умолчанию это корень проекта VitePress, если не настроен `srcDir`. Альтернативно вы также можете импортировать из относительных путей:
```md
<<< ../snippets/snippet.js
```
:::
Вы также можете использовать [регион VS Code](https://code.visualstudio.com/docs/editor/codebasics#_folding), чтобы включить только соответствующую часть файла кода. Имя пользовательского региона начинается с `#` после пути к файлу:
**Разметка**
```md
<<< @/snippets/snippet-with-region.js#snippet{1}
```
**Файл с кодом**
<<< @/snippets/snippet-with-region.js
**Результат**
<<< @/snippets/snippet-with-region.js#snippet{1}
Вы также можете указать язык внутри фигурных скобок (`{}`) следующим образом:
```md
<<< @/snippets/snippet.cs{c#}
<!-- с подсветкой строк: -->
<<< @/snippets/snippet.cs{1,2,4-6 c#}
<!-- с номерами строк: -->
<<< @/snippets/snippet.cs{1,2,4-6 c#:line-numbers}
```
Это полезно, если исходный язык нельзя определить по расширению вашего файла.
## Группы кодов {#code-groups}
Вы можете сгруппировать несколько блоков кода следующим образом:
**Разметка**
````md
::: code-group
```js [config.js]
/**
* @type {import('vitepress').UserConfig}
*/
const config = {
// ...
}
export default config
```
```ts [config.ts]
import type { UserConfig } from 'vitepress'
const config: UserConfig = {
// ...
}
export default config
```
:::
````
**Результат**
::: code-group
```js [config.js]
/**
* @type {import('vitepress').UserConfig}
*/
const config = {
// ...
}
export default config
```
```ts [config.ts]
import type { UserConfig } from 'vitepress'
const config: UserConfig = {
// ...
}
export default config
```
:::
Вы также можете [импортировать фрагменты](#import-code-snippets) в группы кода:
**Разметка**
```md
::: code-group
<!-- по умолчанию в качестве заголовка используется имя файла -->
<<< @/snippets/snippet.js
<!-- но можно предоставить и индивидуальный вариант -->
<<< @/snippets/snippet-with-region.js#snippet{1,2 ts:line-numbers} [фрагмент с регионом]
:::
```
**Результат**
::: code-group
<<< @/snippets/snippet.js
<<< @/snippets/snippet-with-region.js#snippet{1,2 ts:line-numbers} [фрагмент с регионом]
:::
## Включение файла Markdown {#markdown-file-inclusion}
Вы можете включить файл Markdown в другой файл Markdown, даже вложенный.
::: tip СОВЕТ
Вы также можете добавить в префикс пути к Markdown символ `@`, он будет выступать в качестве корня источника. По умолчанию это корень проекта VitePress, если не настроена опция `srcDir`.
:::
Например, вы можете включить относительный файл Markdown следующим образом:
**Разметка**
```md
# Документация
## Основы
<!--@include: ./parts/basics.md-->
```
**Файл части** (`parts/basics.md`)
```md
Некоторые вещи для начала.
### Конфигурация
Может быть создана с помощью `.foorc.json`.
```
**Эквивалентный код**
```md
# Документация
## Основы
Некоторые вещи для начала.
### Конфигурация
Может быть создана с помощью `.foorc.json`.
```
Он также поддерживает выбор диапазона строк:
**Разметка**
```md
# Документация
## Основы
<!--@include: ./parts/basics.md{3,}-->
```
**Файл части** (`parts/basics.md`)
```md
Некоторые вещи для начала.
### Конфигурация
Может быть создана с помощью `.foorc.json`.
```
**Эквивалентный код**
```md
# Документация
## Основы
### Конфигурация
Может быть создана с помощью `.foorc.json`.
```
Формат выбранного диапазона строк может быть следующим: `{3,}`, `{,10}`, `{1,10}`
::: warning ПРЕДУПРЕЖДЕНИЕ
Обратите внимание, что это не приводит к ошибкам, если ваш файл отсутствует. Поэтому при использовании этой функции убедитесь, что содержимое отображается так, как ожидается.
:::
## Математические уравнения {#math-equations}
В настоящее время эта фича предоставляется по желанию. Чтобы включить её, вам нужно установить `markdown-it-mathjax3` и установить значение `true` для опции `markdown.math` в вашем файле конфигурации:
```sh
npm add -D markdown-it-mathjax3
```
```ts
// .vitepress/config.ts
export default {
markdown: {
math: true
}
}
```
**Разметка**
```md
Когда $a \ne 0$, существует два решения $(ax^2 + bx + c = 0)$:
$$ x = {-b \pm \sqrt{b^2-4ac} \over 2a} $$
**Уравнения Максвелла:**
| уравнение | описание |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| $\nabla \cdot \vec{\mathbf{B}} = 0$ | дивергенция $\vec{\mathbf{B}}$ равна нулю |
| $\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} = \vec{\mathbf{0}}$ | искривление $\vec{\mathbf{E}}$ пропорционально скорости изменения $\vec{\mathbf{B}}$ |
| $\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} = \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} = 4 \pi \rho$ | _что?_ |
```
**Результат**
Когда $a \ne 0$, существует два решения $(ax^2 + bx + c = 0)$:
$$ x = {-b \pm \sqrt{b^2-4ac} \over 2a} $$
**Уравнения Максвелла:**
| уравнение | описание |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| $\nabla \cdot \vec{\mathbf{B}} = 0$ | дивергенция $\vec{\mathbf{B}}$ равна нулю |
| $\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} = \vec{\mathbf{0}}$ | искривление $\vec{\mathbf{E}}$ пропорционально скорости изменения $\vec{\mathbf{B}}$ |
| $\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} = \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} = 4 \pi \rho$ | _что?_ |
## Ленивая загрузка изображений {#image-lazy-loading}
Вы можете включить ленивую загрузку для каждого изображения, добавленного через markdown, установив значение `true` для опции `lazyLoading` в вашем файле конфигурации:
```js
export default {
markdown: {
image: {
// ленивая загрузка изображений отключена по умолчанию
lazyLoading: true
}
}
}
```
## Расширенная конфигурация {#advanced-configuration}
VitePress использует [markdown-it](https://github.com/markdown-it/markdown-it) для отрисовки Markdown. Многие из вышеперечисленных расширений реализованы с помощью пользовательских плагинов. Вы можете дополнительно настроить экземпляр `markdown-it` с помощью опции `markdown` в файле `.vitepress/config.js`:
```js
import { defineConfig } from 'vitepress'
import markdownItAnchor from 'markdown-it-anchor'
import markdownItFoo from 'markdown-it-foo'
export default defineConfig({
markdown: {
// опции для markdown-it-anchor
// https://github.com/valeriangalliat/markdown-it-anchor#usage
anchor: {
permalink: markdownItAnchor.permalink.headerLink()
},
// опции для @mdit-vue/plugin-toc
// https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-toc#options
toc: { level: [1, 2] },
config: (md) => {
// используйте любые плагины для markdown-it!
md.use(markdownItFoo)
}
}
})
```
Полный список настраиваемых свойств см. в секции [`markdown`](../reference/site-config#markdown).

@ -0,0 +1,23 @@
# Переход с VitePress 0.x
Если вы переходите с версии VitePress 0.x, то в ней есть несколько изменений, связанных с новыми функциями и улучшениями. Следуйте этому руководству, чтобы узнать, как перенести ваше приложение на последнюю версию VitePress.
## Конфигурация приложения
- Функция интернационализации ещё не реализована.
## Конфигурация темы
- Опция `sidebar` изменила свою структуру.
- Ключ `children` теперь называется `items`.
- Элемент верхнего уровня может не содержать `link` в данный момент. Мы планируем вернуть его обратно.
- `repo`, `repoLabel`, `docsDir`, `docsBranch`, `editLinks`, `editLinkText` удалены в пользу более гибкого api.
- Для добавления ссылки GitHub с иконкой в навигацию используйте функцию [Социальные ссылки](../reference/default-theme-nav#navigation-links).
- Для добавления ссылки «Редактировать эту страницу» используйте функцию [Ссылка для редактирования](../reference/default-theme-edit-link).
- Опция `lastUpdated` теперь разделена на `config.lastUpdated` и `themeConfig.lastUpdatedText`.
- Опция `carbonAds.carbon` заменена на `carbonAds.code`.
## Конфигурация метаданных
- Опция `home: true` заменена на `layout: home`. Кроме того, многие настройки, связанные с главной страницей, были изменены для обеспечения дополнительных возможностей. Подробности см. в разделе [Главная страница](../reference/default-theme-home-page).
- Опция `footer` перенесена в [`themeConfig.footer`](../reference/default-theme-config#footer).

@ -0,0 +1,30 @@
# Переход с VuePress
## Конфигурация
### Сайдбар
Боковая панель больше не заполняется автоматически из метаданных. Вы можете [самостоятельно прочитать вступление](https://github.com/vuejs/vitepress/issues/572#issuecomment-1170116225), чтобы научиться динамически заполнять боковую панель. [Дополнительные утилиты для этого](https://github.com/vuejs/vitepress/issues/96) могут быть предоставлены в будущем.
## Markdown
### Изображения
В отличие от VuePress, VitePress автоматически обрабатывает опцию [`base`](./asset-handling#base-url) вашего конфига, когда вы используете статическое изображение.
Таким образом, теперь вы можете выводить изображения без тега `img`.
```diff
- <img :src="$withBase('/foo.png')" alt="foo">
+ ![foo](/foo.png)
```
::: warning ПРЕДУПРЕЖДЕНИЕ
Для динамических изображений вам всё ещё нужно использовать `withBase`, как показано в [Руководстве по базовому URL](./asset-handling#base-url).
:::
Используйте регулярное выражение `<img.*withBase\('(.*)'\).*alt="([^"]*)".*>` для поиска всех изображений с синтаксисом `![](...)` и замены на `![$2]($1)`.
---
продолжение следует...

@ -0,0 +1,23 @@
# Режим MPA <Badge type="warning" text="экспериментально" /> {#mpa-mode}
Режим MPA (Multi-Page Application — «Многостраничное приложение») можно включить через командную строку с помощью команды `vitepress build --mpa`, или через конфигурацию с помощью опции `mpa: true`.
В режиме MPA все страницы по умолчанию отображаются без включенного JavaScript. В результате производственный сайт, скорее всего, получит более высокую оценку эффективности первых посещений с помощью инструментов аудита.
Однако из-за отсутствия навигации SPA межстраничные ссылки будут приводить к полной перезагрузке страницы. После загрузки навигация в режиме MPA будет не такой мгновенной, как в режиме SPA.
Также обратите внимание, что «no-JS-by-default» («без JS по умолчанию») означает, что вы используете Vue исключительно как серверный язык шаблонов. Никаких обработчиков событий в браузере не будет, поэтому интерактивности не будет. Чтобы загрузить JavaScript со стороны клиента, вам нужно использовать специальный тег `<script client>`:
```html
<script client>
document.querySelector('h1').addEventListener('click', () => {
console.log('JavaScript на стороне клиента!')
})
</script>
# Привет
```
`<script client>` — это функция только для VitePress, а не для Vue. Она работает как в файлах `.md`, так и в файлах `.vue`, но только в режиме MPA. Клиентские скрипты во всех компонентах темы будут объединены вместе, в то время как клиентский скрипт для конкретной страницы будет разделён только для этой страницы.
Обратите внимание, что `<script client>` **не оценивается как код компонента Vue**: он обрабатывается как обычный модуль JavaScript. По этой причине режим MPA следует использовать только в том случае, если ваш сайт требует абсолютно минимальной интерактивности на стороне клиента.

@ -0,0 +1,371 @@
---
outline: deep
---
# Маршрутизация {#routing}
## Маршрутизация на основе файлов {#file-based-routing}
VitePress использует маршрутизацию на основе файлов, что означает, что сгенерированные HTML-страницы отображаются на основе структуры папок исходных файлов Markdown. Например, при следующей структуре папок:
```
.
├─ guide
│ ├─ getting-started.md
│ └─ index.md
├─ index.md
└─ prologue.md
```
Сгенерированные HTML-страницы будут выглядеть так:
```
index.md --> /index.html (доступна по адресу /)
prologue.md --> /prologue.html
guide/index.md --> /guide/index.html (доступна по адресу /guide/)
guide/getting-started.md --> /guide/getting-started.html
```
Полученный HTML можно разместить на любом веб-сервере, который может обслуживать статические файлы.
## Корневая директория и директория с исходными файлами {#root-and-source-directory}
В файловой структуре проекта VitePress есть два важных понятия: **корень проекта** и **директория с исходными файлами**.
### Корень проекта {#project-root}
Корень проекта — это место, где VitePress будет пытаться искать специальный каталог `.vitepress`. Директория `.vitepress` — это зарезервированное место для конфигурационного файла VitePress, кэша dev-сервера, результатов сборки и дополнительного кода настройки темы.
Когда вы запускаете `vitepress dev` или `vitepress build` из командной строки, VitePress будет использовать текущий рабочий каталог в качестве корня проекта. Чтобы указать подкаталог в качестве корневого, нужно передать команде относительный путь. Например, если ваш проект VitePress находится в папке `./docs`, вам следует выполнить команду `vitepress dev docs`:
```
.
├─ docs # корень проекта
│ ├─ .vitepress # директория с настройками
│ ├─ getting-started.md
│ └─ index.md
└─ ...
```
```sh
vitepress dev docs
```
В результате получится следующее сопоставление источника с HTML:
```
docs/index.md --> /index.html (доступна по адресу /)
docs/getting-started.md --> /getting-started.html
```
### Директория с исходными файлами {#source-directory}
Директория с исходными файлами — это место, где хранятся ваши исходные файлы Markdown. По умолчанию она совпадает с корнем проекта. Однако вы можете настроить её с помощью параметра [`srcDir`](../reference/site-config#srcdir).
Опция `srcDir` разрешается относительно корня проекта. Например, с помощью `srcDir: 'src'`, ваша файловая структура будет выглядеть следующим образом:
```
. # корень проекта
├─ .vitepress # директория с настройками
└─ src # директория с исходными файлами
├─ getting-started.md
└─ index.md
```
Итоговое сопоставление исходного кода с HTML:
```
src/index.md --> /index.html (доступна по адресу /)
src/getting-started.md --> /getting-started.html
```
## Связи между страницами {#linking-between-pages}
При создании ссылок между страницами можно использовать как абсолютные, так и относительные пути. Обратите внимание, что хотя расширения `.md` и `.html` будут работать, лучше всего опускать расширения файлов, чтобы VitePress мог генерировать конечные URL на основе вашего конфига.
```md
<!-- Будут работать -->
[Первые шаги](./getting-started)
[Первые шаги](../guide/getting-started)
<!-- Не будут работать -->
[Первые шаги](./getting-started.md)
[Первые шаги](./getting-started.html)
```
Узнайте больше о ссылках на такие ресурсы, как изображения, в главе [Обработка ресурсов](./asset-handling).
### Ссылки на страницы, не принадлежащие VitePress {#linking-to-non-vitepress-pages}
Если вы хотите создать ссылку на страницу вашего сайта, которая не создана VitePress, вам нужно будет либо использовать полный URL-адрес (откроется в новой вкладке), либо явно указать цель:
**Разметка**
```md
[Ссылка на pure.html](/pure.html){target="_self"}
```
**Результат**
[Ссылка на pure.html](/pure.html){target="_self"}
::: tip Примечание
В ссылках Markdown к URL-адресу автоматически добавляется значение параметра `base`. Это означает, что если вы хотите создать ссылку на страницу за пределами `base`, вам понадобится что-то вроде `../../pure.html` в ссылке (разрешаемой браузером относительно текущей страницы).
Альтернативно можно напрямую использовать синтаксис тега ссылки:
```md
<a href="/pure.html" target="_self">Ссылка на pure.html</a>
```
:::
## Создание чистого URL-адреса {#generating-clean-url}
::: warning Требуется поддержка сервера
Для обслуживания чистых URL-адресов с помощью VitePress требуется поддержка на стороне сервера.
:::
По умолчанию VitePress разрешает входящие ссылки на URL-адреса, заканчивающиеся на `.html`. Однако некоторые пользователи могут предпочесть «Чистые URL-адреса» без расширения `.html` — например, `example.com/path` вместо `example.com/path.html`.
Некоторые серверы или хостинговые платформы (например, Netlify, Vercel, GitHub Pages) предоставляют возможность сопоставлять URL-адрес типа `/foo` с `/foo.html`, если он существует, без перенаправления:
- Страницы Netlify и GitHub поддерживают это по умолчанию.
- Vercel требует включить [опцию `cleanUrls` в `vercel.json`](https://vercel.com/docs/concepts/projects/project-configuration#cleanurls).
Если эта функция вам доступна, вы также можете включить собственную опцию конфигурации VitePress [`cleanUrls`](../reference/site-config#cleanurls), чтобы обеспечить следующее поведение:
- Входящие ссылки между страницами генерируются без расширения `.html`.
- Если текущий путь заканчивается на `.html`, маршрутизатор выполнит перенаправление на стороне клиента на путь без расширений.
Однако если вы не можете настроить свой сервер с такой поддержкой, вам придётся вручную прибегнуть к следующей структуре каталогов:
```
.
├─ getting-started
│ └─ index.md
├─ installation
│ └─ index.md
└─ index.md
```
## Перезапись маршрутов {#route-rewrites}
Вы можете настроить сопоставление между структурой исходного каталога и создаваемыми страницами. Это полезно, когда у вас сложная структура проекта. Допустим, у вас есть монорепо с несколькими пакетами, и вы хотите поместить документацию вместе с исходными файлами, как это сделано здесь:
```
.
├─ packages
│ ├─ pkg-a
│ │ └─ src
│ │ ├─ pkg-a-code.ts
│ │ └─ pkg-a-docs.md
│ └─ pkg-b
│ └─ src
│ ├─ pkg-b-code.ts
│ └─ pkg-b-docs.md
```
И вы хотите, чтобы страницы VitePress генерировались следующим образом:
```
packages/pkg-a/src/pkg-a-docs.md --> /pkg-a/index.html
packages/pkg-b/src/pkg-b-docs.md --> /pkg-b/index.html
```
Этого можно добиться, настроив опцию [`rewrites`](../reference/site-config#rewrites) следующим образом:
```ts
// .vitepress/config.js
export default {
rewrites: {
'packages/pkg-a/src/pkg-a-docs.md': 'pkg-a/index.md',
'packages/pkg-b/src/pkg-b-docs.md': 'pkg-b/index.md'
}
}
```
Опция `rewrites` также поддерживает динамические параметры маршрута. В приведенном выше примере, если у вас много пакетов, перечислять все пути было бы скучно. Учитывая, что все они имеют одинаковую структуру файлов, вы можете упростить конфигурацию следующим образом:
```ts
export default {
rewrites: {
'packages/:pkg/src/(.*)': ':pkg/index.md'
}
}
```
Пути перезаписи компилируются с помощью пакета `path-to-regexp` — обратитесь к [его документации](https://github.com/pillarjs/path-to-regexp#parameters) за более сложным синтаксисом.
::: warning Относительные ссылки с переписыванием
Когда переписывание включено, **относительные ссылки должны быть основаны на переписанных путях**. Например, чтобы создать относительную ссылку с `packages/pkg-a/src/pkg-a-code.md` на `packages/pkg-b/src/pkg-b-code.md`, нужно использовать:
```md
[Ссылка на PKG B](../pkg-b/pkg-b-code)
```
:::
## Динамические маршруты {#dynamic-routes}
Вы можете создать множество страниц, используя один файл Markdown и динамические данные. Например, вы можете создать файл `packages/[pkg].md`, который будет генерировать соответствующую страницу для каждого пакета в проекте. Здесь сегмент `[pkg]` является **параметром** маршрута, который отличает каждую страницу от других.
### Файл загрузчика путей {#paths-loader-file}
Поскольку VitePress — это генератор статических сайтов, возможные пути страниц должны быть определены во время сборки. Поэтому динамическая маршрутная страница **должна** сопровождаться **файлом загрузчика путей**. Для `packages/[pkg].md` нам понадобится `packages/[pkg].paths.js` (`.ts` также поддерживается):
```
.
└─ packages
├─ [pkg].md # шаблон маршрута
└─ [pkg].paths.js # загрузчик путей маршрута
```
Загрузчик путей должен предоставлять объект с методом `paths` в качестве экспорта по умолчанию. Метод `paths` должен возвращать массив объектов со свойством `params`. Для каждого из этих объектов будет создана соответствующая страница.
Дан следующий массив `paths`:
```js
// packages/[pkg].paths.js
export default {
paths() {
return [{ params: { pkg: 'foo' } }, { params: { pkg: 'bar' } }]
}
}
```
Сгенерированные HTML-страницы будут выглядеть так:
```
.
└─ packages
├─ foo.html
└─ bar.html
```
### Несколько параметров {#multiple-params}
Динамический маршрут может содержать несколько параметров:
**Структура файлов**
```
.
└─ packages
├─ [pkg]-[version].md
└─ [pkg]-[version].paths.js
```
**Загрузчик путей**
```js
export default {
paths: () => [
{ params: { pkg: 'foo', version: '1.0.0' } },
{ params: { pkg: 'foo', version: '2.0.0' } },
{ params: { pkg: 'bar', version: '1.0.0' } },
{ params: { pkg: 'bar', version: '2.0.0' } }
]
}
```
**Результат**
```
.
└─ packages
├─ foo-1.0.0.html
├─ foo-2.0.0.html
├─ bar-1.0.0.html
└─ bar-2.0.0.html
```
### Динамически генерируемые пути {#dynamically-generating-paths}
Модуль загрузчика путей запускается в Node.js и выполняется только во время сборки. Вы можете динамически генерировать массив путей, используя любые данные, как локальные, так и удалённые.
Генерация путей из локальных файлов:
```js
import fs from 'fs'
export default {
paths() {
return fs.readdirSync('packages').map((pkg) => {
return { params: { pkg } }
})
}
}
```
Генерация путей из удалённых данных:
```js
export default {
async paths() {
const pkgs = await (await fetch('https://my-api.com/packages')).json()
return pkgs.map((pkg) => {
return {
params: {
pkg: pkg.name,
version: pkg.version
}
}
})
}
}
```
### Доступ к параметрам на странице {#accessing-params-in-page}
Вы можете использовать параметры для передачи дополнительных данных на каждую страницу. Файл маршрута Markdown может получить доступ к параметрам текущей страницы в выражениях Vue через глобальное свойство `$params`:
```md
- package name: {{ $params.pkg }}
- version: {{ $params.version }}
```
Вы также можете получить доступ к параметрам текущей страницы через Runtime API [`useData`](../reference/runtime-api#usedata). Это доступно как в файлах Markdown, так и в компонентах Vue:
```vue
<script setup>
import { useData } from 'vitepress'
// params — это ref-ссылка Vue
const { params } = useData()
console.log(params.value)
</script>
```
### Рендеринг необработанного содержимого {#rendering-raw-content}
Параметры, передаваемые странице, будут сериализованы в полезной нагрузке клиентского JavaScript, поэтому вам следует избегать передачи в параметрах больших объемов данных, например, необработанного Markdown или HTML-контента, полученного из удаленной CMS.
Вместо этого вы можете передавать такое содержимое на каждую страницу с помощью свойства `content` каждого объекта path:
```js
export default {
async paths() {
const posts = await (await fetch('https://my-cms.com/blog-posts')).json()
return posts.map((post) => {
return {
params: { id: post.id },
content: post.content // необработанный Markdown или HTML
}
})
}
}
```
Затем используйте следующий специальный синтаксис, чтобы отобразить содержимое как часть самого файла Markdown:
```md
<!-- @content -->
```

@ -0,0 +1,53 @@
# Генерация карты сайта {#sitemap-generation}
VitePress поставляется с готовой поддержкой генерации файла `sitemap.xml` для вашего сайта. Чтобы включить её, добавьте следующее в файл `.vitepress/config.js`:
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
sitemap: {
hostname: 'https://example.com'
}
})
```
Чтобы теги `<lastmod>` присутствовали в вашем файле `sitemap.xml`, вы можете включить опцию [`lastUpdated`](../reference/default-theme-last-updated).
## Параметры {#options}
Поддержка карты сайта осуществляется с помощью модуля [`sitemap`](https://www.npmjs.com/package/sitemap). Вы можете передать любые поддерживаемые им параметры в опцию `sitemap` в вашем конфигурационном файле. Они будут переданы непосредственно в конструктор `SitemapStream`. Более подробную информацию см. в документации [`sitemap`](https://www.npmjs.com/package/sitemap#options-you-can-pass). Пример:
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
sitemap: {
hostname: 'https://example.com',
lastmodDateOnly: false
}
})
```
## Хук `transformItems` {#transformitems-hook}
Вы можете использовать хук `sitemap.transformItems` для изменения элементов карты сайта перед их записью в файл `sitemap.xml`. Этот хук вызывается с массивом элементов sitemap и ожидает возвращения массива элементов sitemap. Пример:
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
sitemap: {
hostname: 'https://example.com',
transformItems: (items) => {
// добавляем новые элементы или изменяем/фильтруем существующие
items.push({
url: '/extra-page',
changefreq: 'monthly',
priority: 0.8
})
return items
}
}
})
```

@ -0,0 +1,137 @@
---
outline: deep
---
# Совместимость с SSR {#ssr-compatibility}
VitePress предварительно рендерит приложение в Node.js во время производственной сборки, используя возможности Vue по рендерингу на стороне сервера (SSR). Это означает, что весь пользовательский код в компонентах темы подлежит проверке на совместимость с SSR.
Глава [Рендеринг на стороне сервера](https://ru.vuejs.org/guide/scaling-up/ssr.html) в документации Vue содержит более подробную информацию о том, что такое SSR, взаимосвязь между SSR и SSG, а также общие указания по написанию кода, дружественного к SSR. Правило заключается в том, чтобы обращаться к API браузера / DOM только в хуках `beforeMount` или `mounted` компонентов Vue.
## `<ClientOnly>` {#clientonly}
Если вы используете или демонстрируете компоненты, которые не являются SSR-дружественными (например, содержат пользовательские директивы), вы можете обернуть их внутри встроенного компонента `<ClientOnly>`:
```md
<ClientOnly>
<NonSSRFriendlyComponent />
</ClientOnly>
```
## Библиотеки, обращающиеся к API браузера при импорте {#libraries-that-access-browser-api-on-import}
Некоторые компоненты или библиотеки получают доступ к API браузера **при импорте**. Чтобы использовать код, предполагающий наличие среды браузера при импорте, необходимо динамически импортировать их.
### Импорт в хуке `onMounted` {#importing-in-mounted-hook}
```vue
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
import('./lib-that-access-window-on-import').then((module) => {
// используем код
})
})
</script>
```
### Условный импорт {#conditional-import}
Вы также можете условно импортировать зависимость с помощью флага `import.meta.env.SSR` (часть [env-переменных Vite](https://vitejs.dev/guide/env-and-mode.html#env-variables)):
```js
if (!import.meta.env.SSR) {
import('./lib-that-access-window-on-import').then((module) => {
// используем код
})
}
```
Поскольку [`Theme.enhanceApp`](./custom-theme#theme-interface) может быть асинхронным, вы можете условно импортировать и регистрировать плагины Vue, которые получают доступ к API браузера при импорте:
```js
// .vitepress/theme/index.js
/** @type {import('vitepress').Theme} */
export default {
// ...
async enhanceApp({ app }) {
if (!import.meta.env.SSR) {
const plugin = await import('plugin-that-access-window-on-import')
app.use(plugin.default)
}
}
}
```
Если вы используете TypeScript:
```ts
// .vitepress/theme/index.ts
import type { Theme } from 'vitepress'
export default {
// ...
async enhanceApp({ app }) {
if (!import.meta.env.SSR) {
const plugin = await import('plugin-that-access-window-on-import')
app.use(plugin.default)
}
}
} satisfies Theme
```
### `defineClientComponent` {#defineclientcomponent}
VitePress предоставляет удобный помощник для импорта компонентов Vue, которые получают доступ к API браузера при импорте.
```vue
<script setup>
import { defineClientComponent } from 'vitepress'
const ClientComp = defineClientComponent(() => {
return import('component-that-access-window-on-import')
})
</script>
<template>
<ClientComp />
</template>
```
Вы также можете передавать параметры/дочерние элементы/слоты целевому компоненту:
```vue
<script setup>
import { ref } from 'vue'
import { defineClientComponent } from 'vitepress'
const clientCompRef = ref(null)
const ClientComp = defineClientComponent(
() => import('component-that-access-window-on-import'),
// args передаются в функцию h() - https://ru.vuejs.org/api/render-function.html#h
[
{
ref: clientCompRef
},
{
default: () => 'default slot',
foo: () => h('div', 'foo'),
bar: () => [h('span', 'one'), h('span', 'two')]
}
],
// обратный вызов после загрузки компонента, может быть асинхронным
() => {
console.log(clientCompRef.value)
}
)
</script>
<template>
<ClientComp />
</template>
```
Целевой компонент будет импортирован только в смонтированный хук компонента-обёртки.

@ -0,0 +1,253 @@
# Использование Vue в Markdown {#using-vue-in-markdown}
В VitePress каждый Markdown-файл компилируется в HTML, а затем обрабатывается как [однофайловый компонент Vue](https://ru.vuejs.org/guide/scaling-up/sfc.html). Это означает, что вы можете использовать любые возможности Vue внутри Markdown, включая динамический шаблонизатор, использование компонентов Vue или произвольную логику компонентов Vue на странице, добавив тег `<script>`.
Стоит отметить, что VitePress использует компилятор Vue для автоматического обнаружения и оптимизации чисто статических частей контента в формате Markdown. Статичное содержимое оптимизируется в отдельные узлы-заполнители и исключается из полезной нагрузки JavaScript страницы при первых посещениях. Они также пропускаются при гидратации на стороне клиента. Короче говоря, вы платите только за динамические части на каждой конкретной странице.
::: tip Совместимость с SSR
Всё, что используется в Vue, должно быть совместимо с SSR. Подробности и общие обходные пути см. в главе [Совместимость с SSR](./ssr-compat).
:::
## Шаблонизация {#templating}
### Интерполяция {#interpolation}
Каждый файл Markdown сначала компилируется в HTML, а затем передается в качестве компонента Vue в конвейер процесса Vite. Это означает, что вы можете использовать интерполяцию в стиле Vue в тексте:
**Разметка**
```md
{{ 1 + 1 }}
```
**Результат**
<div class="language-text"><pre><code>{{ 1 + 1 }}</code></pre></div>
### Директивы {#directives}
Директивы также работают (обратите внимание, что по замыслу необработанный HTML также допустим в Markdown):
**Разметка**
```html
<span v-for="i in 3">{{ i }}</span>
```
**Результат**
<div class="language-text"><pre><code><span v-for="i in 3">{{ i }} </span></code></pre></div>
## `<script>` и `<style>` {#script-and-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>
```
::: warning Избегайте `<style scoped>` в Markdown
При использовании в Markdown `<style scoped>` требует добавления специальных атрибутов к каждому элементу на текущей странице, что значительно увеличивает размер страницы. `<style module>` предпочтительнее, когда на странице требуется локально копируемый стиль.
:::
У вас также есть доступ к runtime API VitePress, например, к [хелперу `useData`](../reference/runtime-api#usedata), который предоставляет доступ к метаданным текущей страницы:
**Разметка**
```html
<script setup>
import { useData } from 'vitepress'
const { page } = useData()
</script>
<pre>{{ page }}</pre>
```
**Результат**
```json
{
"path": "/using-vue.html",
"title": "Использование Vue в Markdown",
"frontmatter": {},
...
}
```
## Использование компонентов {#using-components}
Вы можете импортировать и использовать компоненты Vue непосредственно в файлах Markdown.
### Импорт в Markdown {#importing-in-markdown}
Если компонент используется только на нескольких страницах, рекомендуется явно импортировать его туда, где он используется. Это позволяет правильно разделить их по коду и загружать только при показе соответствующих страниц:
```md
<script setup>
import CustomComponent from '../components/CustomComponent.vue'
</script>
# Документация
Это .md с использованием пользовательского компонента
<CustomComponent />
## Другая документация
...
```
### Глобальная регистрация компонентов {#registering-components-globally}
Если компонент будет использоваться на большинстве страниц, его можно зарегистрировать глобально, настроив экземпляр приложения Vue. Пример смотрите в соответствующей главе [Расширение темы по умолчанию](./extending-default-theme#registering-global-components).
::: warning ВАЖНО
Убедитесь, что имя пользовательского компонента содержит дефис или написано в PascalCase. В противном случае он будет рассматриваться как встроенный элемент и заключен в тег `<p>`, что приведет к несоответствию гидратации, поскольку `<p>` не позволяет размещать внутри него блочные элементы.
:::
### Использование компонентов в заголовках <ComponentInHeader /> {#using-components-in-headers}
Вы можете использовать компоненты Vue в заголовках, но обратите внимание на разницу между следующими синтаксисами:
| Markdown | Итоговый HTML | Разобранный заголовок |
| -------------------------------------------------------- | ------------------------------------------ | --------------------- |
| <pre v-pre><code> # текст &lt;Tag/&gt; </code></pre> | `<h1>текст <Tag/></h1>` | `текст` |
| <pre v-pre><code> # текст \`&lt;Tag/&gt;\` </code></pre> | `<h1>текст <code>&lt;Tag/&gt;</code></h1>` | `текст <Tag/>` |
HTML, обёрнутый `<code>`, будет отображаться как есть; только тот HTML, который **не** обёрнут, будет разобран Vue.
::: tip ПРИМЕЧАНИЕ
Вывод HTML осуществляется с помощью [Markdown-it](https://github.com/Markdown-it/Markdown-it), а парсинг заголовков обрабатывается VitePress (и используется как для боковой панели, так и для заголовка документа).
:::
## Экранирование {#escaping}
Вы можете избежать интерполяций Vue, обернув их в `<span>` или другие элементы с помощью директивы `v-pre`:
**Разметка**
```md
Это <span v-pre>{{ будет отображаться как есть}}</span>.
```
**Результат**
<div class="escape-demo">
<p>Это <span v-pre>{{ будет отображаться как есть}}</span></p>
</div>
В качестве альтернативы вы можете обернуть весь абзац в пользовательский контейнер `v-pre`:
```md
::: v-pre
{{ Это будет отображаться как есть }}
:::
```
**Результат**
<div class="escape-demo">
::: v-pre
{{ Это будет отображаться как есть }}
:::
</div>
## Unescape в блоках кода {#unescape-in-code-blocks}
По умолчанию все изолированные блоки кода автоматически оборачиваются `v-pre`, поэтому внутри них не будет обрабатываться синтаксис Vue. Чтобы включить интерполяцию в стиле Vue внутри фигурных скобок, вы можете добавить к языку суффикс `-vue`, например `js-vue`:
**Разметка**
````md
```js-vue
Привет, {{ 1 + 1 }}
```
````
**Результат**
```js-vue
Привет, {{ 1 + 1 }}
```
Обратите внимание, что при этом некоторые лексемы могут не выделяться синтаксисом должным образом.
## Использование препроцессоров CSS {#using-css-pre-processors}
VitePress имеет [встроенную поддержку](https://vitejs.dev/guide/features.html#css-pre-processors) для препроцессоров 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>
```
## Использование телепортов {#using-teleports}
В настоящее время Vitepress поддерживает SSG только для телепортов к элементу `body`. Для других целей вы можете обернуть их внутри встроенного компонента `<ClientOnly>` или внедрить разметку телепортации в нужное место HTML конечной страницы через [хук `postRender`](../reference/site-config#postrender).
<ModalDemo />
::: details Исходный код
<<< @/ru/components/ModalDemo.vue
:::
```md
<ClientOnly>
<Teleport to="#modal">
<div>
// ...
</div>
</Teleport>
</ClientOnly>
```
<script setup>
import ModalDemo from '../components/ModalDemo.vue'
import ComponentInHeader from '../../components/ComponentInHeader.vue'
</script>
<style>
.escape-demo {
border: 1px solid var(--vp-c-border);
border-radius: 8px;
padding: 0 20px;
}
</style>

@ -0,0 +1,57 @@
# Что такое VitePress? {#what-is-vitepress}
VitePress — это [Генератор статических сайтов](https://en.wikipedia.org/wiki/Static_site_generator) (ГСС), предназначенный для быстрого создания сайтов, ориентированных на контент. В двух словах, VitePress берёт ваш исходный контент, написанный в [Markdown](https://ru.wikipedia.org/wiki/Markdown), применяет к нему тему и генерирует статические HTML-страницы, которые можно легко развернуть в любом месте.
<div class="tip custom-block" style="padding-top: 8px">
Хотите попробовать прямо сейчас? Перейдите к главе [Первые шаги](./getting-started).
</div>
## Примеры использования {#use-cases}
- **Документация**
VitePress поставляется с темой по умолчанию, предназначенной для технической документации. Она содержит эту страницу, которую вы сейчас читаете, а также документацию по [Vite](https://vitejs.dev/), [Rollup](https://rollupjs.org/), [Pinia](https://pinia.vuejs.org/), [VueUse](https://vueuse.org/), [Vitest](https://vitest.dev/), [D3](https://d3js.org/), [UnoCSS](https://unocss.dev/), [Iconify](https://iconify.design/) и [многое другое](https://www.vuetelescope.com/explore?framework.slug=vitepress).
[Официальная документация Vue.js](https://vuejs.org/) также основана на VitePress, но использует пользовательскую тему, разделяемую между несколькими переводами.
- **Блоги, портфолио и маркетинговые сайты**
VitePress поддерживает [полностью кастомизированные темы](./custom-theme), при этом разработчики могут использовать стандартное приложение Vite + Vue. То, что он построен на базе Vite, также означает, что вы можете напрямую использовать плагины Vite из его богатой экосистемы. Кроме того, VitePress предоставляет гибкие API для [загрузки данных](./data-loading) (локальной или удаленной) и [динамической генерации маршрутов](./routing#dynamic-routes). С его помощью можно построить практически всё, что угодно, если данные могут быть определены во время сборки.
Официальный [блог Vue.js](https://blog.vuejs.org/) — это простой блог, который генерирует свою индексную страницу на основе локального контента.
## Опыт разработчика {#developer-experience}
VitePress стремится обеспечить отличные возможности для разработчиков при работе с содержимым в формате Markdown.
- **[На базе Vite:](https://vitejs.dev/)** мгновенный запуск сервера, правки всегда отражаются мгновенно (<100 мс) без перезагрузки страницы.
- **[Встроенные расширения Markdown:](./markdown)** Frontmatter, таблицы, подсветка синтаксиса... называйте как хотите. В частности, VitePress предоставляет множество расширенных возможностей для работы с блоками кода, что делает его идеальным для создания технической документации.
- **[Markdown с возможностями Vue:](./using-vue)** каждая Markdown-страница также является [однофайловым компонентом](https://ru.vuejs.org/guide/scaling-up/sfc.html) Vue, благодаря 100% синтаксической совместимости шаблона Vue с HTML. Вы можете внедрить интерактивность в статический контент, используя шаблонизаторы Vue или импортированные компоненты Vue.
## Производительность {#performance}
В отличие от многих традиционных ГСС, где каждая навигация приводит к полной перезагрузке страницы, сайт, созданный VitePress, обслуживает статический HTML при первом посещении, но становится [Одностраничным приложением](https://ru.wikipedia.org/wiki/%D0%9E%D0%B4%D0%BD%D0%BE%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5) (SPA) для последующей навигации по сайту. Эта модель, на наш взгляд, обеспечивает оптимальный баланс производительности:
- **Быстрая начальная загрузка**
При первом посещении любой страницы будет использоваться статичный, предварительно отрендеренный HTML для быстрой загрузки и оптимального SEO. Затем на страницу загружается пакет JavaScript, который превращает страницу в Vue SPA («гидратация»). Вопреки распространённому мнению о медленной гидратации SPA, этот процесс на самом деле чрезвычайно быстр благодаря высокой производительности Vue 3 и оптимизациям компилятора. По данным [PageSpeed Insights](https://pagespeed.web.dev/report?url=https%3A%2F%2Fvitepress.dev%2F), типичные сайты VitePress достигают почти идеальных показателей производительности даже на мобильных устройствах с низкой скоростью передачи данных.
- **Быстрая навигация после загрузки**
Что ещё более важно, модель SPA приводит к улучшению пользовательского опыта **после** первоначальной загрузки. Последующая навигация по сайту больше не будет приводить к полной перезагрузке страницы. Вместо этого содержимое входящей страницы будет получено и динамически обновлено. VitePress также автоматически выполняет предварительную выборку фрагментов страницы для ссылок, которые находятся в пределах области просмотра. В большинстве случаев навигация после загрузки будет ощущаться мгновенно.
- **Интерактивность без штрафов**
Для того чтобы динамические части Vue, встроенные в статический Markdown, могли работать в режиме гидратации, каждая страница Markdown обрабатывается как компонент Vue и компилируется в JavaScript. Это может показаться неэффективным, но компилятор Vue достаточно умён, чтобы разделить статическую и динамическую части, минимизируя как стоимость гидратации, так и размер полезной нагрузки. При первоначальной загрузке страницы статические части автоматически исключаются из полезной нагрузки JavaScript и пропускаются во время гидратации.
## Что насчёт VuePress? {#what-about-vuepress}
VitePress — это духовный наследник VuePress. Оригинальный VuePress был основан на Vue 2 и webpack. Благодаря Vue 3 и Vite под капотом, VitePress обеспечивает значительно лучший опыт разработки, лучшую производительность, более отточенную тему по умолчанию и более гибкий API для настройки.
Разница в API между VitePress и VuePress заключается в основном в тематическом оформлении и настройке. Если вы используете VuePress 1 с темой по умолчанию, то переход на VitePress будет относительно простым.
Также были приложены усилия для создания VuePress 2, который также поддерживает Vue 3 и Vite с большей совместимостью с VuePress 1. Однако поддерживать два генератора параллельно не представляется возможным, поэтому команда Vue решила сосредоточиться на VitePress как основном рекомендуемом генераторе статических сайтов в долгосрочной перспективе.

@ -0,0 +1,60 @@
---
layout: home
title: VitePress
titleTemplate: Генератор статических сайтов на основе Vite и Vue
hero:
name: VitePress
text: Генератор статических сайтов на основе Vite и Vue
tagline: Из Markdown в красивую документацию за считанные минуты
actions:
- theme: brand
text: Что такое VitePress?
link: /ru/guide/what-is-vitepress
- theme: alt
text: Первые шаги
link: /ru/guide/getting-started
- theme: alt
text: GitHub
link: https://github.com/vuejs/vitepress
image:
src: /vitepress-logo-large.webp
alt: VitePress
features:
- icon: 📝
title: Сосредоточьтесь на своем контенте
details: Легко создавайте красивые сайты с документацией, используя только Markdown.
- icon: <svg xmlns="http://www.w3.org/2000/svg" width="30" viewBox="0 0 256 256.32"><defs><linearGradient id="a" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"/><stop offset="100%" stop-color="#BD34FE"/></linearGradient><linearGradient id="b" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"/><stop offset="8.333%" stop-color="#FFDD35"/><stop offset="100%" stop-color="#FFA800"/></linearGradient></defs><path fill="url(#a)" d="M255.153 37.938 134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"/><path fill="url(#b)" d="M185.432.063 96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028 72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"/></svg>
title: Наслаждайтесь опытом разработчиков Vite
details: Мгновенный запуск сервера, молниеносные горячие обновления и использование плагинов экосистемы Vite.
- icon: <svg xmlns="http://www.w3.org/2000/svg" width="30" viewBox="0 0 256 220.8"><path fill="#41B883" d="M204.8 0H256L128 220.8 0 0h97.92L128 51.2 157.44 0h47.36Z"/><path fill="#41B883" d="m0 0 128 220.8L256 0h-51.2L128 132.48 50.56 0H0Z"/><path fill="#35495E" d="M50.56 0 128 133.12 204.8 0h-47.36L128 51.2 97.92 0H50.56Z"/></svg>
title: Настройка с помощью Vue
details: Используйте синтаксис Vue и компоненты прямо в Markdown или создавайте собственные темы с помощью Vue.
- icon: 🚀
title: Быстрый запуск веб-сайтов
details: Быстрая начальная загрузка с помощью статического HTML, быстрая навигация после загрузки с помощью маршрутизации на стороне клиента.
---
<style>
:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(120deg, #bd34fe 30%, #41d1ff);
--vp-home-hero-image-background-image: linear-gradient(-45deg, #bd34fe 50%, #47caff 50%);
--vp-home-hero-image-filter: blur(44px);
}
@media (min-width: 640px) {
:root {
--vp-home-hero-image-filter: blur(56px);
}
}
@media (min-width: 960px) {
:root {
--vp-home-hero-image-filter: blur(68px);
}
}
</style>

@ -0,0 +1,74 @@
# Интерфейс командной строки {#command-line-interface}
## `vitepress dev` {#vitepress-dev}
Запуск сервера разработки VitePress, с использованием указанного каталога в качестве корневого. По умолчанию используется текущий каталог. Команду `dev` также можно опустить при работе в текущем каталоге.
### Использование {#usage}
```sh
# запуск в текущем каталоге, опускаем `dev`
vitepress
# запуск в подкаталоге
vitepress dev [root]
```
### Параметры {#options}
| Параметр | Описание |
| --------------- | ------------------------------------------------------------------------------ |
| `--open [path]` | Открытие браузера при запуске (`boolean \| string`) |
| `--port <port>` | Номер порта (`number`) |
| `--base <path>` | Публичный базовый путь (по умолчанию: `/`) (`string`) |
| `--cors` | Включить CORS |
| `--strictPort` | Выйти, если указанный порт уже используется (`boolean`) |
| `--force` | Заставить оптимизатор игнорировать кэш и повторно объединять файлы (`boolean`) |
## `vitepress build` {#vitepress-build}
Создание производственной сборки текущего сайта VitePress.
### Использование {#usage-1}
```sh
vitepress build [root]
```
### Параметры {#options-1}
| Параметр | Описание |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| `--mpa` (экспериментально) | Сборка в режиме [MPA](../guide/mpa-mode) без гидратации на стороне клиента (`boolean`) |
| `--base <path>` | Публичный базовый путь (по умолчанию: `/`) (`string`) |
| `--target <target>` | Транспилировать цель (по умолчанию: `"modules"`) (`string`) |
| `--outDir <dir>` | Выходной каталог относительно **cwd** (по умолчанию: `<root>/.vitepress/dist`) (`string`) |
| `--minify [minifier]` | Включить/выключить минификацию или задать используемый минификатор (по умолчанию: `"esbuild"`) (`boolean \| "terser" \| "esbuild"`) |
| `--assetsInlineLimit <number>` | Статический встроенный порог ресурса base64 в байтах (по умолчанию: `4096`) (`number`) |
## `vitepress preview` {#vitepress-preview}
Локальный предварительный просмотр производственной сборки.
### Использование {#usage-2}
```sh
vitepress preview [root]
```
### Параметры {#options-2}
| Параметр | Описание |
| --------------- | ----------------------------------------------------- |
| `--base <path>` | Публичный базовый путь (по умолчанию: `/`) (`string`) |
| `--port <port>` | Номер порта (`number`) |
## `vitepress init` {#vitepress-init}
Запуск [Мастера настройки](../guide/getting-started#setup-wizard) в текущем каталоге.
### Использование {#usage-3}
```sh
vitepress init
```

@ -0,0 +1,72 @@
# Значки {#badge}
С помощью значков удобно добавлять статус к заголовкам. Например, может быть полезно указать тип раздела или поддерживаемую версию.
## Использование {#usage}
Вы можете использовать компонент `Badge`, который доступен глобально.
```html
### Заголовок <Badge type="info" text="по умолчанию" /> ### Заголовок
<Badge type="tip" text="^1.9.0" /> ### Заголовок
<Badge type="warning" text="beta" /> ### Заголовок
<Badge type="danger" text="осторожно" />
```
Приведённый выше код даёт такой результат:
### Заголовок <Badge type="info" text="по умолчанию" /> {#title}
### Заголовок <Badge type="tip" text="^1.9.0" /> {#title-1}
### Заголовок <Badge type="warning" text="beta" /> {#title-2}
### Заголовок <Badge type="danger" text="осторожно" /> {#title-3}
## Дочерние элементы {#custom-children}
`<Badge>` принимает параметр `children`, который будет отображаться внутри значка.
```html
### Заголовок <Badge type="info">вложенный элемент</Badge>
```
### Заголовок <Badge type="info">вложенный элемент</Badge>
## Настройка стиля значков {#customize-type-color}
Вы можете настроить стиль значков, переопределив переменные CSS. Ниже приведены значения по умолчанию:
```css
:root {
--vp-badge-info-border: transparent;
--vp-badge-info-text: var(--vp-c-text-2);
--vp-badge-info-bg: var(--vp-c-default-soft);
--vp-badge-tip-border: transparent;
--vp-badge-tip-text: var(--vp-c-brand-1);
--vp-badge-tip-bg: var(--vp-c-brand-soft);
--vp-badge-warning-border: transparent;
--vp-badge-warning-text: var(--vp-c-warning-1);
--vp-badge-warning-bg: var(--vp-c-warning-soft);
--vp-badge-danger-border: transparent;
--vp-badge-danger-text: var(--vp-c-danger-1);
--vp-badge-danger-bg: var(--vp-c-danger-soft);
}
```
## `<Badge>` {#badge-1}
Компонент `<Badge>` принимает следующие параметры:
```ts
interface Props {
// Когда передается `<slot>`, это значение игнорируется.
text?: string
// По умолчанию: `tip`.
type?: 'info' | 'tip' | 'warning' | 'danger'
}
```

@ -0,0 +1,22 @@
# Carbon Ads {#carbon-ads}
В VitePress встроена встроенная поддержка [Carbon Ads](https://www.carbonads.net/). Определив в конфиге учётные данные Carbon Ads, VitePress будет отображать рекламу на странице.
```js
export default {
themeConfig: {
carbonAds: {
code: 'код-рекламы',
placement: 'место-размещения-рекламы'
}
}
}
```
Эти значения используются для вызова сценария carbon CDN, как показано ниже:
```js
;`//cdn.carbonads.com/carbon.js?serve=${code}&placement=${placement}`
```
Чтобы узнать больше о конфигурации Carbon Ads, посетите [веб-сайт Carbon Ads](https://www.carbonads.net/).

@ -0,0 +1,452 @@
# Настройка темы по умолчанию {#default-theme-config}
Конфигурация темы позволяет настроить её под себя. Вы можете настроить тему с помощью опции `themeConfig` в файле конфигурации:
```ts
export default {
lang: 'ru-RU',
title: 'VitePress',
description: 'Генератор статического сайта на базе Vite и Vue.',
// Конфигурации, связанные с темой.
themeConfig: {
logo: '/logo.svg',
nav: [...],
sidebar: { ... }
}
}
```
**Параметры, описанные на этой странице, применимы только к теме по умолчанию.** Разные темы предполагают разные конфигурации темы. При использовании пользовательской темы объект конфигурации темы будет передан теме, чтобы она могла определить условное поведение на его основе.
## i18nRouting {#i18nrouting}
- Тип: `boolean`
При смене локали на `ru` URL изменится с `/foo` (или `/en/foo/`) на `/ru/foo`. Вы можете отключить это поведение, установив для параметра `themeConfig.i18nRouting` значение `false`.
## logo {#logo}
- Тип: `ThemeableImage`
Файл логотипа для отображения в навигационной панели, прямо перед заголовком сайта. Принимает строку пути или объект, чтобы установить другой логотип для светлого/тёмного режима.
```ts
export default {
themeConfig: {
logo: '/logo.svg'
}
}
```
```ts
type ThemeableImage =
| string
| { src: string; alt?: string }
| { light: string; dark: string; alt?: string }
```
## siteTitle {#sitetitle}
- Тип: `string | false`
Вы можете настроить этот элемент для замены стандартного заголовка сайта (`title` в конфигурации приложения) в nav. При установке значения `false` заголовок в панели навигации будет отключен. Пригодится, если у вас есть `logo`, который уже содержит текст названия сайта.
```ts
export default {
themeConfig: {
siteTitle: 'Привет, мир'
}
}
```
## nav {#nav}
- Тип: `NavItem`
Конфигурация для пункта навигационного меню. Подробнее в главе [Тема по умолчанию: Навигация](./default-theme-nav#navigation-links).
```ts
export default {
themeConfig: {
nav: [
{ text: 'Руководство', link: '/guide' },
{
text: 'Выпадающее меню',
items: [
{ text: 'Пункт A', link: '/item-1' },
{ text: 'Пункт B', link: '/item-2' },
{ text: 'Пункт C', link: '/item-3' }
]
}
]
}
}
```
```ts
type NavItem = NavItemWithLink | NavItemWithChildren
interface NavItemWithLink {
text: string
link: string
activeMatch?: string
target?: string
rel?: string
noIcon?: boolean
}
interface NavItemChildren {
text?: string
items: NavItemWithLink[]
}
interface NavItemWithChildren {
text?: string
items: (NavItemChildren | NavItemWithLink)[]
activeMatch?: string
}
```
## sidebar {#sidebar}
- Тип: `Sidebar`
Конфигурация для пунктов меню боковой панели. Подробнее в главе [Тема по умолчанию: Сайдбар](./default-theme-sidebar).
```ts
export default {
themeConfig: {
sidebar: [
{
text: 'Руководство',
items: [
{ text: 'Введение', link: '/introduction' },
{ text: 'Первые шаги', link: '/getting-started' },
...
]
}
]
}
}
```
```ts
export type Sidebar = SidebarItem[] | SidebarMulti
export interface SidebarMulti {
[path: string]: SidebarItem[]
}
export type SidebarItem = {
/**
* Текстовая метка элемента
*/
text?: string
/**
* Ссылка на элемент
*/
link?: string
/**
* Потомки элемента
*/
items?: SidebarItem[]
/**
* Если не указано, группа не будет сворачиваться
*
* Если `true`, то группа будет сворачиваться и разворачиваться по умолчанию
*
* Если `false`, группа сворачивается, но по умолчанию разворачивается
*/
collapsed?: boolean
}
```
## aside {#aside}
- Тип: `boolean | 'left'`
- По умолчанию: `true`
- Можно переопределить для каждой страницы с помощью [метаданных](./frontmatter-config#aside)
Установка этого значения в `false` предотвращает отрисовку контейнера сайдбара.\
Установка этого значения в `true` приведёт к отображению сайдбара справа.\
Установка этого значения в `left` приведёт к отображению сайдбара слева.
Если вы хотите отключить его для всех режимов просмотра, используйте `aside: false`.
## outline {#outline}
- Тип: `Outline | Outline['level'] | false`
- Уровень можно переопределить для каждой страницы с помощью [метаданных](./frontmatter-config#outline)
Установка этого значения в `false` предотвращает отрисовку оглавления. Для получения более подробной информации обратитесь к этому интерфейсу:
```ts
interface Outline {
/**
* Уровни заголовков, которые будут отображаться в оглавлении.
* Одиночное число означает, что будут отображаться только заголовки этого уровня.
* Если передается кортеж, то первое число — это минимальный уровень, а второе — максимальный.
* `'deep'` то же самое, что `[2, 6]`, что означает, что будут отображены все заголовки от `<h2>` до `<h6>`.
*
* @default 2
*/
level?: number | [number, number] | 'deep'
/**
* Заголовок, который будет отображаться в оглавлении.
*
* @default 'На этой странице'
*/
label?: string
}
```
## socialLinks {#sociallinks}
- Тип: `SocialLink[]`
Вы можете задать эту опцию, чтобы показывать ссылки на ваши социальные аккаунты с помощью иконок в панели навигации.
```ts
export default {
themeConfig: {
socialLinks: [
{ icon: 'github', link: 'https://github.com/vuejs/vitepress' },
{ icon: 'twitter', link: '...' },
// Вы также можете добавить пользовательские иконки, передав SVG в виде строки:
{
icon: {
svg: '<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Dribbble</title><path d="M12...6.38z"/></svg>'
},
link: '...',
// Вы также можете включить пользовательский ярлык для доступности (необязательно, но рекомендуется):
ariaLabel: 'классная ссылка'
}
]
}
}
```
```ts
interface SocialLink {
icon: SocialLinkIcon
link: string
ariaLabel?: string
}
type SocialLinkIcon =
| 'discord'
| 'facebook'
| 'github'
| 'instagram'
| 'linkedin'
| 'mastodon'
| 'npm'
| 'slack'
| 'twitter'
| 'x'
| 'youtube'
| { svg: string }
```
## footer {#footer}
- Тип: `Footer`
- Можно переопределить для каждой страницы с помощью [метаданных](./frontmatter-config#footer)
Настройка подвала. Вы можете разместить в подвале сообщение или текст об авторских правах, однако он будет отображаться только в том случае, если страница не содержит боковой панели. Это объясняется соображениями дизайна.
```ts
export default {
themeConfig: {
footer: {
message: 'Опубликовано под лицензией MIT.',
copyright: '© 2019 настоящее время, Эван Ю'
}
}
}
```
```ts
export interface Footer {
message?: string
copyright?: string
}
```
## editLink {#editlink}
- Тип: `EditLink`
- Можно переопределить для каждой страницы с помощью [метаданных](./frontmatter-config#editlink)
Ссылка для редактирования позволяет отобразить ссылку для редактирования страницы на сервисах управления Git, таких как GitHub или GitLab. См. секцию [Тема по умолчанию: Ссылка для редактирования](./default-theme-edit-link) для получения более подробной информации.
```ts
export default {
themeConfig: {
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
text: 'Редактировать эту страницу на GitHub'
}
}
}
```
```ts
export interface EditLink {
pattern: string
text?: string
}
```
## lastUpdated {#lastupdated}
- Тип: `LastUpdatedOptions`
Позволяет настраивать текст и формат даты последнего обновления.
```ts
export default {
themeConfig: {
lastUpdated: {
text: 'Обновлено',
formatOptions: {
dateStyle: 'full',
timeStyle: 'medium'
}
}
}
}
```
```ts
export interface LastUpdatedOptions {
/**
* @default 'Last updated'
*/
text?: string
/**
* @default
* { dateStyle: 'short', timeStyle: 'short' }
*/
formatOptions?: Intl.DateTimeFormatOptions & { forceLocale?: boolean }
}
```
## algolia {#algolia}
- Тип: `AlgoliaSearch`
Опция для поддержки поиска на вашем сайте документации с помощью [Algolia DocSearch](https://docsearch.algolia.com/docs/what-is-docsearch). Подробнее в главе [Тема по умолчанию: Поиск](./default-theme-search)
```ts
export interface AlgoliaSearchOptions extends DocSearchProps {
locales?: Record<string, Partial<DocSearchProps>>
}
```
Посмотреть все доступные опции можно [здесь](https://github.com/vuejs/vitepress/blob/main/types/docsearch.d.ts).
## carbonAds {#carbon-ads}
- Тип: `CarbonAdsOptions`
Возможность отображения [Carbon Ads](https://www.carbonads.net/).
```ts
export default {
themeConfig: {
carbonAds: {
code: 'код-рекламы',
placement: 'место-размещения-рекламы'
}
}
}
```
```ts
export interface CarbonAdsOptions {
code: string
placement: string
}
```
Подробнее в главе [Тема по умолчанию: Carbon Ads](./default-theme-carbon-ads)
## docFooter {#docfooter}
- Тип: `DocFooter`
Можно использовать для настройки текста, отображаемого над ссылками на предыдущую и следующую страницы. Полезно, если вы не пишете документы только на английском языке. Также можно использовать для глобального отключения подобных ссылок. Если вы хотите выборочно включить/выключить эти ссылки на отдельной странице, воспользуйтесь [метаданными](./default-theme-prev-next-links).
```ts
export default {
themeConfig: {
docFooter: {
prev: 'Предыдущая страница',
next: 'Следующая страница'
}
}
}
```
```ts
export interface DocFooter {
prev?: string | false
next?: string | false
}
```
## darkModeSwitchLabel {#darkmodeswitchlabel}
- Тип: `string`
- По умолчанию: `Appearance`
Можно использовать для настройки надписи переключателя тёмного режима. Этот ярлык отображается только в мобильном представлении.
## lightModeSwitchTitle {#lightmodeswitchtitle}
- Тип: `string`
- По умолчанию: `Switch to light theme`
Может использоваться для настройки заголовка переключателя светлого режима, который появляется при наведении курсора.
## darkModeSwitchTitle {#darkmodeswitchtitle}
- Тип: `string`
- По умолчанию: `Switch to dark theme`
Можно использовать для настройки заголовка переключателя тёмного режима, который появляется при наведении курсора.
## sidebarMenuLabel {#sidebarmenulabel}
- Тип: `string`
- По умолчанию: `Menu`
Может использоваться для настройки метки бокового меню. Эта метка отображается только в мобильном представлении.
## returnToTopLabel {#returntotoplabel}
- Тип: `string`
- По умолчанию: `Return to top`
Может использоваться для настройки метки кнопки возврата наверх. Эта метка отображается только в мобильном представлении.
## langMenuLabel {#langmenulabel}
- Тип: `string`
- По умолчанию: `Change language`
Можно использовать для настройки aria-метки кнопки переключения языка в панели навигации. Это используется только в том случае, если вы используете [i18n](../guide/i18n).
## externalLinkIcon {#externallinkicon}
- Тип: `boolean`
- По умолчанию: `false`
Отображать ли значок внешней ссылки рядом с внешними ссылками в Markdown.

@ -0,0 +1,60 @@
# Ссылка для редактирования {#edit-link}
## Настройка в файле конфигурации {#site-level-config}
Ссылка на редактирование позволяет отобразить ссылку для редактирования страницы на сервисах управления Git, таких как GitHub или GitLab. Чтобы включить её, добавьте опции `themeConfig.editLink` в свой конфиг:
```js
export default {
themeConfig: {
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path'
}
}
}
```
Параметр `pattern` задает структуру URL для ссылки, а `:path` будет заменён на путь к странице.
Вы также можете поместить чистую функцию, которая принимает [`PageData`](./runtime-api#usedata) в качестве аргумента и возвращает строку URL.
```js
export default {
themeConfig: {
editLink: {
pattern: ({ filePath }) => {
if (filePath.startsWith('packages/')) {
return `https://github.com/acme/monorepo/edit/main/${filePath}`
} else {
return `https://github.com/acme/monorepo/edit/main/docs/${filePath}`
}
}
}
}
}
```
Она не должна иметь побочных эффектов или доступа к чему-либо за пределами своей области, поскольку будет сериализована и выполнена в браузере.
По умолчанию это добавит текст ссылки «Редактировать страницу» в нижней части документа. Вы можете настроить этот текст, определив опцию `text`.
```js
export default {
themeConfig: {
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
text: 'Редактировать эту страницу на GitHub'
}
}
}
```
## Настройка в метаданных {#frontmatter-config}
Эту ссылку можно отключить на конкретной странице с помощью опции `editLink` в метаданных:
```yaml
---
editLink: false
---
```

@ -0,0 +1,55 @@
# Подвал {#footer}
VitePress будет отображать блок подвала внизу страницы, если присутствует объект `themeConfig.footer`.
```ts
export default {
themeConfig: {
footer: {
message: 'Опубликовано под лицензией MIT.',
copyright: '© 2019 настоящее время, Эван Ю'
}
}
}
```
```ts
export interface Footer {
// Сообщение, отображаемое прямо перед копирайтом.
message?: string
// Уведомление об авторских правах.
copyright?: string
}
```
Приведённая выше конфигурация также поддерживает строки HTML. Так, например, если вы хотите разместить в подвале несколько ссылок, можно настроить конфигурацию следующим образом:
```ts
export default {
themeConfig: {
footer: {
message:
'Опубликовано под <a href="https://github.com/vuejs/vitepress/blob/main/LICENSE">лицензией MIT</a>.',
copyright:
'© 2019 настоящее время, <a href="https://github.com/yyx990803">Эван Ю</a>'
}
}
}
```
::: warning ПРЕДУПРЕЖДЕНИЕ
В `message` и `copyright` можно использовать только встроенные элементы, поскольку они отображаются внутри элемента `<p>`. Если вы хотите добавить блочные элементы, рассмотрите возможность использования слота [`layout-bottom`](../guide/extending-default-theme#layout-slots).
:::
Обратите внимание, что подвал не будет отображаться, если виден [Сайдбар](./default-theme-sidebar).
## Настройка в метаданных {#frontmatter-config}
Отображение подвала можно отключить на конкретной странице с помощью опции `footer` в метаданных:
```yaml
---
footer: false
---
```

@ -0,0 +1,196 @@
# Главная страница {#home-page}
Тема VitePress по умолчанию предоставляет макет главной страницы, который вы также можете увидеть на [главной странице этого сайта](../). Вы можете использовать его на любой из своих страниц, указав `layout: home` в [метаданных](./frontmatter-config) страницы.
```yaml
---
layout: home
---
```
Однако сам по себе этот вариант мало что даст. Вы можете добавить несколько различных готовых «секций» на главную страницу, установив дополнительные опции, такие как `hero` и `features`.
## Секция `hero` {#hero-section}
Секция `hero` находится в верхней части главной страницы. Вот как можно её настроить:
```yaml
---
layout: home
hero:
name: VitePress
text: Генератор статических сайтов на основе Vite и Vue.
tagline: Lorem ipsum...
image:
src: /logo.png
alt: VitePress
actions:
- theme: brand
text: Начать
link: /guide/what-is-vitepress
- theme: alt
text: Посмотреть на GitHub
link: https://github.com/vuejs/vitepress
---
```
```ts
interface Hero {
// Строка, отображаемая поверх `text`. Поставляется в фирменном цвете и,
// как ожидается, будет короткой — например, название продукта
name?: string
// Основной текст секции. Будет использоваться внутри тега `h1`
text: string
// Заголовок, отображаемый под `text`
tagline?: string
// Изображение отображается рядом с `text` и `tagline`
image?: ThemeableImage
// Кнопки действий для отображения в секции
actions?: HeroAction[]
}
type ThemeableImage =
| string
| { src: string; alt?: string }
| { light: string; dark: string; alt?: string }
interface HeroAction {
// Цветовая тема кнопки. По умолчанию принимает значение `brand`.
theme?: 'brand' | 'alt'
// Метка кнопки.
text: string
// Ссылка назначения кнопки.
link: string
// Атрибут цели ссылки.
target?: string
// Атрибут rel ссылки.
rel?: string
}
```
### Настройка цвета заголовка секции {#customizing-the-name-color}
VitePress использует фирменный цвет (`--vp-c-brand-1`) для атрибута `name` в секции `hero`. Однако вы можете настроить этот цвет, переопределив переменную `--vp-home-hero-name-color`.
```css
:root {
--vp-home-hero-name-color: blue;
}
```
Также вы можете настроить его ещё больше, комбинируя `--vp-home-hero-name-background`, чтобы придать `name` градиентный цвет.
```css
:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(
120deg,
#bd34fe,
#41d1ff
);
}
```
## Секция `features` {#features-section}
В секции `features` можно перечислить любое количество функций, которые вы хотели бы показать сразу после секции `hero`. Чтобы настроить её, передайте опцию `features` в метаданных страницы.
Для каждой функции можно указать иконку, который может быть эмодзи или любым другим изображением. Если настраиваемая иконка представляет собой изображение (svg, png, jpeg...), вы должны предоставить ей соответствующую ширину и высоту. При необходимости можно указать описание, собственный размер, а также варианты для тёмной и светлой темы.
```yaml
---
layout: home
features:
- icon: 🛠️
title: Просто и минималистично, всегда
details: Lorem ipsum...
- icon:
src: /cool-feature-icon.svg
title: Ещё одна интересная функция
details: Lorem ipsum...
- icon:
dark: /dark-feature-icon.svg
light: /light-feature-icon.svg
title: Ещё одна интересная функция
details: Lorem ipsum...
---
```
```ts
interface Feature {
// Иконка
icon?: FeatureIcon
// Заголовок фичи
title: string
// Описание фичи
details: string
// Ссылка при нажатии на компонент функции. Ссылка может быть как внутренней, так и внешней.
//
// например, `guide/reference/default-theme-home-page` или `https://example.com`
link?: string
// Текст ссылки, который будет отображаться внутри компонента функции. Лучше всего использовать с опцией `link`.
//
// например, `Узнать подробнее`, `Посетить страницу` и т. д.
linkText?: string
// Атрибут rel для опции `link`
//
// например, `external`
rel?: string
// Атрибут target для опции `link`
target?: string
}
type FeatureIcon =
| string
| { src: string; alt?: string; width?: string; height: string }
| {
light: string
dark: string
alt?: string
width?: string
height: string
}
```
## Содержимое Markdown {#markdown-content}
Вы можете добавить дополнительный контент на главную страницу вашего сайта, просто добавив Markdown под разделителем `---`.
````md
---
layout: home
hero:
name: VitePress
text: Генератор статических сайтов на основе Vite и Vue.
---
## Начало работы
Вы можете начать использовать VitePress прямо сейчас, используя `npx`!
```sh
npm init
npx vitepress init
```
````
::: info ПРИМЕЧАНИЕ
VitePress не всегда автоматически стилизовал дополнительный контент страницы с макетом `layout: home`. Чтобы вернуться к старому поведению, добавьте `markdownStyles: false` в метаданных.
:::

@ -0,0 +1,27 @@
# Последнее обновление {#last-updated}
Время последнего обновления содержимого будет отображаться в правом нижнем углу страницы. Чтобы включить его, добавьте опцию `lastUpdated` в свой конфиг.
::: tip Совет
Чтобы увидеть обновленное время, необходимо зафиксировать файл Markdown.
:::
## Настройка в файле конфигурации {#site-level-config}
```js
export default {
lastUpdated: true
}
```
## Настройка в метаданных {#frontmatter-config}
Эту информацию можно отключить на конкретной странице с помощью опции `lastUpdated` в метаданных:
```yaml
---
lastUpdated: false
---
```
Также смотрите [Тема по умолчанию: `lastUpdated`](./default-theme-config#lastupdated) для получения более подробной информации. Любое истинное значение на уровне темы также включит функцию, если только она не будет явно отключена на уровне сайта или страницы.

@ -0,0 +1,62 @@
# Макет {#layout}
Вы можете выбрать макет страницы, установив опцию `layout` в [метаданных](./frontmatter-config). Изначально есть 3 макета: `doc`, `page` и `home`. Если ничего не указано, то страница будет использовать макет `doc`.
```yaml
---
layout: doc
---
```
## Макет `doc` {#doc-layout}
Вариант `doc` — это макет по умолчанию, который стилизует всё содержимое Markdown в виде «документации». Он работает, оборачивая весь контент в CSS-класс `vp-doc` и применяя стили к вложенным элементам.
Почти все общие элементы, такие как `p` или `h2`, получают специальную стилизацию. Поэтому имейте в виду, что если вы добавите какой-либо пользовательский HTML внутри Markdown-контента, то он также будет подвержен влиянию этих стилей.
Кроме того, в нём предусмотрены специальные функции, перечисленные ниже. Эти функции включены только в данном макете.
- Ссылка «Редактировать»
- Ссылки предыдущая/следующая
- Оглавление
- Реклама [Carbon Ads](./default-theme-carbon-ads)
## Макет `page` {#page-layout}
Вариант `page` сгенерирует «пустую страницу». Markdown всё равно будет разобран, и все [расширения Markdown](../guide/markdown) будут работать так же, как и с макетом `doc`, но никаких стилей по умолчанию применено не будет.
Макет `page` позволит вам оформить всё самостоятельно, без влияния темы VitePress на разметку. Это удобно, когда вы хотите создать свою собственную страницу.
Обратите внимание, что даже при таком раскладе сайдбар всё равно будет отображаться, если у страницы есть соответствующая конфигурация сайдбара.
## Макет `home` {#home-layout}
Вариант `home` сгенерирует шаблонную «домашнюю страницу». В этом макете вы можете установить дополнительные параметры, такие как `hero` и `features`, для дальнейшей настройки контента. Посетите секцию [Тема по умолчанию: Домашняя страница](./default-theme-home-page) для получения более подробной информации.
## Без макета {#no-layout}
Если вам не нужен макет, вы можете указать `layout: false` в метаданных. Этот параметр полезен, если вам нужна полностью настраиваемая целевая страница (по умолчанию без сайдбара, панели навигации или подвала).
## Свой макет {#custom-layout}
Вы также можете использовать собственный макет:
```md
---
layout: foo
---
```
Будет выполнен поиск компонента с именем `foo`, зарегистрированного в контексте. Например, вы можете зарегистрировать свой компонент глобально в `.vitepress/theme/index.ts`:
```ts
import DefaultTheme from 'vitepress/theme'
import Foo from './Foo.vue'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component('foo', Foo)
}
}
```

@ -0,0 +1,162 @@
# Навигация {#nav}
Ключ `nav` в конфигурации — это панель навигации, отображаемая в верхней части страницы. Она содержит заголовок сайта, ссылки глобального меню и т. д.
## Название и логотип сайта {#site-title-and-logo}
По умолчанию навигация отображает название сайта, ссылаясь на значение [`config.title`](./site-config#title). Если вы хотите изменить то, что отображается в панели навигации, задайте пользовательский текст в опции `themeConfig.siteTitle`.
```js
export default {
themeConfig: {
siteTitle: 'Мой заголовок'
}
}
```
Если у вас есть логотип для вашего сайта, вы можете отобразить его, передав путь к изображению. Вы должны поместить логотип непосредственно в директорию `public` и указать абсолютный путь к нему.
```js
export default {
themeConfig: {
logo: '/my-logo.svg'
}
}
```
При добавлении логотипа он отображается вместе с названием сайта. Если вам нужен только логотип и вы хотите скрыть текст заголовка сайта, установите `false` для параметра `SiteTitle`.
```js
export default {
themeConfig: {
logo: '/my-logo.svg',
siteTitle: false
}
}
```
Вы также можете передать объект в качестве логотипа, если хотите добавить атрибут `alt` или настроить его в зависимости от тёмного/светлого режима. Подробности смотрите в [`themeConfig.logo`](./default-theme-config#logo).
## Навигационные ссылки {#navigation-links}
Вы можете определить опцию `themeConfig.nav`, чтобы добавить ссылки в панель навигации:
```js
export default {
themeConfig: {
nav: [
{ text: 'Руководство', link: '/guide' },
{ text: 'Настройка', link: '/config' },
{ text: 'Изменения', link: 'https://github.com/...' }
]
}
}
```
`text` — это текст, отображаемый в навигации, а `link` — это ссылка, на которую будет осуществлён переход при нажатии на текст. Для ссылки задайте путь к фактическому файлу без префикса `.md` и всегда начинайте с `/`.
Навигационные ссылки также могут быть выпадающими меню. Для этого установите ключ `items` вместо ключа `link`:
```js
export default {
themeConfig: {
nav: [
{ text: 'Руководство', link: '/guide' },
{
text: 'Выпадающее меню',
items: [
{ text: 'Пункт A', link: '/item-1' },
{ text: 'Пункт B', link: '/item-2' },
{ text: 'Пункт C', link: '/item-3' }
]
}
]
}
}
```
Обратите внимание, что заголовок выпадающего меню (`Выпадающее меню` в примере выше) не может иметь свойство `link`, так как он становится кнопкой для открытия выпадающего диалога.
Вы можете добавить «секции» в пункты выпадающего меню, передавая больше вложенных элементов:
```js
export default {
themeConfig: {
nav: [
{ text: 'Руководство', link: '/guide' },
{
text: 'Выпадающее меню',
items: [
{
// Заголовок секции
text: 'Секция A',
items: [
{ text: 'Пункт A в секции A', link: '...' },
{ text: 'Пункт B в секции A', link: '...' }
]
}
]
},
{
text: 'Выпадающее меню',
items: [
{
// Заголовок можно опустить
items: [
{ text: 'Пункт A в секции A', link: '...' },
{ text: 'Пункт B в секции A', link: '...' }
]
}
]
}
]
}
}
```
### Настройка «активного» состояния ссылки {#customize-link-s-active-state}
Пункты меню навигации будут выделены, если текущая страница находится под соответствующим путём. Если вы хотите настроить сопоставление путей, определите свойство `activeMatch` и регулярное выражение в качестве строкового значения.
```js
export default {
themeConfig: {
nav: [
// Эта ссылка получает активное состояние, когда пользователь
// переходит по пути `/config/`.
{
text: 'Руководство',
link: '/guide',
activeMatch: '/config/'
}
]
}
}
```
::: warning ПРЕДУПРЕЖДЕНИЕ
Ожидается, что `activeMatch` будет регулярным выражением, но вы должны определить его как строку. Мы не можем использовать здесь реальный объект RegExp, потому что он не сериализуется во время сборки.
:::
### Настройка атрибутов «target» и «rel» {#customize-link-s-target-and-rel-attributes}
По умолчанию VitePress автоматически определяет атрибуты `target` и `rel` в зависимости от того, является ли ссылка внешней. Но при желании их можно настроить и вручную.
```js
export default {
themeConfig: {
nav: [
{
text: 'Товары',
link: 'https://www.thegithubshop.com/',
target: '_self',
rel: 'sponsored'
}
]
}
}
```
## Социальные ссылки {#social-links}
См. [`socialLinks`](./default-theme-config#sociallinks).

@ -0,0 +1,43 @@
# Предыдущая и следующая страницы {#prev-next-links}
Вы можете настроить текст и ссылку для предыдущей и следующей страниц (отображаются в нижней части страницы). Это полезно, если вы хотите, чтобы текст отличался от того, что находится в сайдбаре. Кроме того, вы можете счесть полезным отключить подвал или ссылку на страницу, которая не включена в сайдбар.
## prev {#prev}
- Тип: `string | false | { text?: string; link?: string }`
- Подробности:
Указывает текст/ссылку, который должен отображаться при переходе на предыдущую страницу. Если вы не зададите это в метаданных, текст/ссылка будет определяться из конфигурации сайдбара.
- Примеры:
- Для настройки только текста:
```yaml
---
prev: 'Начать | Markdown'
---
```
- Для настройки текста и ссылки:
```yaml
---
prev:
text: 'Markdown'
link: '/guide/markdown'
---
```
- Для скрытия предыдущей страницы:
```yaml
---
prev: false
---
```
## next {#next}
Аналогично параметру `prev`, но для следующей страницы.

@ -0,0 +1,382 @@
---
outline: deep
---
# Поиск {#search}
## Локальный поиск {#local-search}
VitePress поддерживает нечёткий полнотекстовый поиск с использованием внутрибраузерного индекса благодаря [MiniSearch](https://github.com/lucaong/minisearch/). Чтобы включить эту функцию, просто установите значение `'local'` для опции `themeConfig.search.provider` в файле `.vitepress/config.ts`:
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
search: {
provider: 'local'
}
}
})
```
Пример результата:
![скриншот модального окна поиска](/search.png)
В качестве альтернативы можно использовать [Algolia DocSearch](#algolia-search) или некоторые плагины сообщества, например <https://www.npmjs.com/package/vitepress-plugin-search> или <https://www.npmjs.com/package/vitepress-plugin-pagefind>.
### i18n {#local-search-i18n}
Вы можете использовать подобную конфигурацию для использования многоязычного поиска:
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
search: {
provider: 'local',
options: {
locales: {
ru: {
translations: {
button: {
buttonText: 'Поиск',
buttonAriaLabel: 'Поиск'
},
modal: {
noResultsText: 'Нет результатов для',
resetButtonTitle: 'Сбросить поиск',
footer: {
selectText: 'выбрать',
navigateText: 'перейти'
}
}
}
}
}
}
}
}
})
```
### Параметры MiniSearch {#minisearch-options}
Вы можете настроить MiniSearch следующим образом:
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
search: {
provider: 'local',
options: {
miniSearch: {
/**
* @type {Pick<import('minisearch').Options, 'extractField' | 'tokenize' | 'processTerm'>}
*/
options: {
/* ... */
},
/**
* @type {import('minisearch').SearchOptions}
* @default
* { fuzzy: 0.2, prefix: true, boost: { title: 4, text: 2, titles: 1 } }
*/
searchOptions: {
/* ... */
}
}
}
}
}
})
```
Подробнее в [документации MiniSearch](https://lucaong.github.io/minisearch/classes/MiniSearch.MiniSearch.html).
### Пользовательский рендерер содержимого {#custom-content-renderer}
Вы можете настроить функцию, используемую для отображения содержимого в формате Markdown перед его индексацией:
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
search: {
provider: 'local',
options: {
/**
* @param {string} src
* @param {import('vitepress').MarkdownEnv} env
* @param {import('markdown-it')} md
*/
_render(src, env, md) {
// возвращаем html
}
}
}
}
})
```
Эта функция будет очищена от данных сайта на стороне клиента, поэтому вы можете использовать в ней API Node.js.
#### Пример: Исключение страниц из поиска {#example-excluding-pages-from-search}
Вы можете исключить страницы из поиска, добавив `search: false` в блок метаданных страницы. Альтернативный вариант:
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
search: {
provider: 'local',
options: {
_render(src, env, md) {
const html = md.render(src, env)
if (env.frontmatter?.search === false) return ''
if (env.relativePath.startsWith('some/path')) return ''
return html
}
}
}
}
})
```
::: warning ПРИМЕЧАНИЕ
В случае, если предоставляется пользовательская функция `_render`, вам нужно самостоятельно обработать заголовок `search: false`. Кроме того, объект `env` не будет полностью заполнен до вызова `md.render`, поэтому любые проверки необязательных свойств `env`, таких как `frontmatter`, должны быть выполнены после этого.
:::
#### Пример: Преобразование содержимого - добавление якорей {#example-transforming-content-adding-anchors}
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
search: {
provider: 'local',
options: {
_render(src, env, md) {
const html = md.render(src, env)
if (env.frontmatter?.title)
return md.render(`# ${env.frontmatter.title}`) + html
return html
}
}
}
}
})
```
## Поиск Algolia {#algolia-search}
VitePress поддерживает поиск в вашей документации с помощью [Algolia DocSearch](https://docsearch.algolia.com/docs/what-is-docsearch). Обратитесь к руководству по началу работы. В файле `.vitepress/config.ts` вам нужно будет указать, по крайней мере, следующее, чтобы всё работало:
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
search: {
provider: 'algolia',
options: {
appId: '...',
apiKey: '...',
indexName: '...'
}
}
}
})
```
### i18n {#algolia-search-i18n}
Вы можете использовать подобную конфигурацию для использования многоязычного поиска:
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
search: {
provider: 'algolia',
options: {
appId: '...',
apiKey: '...',
indexName: '...',
locales: {
ru: {
placeholder: 'Поиск в документации',
translations: {
button: {
buttonText: 'Поиск',
buttonAriaLabel: 'Поиск'
},
modal: {
searchBox: {
resetButtonTitle: 'Сбросить поиск',
resetButtonAriaLabel: 'Сбросить поиск',
cancelButtonText: 'Отменить поиск',
cancelButtonAriaLabel: 'Отменить поиск'
},
startScreen: {
recentSearchesTitle: 'История поиска',
noRecentSearchesText: 'Нет истории поиска',
saveRecentSearchButtonTitle: 'Сохранить в истории поиска',
removeRecentSearchButtonTitle: 'Удалить из истории поиска',
favoriteSearchesTitle: 'Избранное',
removeFavoriteSearchButtonTitle: 'Удалить из избранного'
},
errorScreen: {
titleText: 'Невозможно получить результаты',
helpText:
'Вам может потребоваться проверить подключение к Интернету'
},
footer: {
selectText: 'выбрать',
navigateText: 'перейти',
closeText: 'закрыть',
searchByText: 'поставщик поиска'
},
noResultsScreen: {
noResultsText: 'Нет результатов для',
suggestedQueryText: 'Вы можете попытаться узнать',
reportMissingResultsText:
'Считаете, что поиск даёт ложные результаты?',
reportMissingResultsLinkText:
'Нажмите на кнопку «Обратная связь»'
}
}
}
}
}
}
}
}
})
```
[Эти параметры](https://github.com/vuejs/vitepress/blob/main/types/docsearch.d.ts) можно переопределить. Чтобы узнать о них больше, обратитесь к официальной документации Algolia.
### Конфигурация поискового робота {#crawler-config}
Вот пример конфигурации, основанной на той, что используется на этом сайте:
```ts
new Crawler({
appId: '...',
apiKey: '...',
rateLimit: 8,
startUrls: ['https://vitepress.dev/'],
renderJavaScript: false,
sitemaps: [],
exclusionPatterns: [],
ignoreCanonicalTo: false,
discoveryPatterns: ['https://vitepress.dev/**'],
schedule: 'at 05:10 on Saturday',
actions: [
{
indexName: 'vitepress',
pathsToMatch: ['https://vitepress.dev/**'],
recordExtractor: ({ $, helpers }) => {
return helpers.docsearch({
recordProps: {
lvl1: '.content h1',
content: '.content p, .content li',
lvl0: {
selectors: '',
defaultValue: 'Documentation'
},
lvl2: '.content h2',
lvl3: '.content h3',
lvl4: '.content h4',
lvl5: '.content h5'
},
indexHeadings: true
})
}
}
],
initialIndexSettings: {
vitepress: {
attributesForFaceting: ['type', 'lang'],
attributesToRetrieve: ['hierarchy', 'content', 'anchor', 'url'],
attributesToHighlight: ['hierarchy', 'hierarchy_camel', 'content'],
attributesToSnippet: ['content:10'],
camelCaseAttributes: ['hierarchy', 'hierarchy_radio', 'content'],
searchableAttributes: [
'unordered(hierarchy_radio_camel.lvl0)',
'unordered(hierarchy_radio.lvl0)',
'unordered(hierarchy_radio_camel.lvl1)',
'unordered(hierarchy_radio.lvl1)',
'unordered(hierarchy_radio_camel.lvl2)',
'unordered(hierarchy_radio.lvl2)',
'unordered(hierarchy_radio_camel.lvl3)',
'unordered(hierarchy_radio.lvl3)',
'unordered(hierarchy_radio_camel.lvl4)',
'unordered(hierarchy_radio.lvl4)',
'unordered(hierarchy_radio_camel.lvl5)',
'unordered(hierarchy_radio.lvl5)',
'unordered(hierarchy_radio_camel.lvl6)',
'unordered(hierarchy_radio.lvl6)',
'unordered(hierarchy_camel.lvl0)',
'unordered(hierarchy.lvl0)',
'unordered(hierarchy_camel.lvl1)',
'unordered(hierarchy.lvl1)',
'unordered(hierarchy_camel.lvl2)',
'unordered(hierarchy.lvl2)',
'unordered(hierarchy_camel.lvl3)',
'unordered(hierarchy.lvl3)',
'unordered(hierarchy_camel.lvl4)',
'unordered(hierarchy.lvl4)',
'unordered(hierarchy_camel.lvl5)',
'unordered(hierarchy.lvl5)',
'unordered(hierarchy_camel.lvl6)',
'unordered(hierarchy.lvl6)',
'content'
],
distinct: true,
attributeForDistinct: 'url',
customRanking: [
'desc(weight.pageRank)',
'desc(weight.level)',
'asc(weight.position)'
],
ranking: [
'words',
'filters',
'typo',
'attribute',
'proximity',
'exact',
'custom'
],
highlightPreTag: '<span class="algolia-docsearch-suggestion--highlight">',
highlightPostTag: '</span>',
minWordSizefor1Typo: 3,
minWordSizefor2Typos: 7,
allowTyposOnNumericTokens: false,
minProximity: 1,
ignorePlurals: true,
advancedSyntax: true,
attributeCriteriaComputedByMinProximity: true,
removeWordsIfNoResults: 'allOptional'
}
}
})
```
<style>
img[src="/search.png"] {
width: 100%;
aspect-ratio: 1 / 1;
}
</style>

@ -0,0 +1,213 @@
# Сайдбар {#sidebar}
Сайдбар (боковая панель) — основной навигационный блок вашей документации. Меню боковой панели можно настроить в секции [`themeConfig.sidebar`](./default-theme-config#sidebar).
```js
export default {
themeConfig: {
sidebar: [
{
text: 'Руководство',
items: [
{ text: 'Введение', link: '/ru/introduction' },
{ text: 'Первые шаги', link: '/ru/getting-started' },
...
]
}
]
}
}
```
## Основы {#the-basics}
Простейшая форма сайдбара — это передача массива ссылок. Элемент первого уровня определяет «секцию» сайдбара. Он должен содержать `text`, который является заголовком секции, и `items`, которые являются фактическими навигационными ссылками.
```js
export default {
themeConfig: {
sidebar: [
{
text: 'Заголовок секции A',
items: [
{ text: 'Пункт A', link: '/item-a' },
{ text: 'Пункт B', link: '/item-b' },
...
]
},
{
text: 'Заголовок секции B',
items: [
{ text: 'Пункт C', link: '/item-c' },
{ text: 'Пункт D', link: '/item-d' },
...
]
}
]
}
}
```
Каждый элемент `link` должен указывать путь к фактическому файлу, начинающийся с `/`. Если добавить в конец ссылки косую черту, то будет показан `index.md` соответствующего каталога.
```js
export default {
themeConfig: {
sidebar: [
{
text: 'Руководство',
items: [
// Ссылка на страницу `/ru/guide/index.md`
{ text: 'Введение', link: '/ru/guide/' }
]
}
]
}
}
```
Вы можете вложить элементы боковой панели на 6 уровней вглубь, считая от корневого уровня. Обратите внимание, что более 6 уровней вложенных элементов будут игнорироваться и не отображаться на боковой панели.
```js
export default {
themeConfig: {
sidebar: [
{
text: 'Уровень 1',
items: [
{
text: 'Уровень 2',
items: [
{
text: 'Уровень 3',
items: [
...
]
}
]
}
]
}
]
}
}
```
## Несколько сайдбаров {#multiple-sidebars}
Вы можете показывать разные боковые панели в зависимости от текущего маршрута. Например, как показано на этом сайте, вы можете создать в документации отдельные разделы, например, «Руководство» и «Настройка».
Для этого сначала организуйте страницы в каталоги для каждого нужного раздела:
```
.
├─ guide/
│ ├─ index.md
│ ├─ one.md
│ └─ two.md
└─ config/
├─ index.md
├─ three.md
└─ four.md
```
Затем обновите конфигурацию, чтобы определить боковую панель для каждого раздела. На этот раз вместо массива нужно передать объект.
```js
export default {
themeConfig: {
sidebar: {
// Эта боковая панель отображается, когда пользователь находится в директории `guide`
'/guide/': [
{
text: 'Руководство',
items: [
{ text: 'Index', link: '/guide/' },
{ text: 'One', link: '/guide/one' },
{ text: 'Two', link: '/guide/two' }
]
}
],
// Эта боковая панель отображается, когда пользователь находится в директории `config`
'/config/': [
{
text: 'Настройка',
items: [
{ text: 'Index', link: '/config/' },
{ text: 'Three', link: '/config/three' },
{ text: 'Four', link: '/config/four' }
]
}
]
}
}
}
```
## Сворачиваемые группы {#collapsible-sidebar-groups}
Добавив опцию `collapsed` внутри группы `sidebar`, вы увидите кнопку переключения для скрытия/показа каждой секции.
```js
export default {
themeConfig: {
sidebar: [
{
text: 'Заголовок секции A',
collapsed: false,
items: [...]
}
]
}
}
```
Все секции «развёрнуты» по умолчанию. Если вы хотите, чтобы они были «свёрнуты» при первоначальной загрузке страницы, установите для опции `collapsed` значение `true`.
```js
export default {
themeConfig: {
sidebar: [
{
text: 'Заголовок секции A',
collapsed: true,
items: [...]
}
]
}
}
```
## `useSidebar` <Badge type="info" text="композабл" /> {#usesidebar}
Возвращает данные, связанные с сайдбаром. Возвращаемый объект имеет следующий тип:
```ts
export interface DocSidebar {
isOpen: Ref<boolean>
sidebar: ComputedRef<DefaultTheme.SidebarItem[]>
sidebarGroups: ComputedRef<DefaultTheme.SidebarItem[]>
hasSidebar: ComputedRef<boolean>
hasAside: ComputedRef<boolean>
leftAside: ComputedRef<boolean>
isSidebarEnabled: ComputedRef<boolean>
open: () => void
close: () => void
toggle: () => void
}
```
**Пример:**
```vue
<script setup>
import { useSidebar } from 'vitepress/theme'
const { hasSidebar } = useSidebar()
</script>
<template>
<div v-if="hasSidebar">Показывать только при наличии сайдбара</div>
</template>
```

@ -0,0 +1,252 @@
<script setup>
import { VPTeamMembers } from 'vitepress/theme'
const members = [
{
avatar: 'https://github.com/yyx990803.png',
name: 'Эван Ю',
title: 'Создатель',
links: [
{ icon: 'github', link: 'https://github.com/yyx990803' },
{ icon: 'twitter', link: 'https://twitter.com/youyuxi' }
]
},
{
avatar: 'https://github.com/kiaking.png',
name: 'Киа Кинг Исии',
title: 'Разработчик',
links: [
{ icon: 'github', link: 'https://github.com/kiaking' },
{ icon: 'twitter', link: 'https://twitter.com/KiaKing85' }
]
}
]
</script>
# Страница команды {#team-page}
Если вы хотите представить свою команду, вы можете использовать компоненты Team для создания страницы команды. Есть два варианта использования этих компонентов. Один из вариантов — встроить их в страницу с макетом `doc`, а другой — создать полноценную страницу команды.
## Отображение членов команды на странице {#show-team-members-in-a-page}
Вы можете использовать компонент `<VPTeamMembers>`, доступный из `vitepress/theme`, для отображения списка членов команды на любой странице.
```html
<script setup>
import { VPTeamMembers } from 'vitepress/theme'
const members = [
{
avatar: 'https://www.github.com/yyx990803.png',
name: 'Эван Ю',
title: 'Создатель',
links: [
{ icon: 'github', link: 'https://github.com/yyx990803' },
{ icon: 'twitter', link: 'https://twitter.com/youyuxi' }
]
},
...
]
</script>
# Поприветствуйте нашу замечательную команду
<VPTeamMembers size="small" :members="members" />
```
Вышеуказанное отобразит члена команды в виде карточки. Должно отобразиться что-то похожее на то, что показано ниже.
<VPTeamMembers size="small" :members="members" />
Компонент `<VPTeamMembers>` поставляется в двух различных размерах, `small` и `medium`. Хотя это зависит от ваших предпочтений, обычно размер `small` лучше подходит для использования на странице с макетом `doc`. Кроме того, вы можете добавить дополнительные свойства для карточки члена команды, например, добавить «описание» или кнопку «спонсировать». Подробнее об этом в секции [`<VPTeamMembers>`](#vpteammembers).
Встраивание членов команды в страницу документа хорошо подходит для небольших команд, где наличие полной страницы команды может быть слишком большим, или для представления частичных членов команды в качестве ссылки на контекст документации.
Если у вас большое количество участников или вы просто хотите иметь больше места для отображения членов команды, подумайте о [создании отдельной страницы команды](#create-a-full-team-page).
## Создание отдельной страницы команды {#create-a-full-team-page}
Вместо того чтобы добавлять членов команды на страницу с макетом `doc`, вы можете создать полноценную страницу команды, подобно созданию пользовательской [главной страницы](./default-theme-home-page).
Чтобы создать страницу команды, сначала создайте новый md-файл. Имя файла не имеет значения, но здесь мы назовем его `team.md`. В этом файле установите в блоке метаданных параметр `layout: page`, а затем вы можете организовать структуру страницы, используя компоненты `TeamPage`.
```html
---
layout: page
---
<script setup>
import {
VPTeamPage,
VPTeamPageTitle,
VPTeamMembers
} from 'vitepress/theme'
const members = [
{
avatar: 'https://www.github.com/yyx990803.png',
name: 'Эван Ю',
title: 'Создатель',
links: [
{ icon: 'github', link: 'https://github.com/yyx990803' },
{ icon: 'twitter', link: 'https://twitter.com/youyuxi' }
]
},
...
]
</script>
<VPTeamPage>
<VPTeamPageTitle>
<template #title> Наша команда </template>
<template #lead>
Разработкой VitePress руководит международная команда, некоторые члены
которой представлены ниже.
</template>
</VPTeamPageTitle>
<VPTeamMembers :members="members" />
</VPTeamPage>
```
При создании полной страницы команды не забудьте обернуть все компоненты компонентом `<VPTeamPage>`. Этот компонент обеспечит всем вложенным компонентам, связанным с командой, правильную структуру макета, например, расстояние между ними.
Компонент `<VPPageTitle>` добавляет блок заголовка страницы. Заголовок — это тег `<h1>`. Используйте слоты `#title` и `#lead`, чтобы рассказать о своей команде.
`<VPMembers>` работает так же, как и при использовании в doc-странице. Отобразится список участников.
### Добавление секций для разделения членов команды {#add-sections-to-divide-team-members}
Вы можете добавить «секции» на страницу команды. Например, у вас могут быть разные типы членов команды, такие как члены основной команды и партнёры сообщества. Вы можете разделить этих членов на секции, чтобы лучше объяснить роли каждой группы.
Для этого добавьте компонент `<VPTeamPageSection>` в файл `team.md`, который мы создали ранее.
```html
---
layout: page
---
<script setup>
import {
VPTeamPage,
VPTeamPageTitle,
VPTeamMembers,
VPTeamPageSection
} from 'vitepress/theme'
const coreMembers = [...]
const partners = [...]
</script>
<VPTeamPage>
<VPTeamPageTitle>
<template #title>Наша команда</template>
<template #lead>...</template>
</VPTeamPageTitle>
<VPTeamMembers size="medium" :members="coreMembers" />
<VPTeamPageSection>
<template #title>Партнёры</template>
<template #lead>...</template>
<template #members>
<VPTeamMembers size="small" :members="partners" />
</template>
</VPTeamPageSection>
</VPTeamPage>
```
Компонент `<VPTeamPageSection>` может иметь слоты `#title` и `#lead`, аналогичные компоненту `VPTeamPageTitle`, а также слот `#members` для отображения членов команды.
Не забудьте поместить компонент `<VPTeamMembers>` в слот `#members`.
## `<VPTeamMembers>` {#vpteammembers}
Компонент `<VPTeamMembers>` отображает заданный список членов команды.
```html
<VPTeamMembers
size="medium"
:members="[
{ avatar: '...', name: '...' },
{ avatar: '...', name: '...' },
...
]"
/>
```
```ts
interface Props {
// Размер карточки каждого члена команды. По умолчанию `medium`.
size?: 'small' | 'medium'
// Список членов команды для отображения.
members: TeamMember[]
}
interface TeamMember {
// Изображение аватара.
avatar: string
// Имя члена команды.
name: string
// Заголовок, отображаемый под именем члена команды.
// например: разработчик, инженер-программист и т. д.
title?: string
// Организация, в которой состоит текущий член команды.
org?: string
// URL-адрес сайта организации.
orgLink?: string
// Описание члена команды.
desc?: string
// Социальные ссылки: GitHub, Twitter и т. д.
// Могут быть переданы в виде объекта.
// См. https://vitepress.dev/reference/default-theme-config.html#sociallinks
links?: SocialLink[]
// URL-адрес спонсорской страницы члена команды.
sponsor?: string
// Текст спонсорской ссылки. По умолчанию 'Sponsor'.
actionText?: string
}
```
## `<VPTeamPage>` {#vpteampage}
Корневой компонент при создании отдельной страницы команды. Принимает только один слот. Он будет стилизовать все передаваемые компоненты, связанные с командой.
## `<VPTeamPageTitle>` {#vpteampagetitle}
Добавляет блок «заголовка» страницы. Лучше всего использовать в самом начале внутри `<VPTeamPage>`. Принимает слоты `#title` и `#lead`.
```html
<VPTeamPage>
<VPTeamPageTitle>
<template #title> Наша команда </template>
<template #lead>
Разработкой VitePress руководит международная команда, некоторые члены
которой представлены ниже.
</template>
</VPTeamPageTitle>
</VPTeamPage>
```
## `<VPTeamPageSection>` {#vpteampagesection}
Создает «секцию» на странице команды. Принимает слоты `#title`, `#lead` и `#members`. Внутри `<VPTeamPage>` вы можете добавить столько секций, сколько захотите.
```html
<VPTeamPage>
...
<VPTeamPageSection>
<template #title>Партнёры</template>
<template #lead>Lorem ipsum...</template>
<template #members>
<VPTeamMembers :members="data" />
</template>
</VPTeamPageSection>
</VPTeamPage>
```

@ -0,0 +1,221 @@
---
outline: deep
---
# Конфигурация метаданных {#frontmatter-config}
Метаданные обеспечивают настройку отдельных страниц. В каждом файле Markdown можно использовать метаданные, чтобы переопределить параметры конфигурации сайта или темы. Кроме того, есть параметры конфигурации, которые можно задать только через метаданные.
Пример использования:
```md
---
title: Документация с VitePress
editLink: true
---
```
Вы можете получить доступ к метаданным через глобальный объект `$frontmatter` в выражениях Vue:
```md
{{ $frontmatter.title }}
```
## title {#title}
- Тип: `string`
Заголовок страницы. Это то же самое, что [config.title](./site-config#title), и оно переопределяет конфигурацию сайта.
```yaml
---
title: VitePress
---
```
## titleTemplate {#titletemplate}
- Тип: `string | boolean`
Суффикс для названия. Это то же самое, что и [config.titleTemplate](./site-config#titletemplate), и оно переопределяет конфигурацию сайта.
```yaml
---
title: VitePress
titleTemplate: Генератор статических сайтов на основе Vite и Vue
---
```
## description {#description}
- Тип: `string`
Описание для страницы. Это то же самое, что и [config.description](./site-config#description), и оно переопределяет конфигурацию сайта.
```yaml
---
description: VitePress
---
```
## head {#head}
- Тип: `HeadConfig[]`
Укажите дополнительные теги, которые будут выводиться для текущей страницы. Они будут добавляться после других тегов внутри блока head, введённых в конфигурации сайта.
```yaml
---
head:
- - meta
- name: description
content: привет
- - meta
- name: keywords
content: супер-пупер SEO
---
```
```ts
type HeadConfig =
| [string, Record<string, string>]
| [string, Record<string, string>, string]
```
## Только для темы по умолчанию {#default-theme-only}
Следующие параметры метаданных применимы только при использовании темы по умолчанию.
### layout {#layout}
- Тип: `doc | home | page`
- По умолчанию: `doc`
Определяет макет страницы.
- `doc` - Применяет стили документации по умолчанию к содержимому Markdown.
- `home` - Вы можете добавить дополнительные параметры, такие как `hero` и `features`, чтобы быстро создать красивую целевую страницу.
- `page` - Ведет себя аналогично `doc`, но не применяет стили к содержимому. Полезно, если вы хотите создать полностью настраиваемую страницу.
```yaml
---
layout: doc
---
```
### hero <Badge type="info" text="только для страниц с макетом home" /> {#hero}
Определяет содержимое секции `hero`, когда `layout` имеет значение `home`. Подробнее в главе [Тема по умолчанию: Главная страница](./default-theme-home-page).
### features <Badge type="info" text="только для страниц с макетом home" /> {#features}
Определяет элементы для отображения в секции `features`, когда `layout` имеет значение `home`. Подробнее в главе [Тема по умолчанию: Главная страница](./default-theme-home-page).
### navbar {#navbar}
- Тип: `boolean`
- По умолчанию: `true`
Отображать ли [панель навигации](./default-theme-nav).
```yaml
---
navbar: false
---
```
### sidebar {#sidebar}
- Тип: `boolean`
- По умолчанию: `true`
Отображать ли [сайдбар](./default-theme-sidebar).
```yaml
---
sidebar: false
---
```
### aside {#aside}
- Тип: `boolean | 'left'`
- По умолчанию: `true`
Определяет расположение компонента aside в макете `doc`.
Установка этого значения в `false` предотвращает отрисовку контейнера сайдбара.\
Установка этого значения в `true` приведёт к отображению сайдбара справа.\
Установка этого значения в `left` приведёт к отображению сайдбара слева.
```yaml
---
aside: false
---
```
### outline {#outline}
- Тип: `number | [number, number] | 'deep' | false`
- По умолчанию: `2`
Уровни заголовков в оглавлении для отображения на странице. Это то же самое, что и [config.themeConfig.outline.level](./default-theme-config#outline), и оно переопределяет значение, установленное в конфигурации сайта.
### lastUpdated {#lastupdated}
- Тип: `boolean | Date`
- По умолчанию: `true`
Отображать ли текст [Обновлено](./default-theme-last-updated) в нижнем колонтитуле текущей страницы. Если указано время даты, оно будет отображаться вместо временной метки последнего изменения git.
```yaml
---
lastUpdated: false
---
```
### editLink {#editlink}
- Тип: `boolean`
- По умолчанию: `true`
Отображать ли [ссылку для редактирования](./default-theme-edit-link) в нижнем колонтитуле текущей страницы.
```yaml
---
editLink: false
---
```
### footer {#footer}
- Тип: `boolean`
- По умолчанию: `true`
Отображать ли [подвал](./default-theme-footer).
```yaml
---
footer: false
---
```
### pageClass {#pageclass}
- Тип: `string`
Добавьте дополнительное имя класса на определённую страницу.
```yaml
---
pageClass: custom-page-class
---
```
Вы также можете настроить стили этой конкретной страницы в файле `.vitepress/theme/custom.css`:
```css
.custom-page-class {
  /* стили для конкретной страницы */
}
```

@ -0,0 +1,165 @@
# Runtime API {#runtime-api}
VitePress предлагает несколько встроенных API, позволяющих получить доступ к данным приложения. VitePress также поставляется с несколькими встроенными компонентами, которые можно использовать глобально.
Вспомогательные методы глобально импортируются из `vitepress` и обычно используются в компонентах Vue для пользовательских тем. Однако их можно использовать и внутри страниц `.md`, так как файлы markdown компилируются в [однофайловые компоненты](https://ru.vuejs.org/guide/scaling-up/sfc.html) Vue.
Методы, начинающиеся с `use*`, указывают на то, что это функция [Vue 3 Composition API](https://ru.vuejs.org/guide/introduction.html#composition-api) («композабл»), которая может быть использована только внутри `setup()` или `<script setup>`.
## `useData` <Badge type="info" text="композабл" /> {#usedata}
Возвращает данные, относящиеся к конкретной странице. Возвращаемый объект имеет следующий тип:
```ts
interface VitePressData<T = any> {
/**
* Метаданные на уровне сайта
*/
site: Ref<SiteData<T>>
/**
* themeConfig from .vitepress/config.js
*/
theme: Ref<T>
/**
* Метаданные на уровне страницы
*/
page: Ref<PageData>
/**
* Метаданные страницы
*/
frontmatter: Ref<PageData['frontmatter']>
/**
* Параметры динамического маршрута
*/
params: Ref<PageData['params']>
title: Ref<string>
description: Ref<string>
lang: Ref<string>
isDark: Ref<boolean>
dir: Ref<string>
localeIndex: Ref<string>
}
interface PageData {
title: string
titleTemplate?: string | boolean
description: string
relativePath: string
filePath: string
headers: Header[]
frontmatter: Record<string, any>
params?: Record<string, any>
isNotFound?: boolean
lastUpdated?: number
}
```
**Пример:**
```vue
<script setup>
import { useData } from 'vitepress'
const { theme } = useData()
</script>
<template>
<h1>{{ theme.footer.copyright }}</h1>
</template>
```
## `useRoute` <Badge type="info" text="композабл" /> {#useroute}
Возвращает текущий объект маршрута со следующим типом:
```ts
interface Route {
path: string
data: PageData
component: Component | null
}
```
## `useRouter` <Badge type="info" text="композабл" /> {#userouter}
Возвращает экземпляр маршрутизатора VitePress, чтобы вы могли программно перейти на другую страницу.
```ts
interface Router {
/**
* Текущий маршрут.
*/
route: Route
/**
* Переход к новому URL-адресу.
*/
go: (to?: string) => Promise<void>
/**
* Вызывается перед изменением маршрута. Верните `false`, чтобы отменить навигацию.
*/
onBeforeRouteChange?: (to: string) => Awaitable<void | boolean>
/**
* Вызывается перед загрузкой компонента страницы (после того, как состояние истории
* обновлено). Верните `false`, чтобы отменить навигацию.
*/
onBeforePageLoad?: (to: string) => Awaitable<void | boolean>
/**
* Вызывается после изменения маршрута.
*/
onAfterRouteChanged?: (to: string) => Awaitable<void>
}
```
## `withBase` <Badge type="info" text="хелпер" /> {#withbase}
- **Тип**: `(path: string) => string`
Добавляет настроенный [`base`](./site-config#base) к заданному URL-пути. Также смотрите секцию [Базовый URL](../guide/asset-handling#base-url).
## `<Content />` <Badge type="info" text="компонент" /> {#content}
Компонент `<Content />` отображает отрисованное содержимое Markdown. Полезно [при создании собственной темы](../guide/custom-theme).
```vue
<template>
<h1>Пользовательский макет!</h1>
<Content />
</template>
```
## `<ClientOnly />` <Badge type="info" text="компонент" /> {#clientonly}
Компонент `<ClientOnly />` отображает свой слот только на стороне клиента.
Поскольку приложения VitePress при генерации статических сборок рендерятся в Node.js, любое использование Vue должно соответствовать универсальным требованиям к коду. Короче говоря, убедитесь, что доступ к API Browser / DOM осуществляется только в хуках `beforeMount` или `mounted`.
Если вы используете или демонстрируете компоненты, которые не являются SSR-дружественными (например, содержат пользовательские директивы), вы можете обернуть их внутри компонента `ClientOnly`.
```vue-html
<ClientOnly>
<NonSSRFriendlyComponent />
</ClientOnly>
```
- См. также: [Совместимость с SSR](../guide/ssr-compat)
## `$frontmatter` <Badge type="info" text="глобальный шаблон" /> {#frontmatter}
Прямой доступ к [метаданным](../guide/frontmatter) текущей страницы в выражениях Vue.
```md
---
title: Привет
---
# {{ $frontmatter.title }}
```
## `$params` <Badge type="info" text="глобальный шаблон" /> {#params}
Прямой доступ к параметрам [динамических маршрутов](../guide/routing#dynamic-routes) текущей страницы в выражениях Vue.
```md
- имя пакета: {{ $params.pkg }}
- версия: {{ $params.version }}
```

@ -0,0 +1,720 @@
---
outline: deep
---
# Конфигурация сайта {#site-config}
Конфигурация сайта — это место, где вы можете определить глобальные настройки сайта. Параметры конфигурации приложения определяют настройки, которые применяются к каждому сайту VitePress, независимо от того, какая тема на нем используется. Например, базовый каталог или название сайта.
## Обзор {#overview}
### Разрешение конфигурации {#config-resolution}
Файл конфигурации всегда разрешается из `<root>/.vitepress/config.[ext]`, где `<root>` — это корень вашего [проекта](../guide/routing#root-and-source-directory) VitePress, а `[ext]` — одно из поддерживаемых расширений файла. TypeScript поддерживается из коробки. Поддерживаемые расширения включают `.js`, `.ts`, `.mjs` и `.mts`.
В файлах конфигурации рекомендуется использовать синтаксис ES-модулей. Файл конфигурации должен по умолчанию экспортировать объект:
```ts
export default {
// параметры конфигурации на уровне приложения
lang: 'ru-RU',
title: 'VitePress',
description: 'Генератор статических сайтов на основе Vite и Vue.',
...
}
```
:::details Динамическая (асинхронная) конфигурация
Если вам нужно генерировать конфигурацию динамически, вы также можете экспортировать функцию по умолчанию. Например:
```ts
import { defineConfig } from 'vitepress'
export default async () => {
const posts = await (await fetch('https://my-cms.com/blog-posts')).json()
return defineConfig({
// параметры конфигурации на уровне приложения
lang: 'ru-RU',
title: 'VitePress',
description: 'Генератор статических сайтов на основе Vite и Vue.',
// параметры конфигурации на уровне темы
themeConfig: {
sidebar: [
...posts.map((post) => ({
text: post.name,
link: `/posts/${post.name}`
}))
]
}
})
}
```
Вы также можете использовать `await` верхнего уровня. Например:
```ts
import { defineConfig } from 'vitepress'
const posts = await (await fetch('https://my-cms.com/blog-posts')).json()
export default defineConfig({
// параметры конфигурации на уровне приложения
lang: 'ru-RU',
title: 'VitePress',
description: 'Генератор статических сайтов на основе Vite и Vue.',
// параметры конфигурации на уровне темы
themeConfig: {
sidebar: [
...posts.map((post) => ({
text: post.name,
link: `/posts/${post.name}`
}))
]
}
})
```
:::
### Интеллектуальная настройка {#config-intellisense}
Использование помощника `defineConfig` обеспечит интеллектуальный анализ опций конфигурации на основе TypeScript. Если ваша IDE поддерживает эту функцию, она должна работать как в JavaScript, так и в TypeScript.
```js
import { defineConfig } from 'vitepress'
export default defineConfig({
// ...
})
```
### Типизированная конфигурация темы {#typed-theme-config}
По умолчанию помощник `defineConfig` ожидает тип конфигурации темы из темы по умолчанию:
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
// Тип `DefaultTheme.Config`
}
})
```
Если вы используете пользовательскую тему и хотите проверять типы для конфигурации темы, вам нужно использовать `defineConfigWithTheme`, и передавать тип конфигурации для вашей пользовательской темы через общий аргумент:
```ts
import { defineConfigWithTheme } from 'vitepress'
import type { ThemeConfig } from 'your-theme'
export default defineConfigWithTheme<ThemeConfig>({
themeConfig: {
// Tип `ThemeConfig`
}
})
```
### Настройка Vite, Vue и Markdown {#vite-vue-markdown-config}
- **Vite**
Вы можете настроить базовый экземпляр Vite с помощью опции [vite](#vite) в конфигурации VitePress. Нет необходимости создавать отдельный файл конфигурации Vite.
- **Vue**
VitePress уже включает в себя официальный плагин Vue для Vite ([@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue)). Вы можете настроить его параметры с помощью опции [vue](#vue) в конфигурации VitePress.
- **Markdown**
Вы можете настроить базовый экземпляр [Markdown-It](https://github.com/markdown-it/markdown-it) с помощью опции [markdown](#markdown) в конфигурации VitePress.
## Метаданные сайта {#site-metadata}
### title {#title}
- Тип: `string`
- По умолчанию: `VitePress`
- Можно переопределить для каждой страницы с помощью [метаданных](./frontmatter-config#title)
Название для сайта. При использовании темы по умолчанию оно будет отображаться в панели навигации.
Оно также будет использоваться в качестве суффикса по умолчанию для всех заголовков отдельных страниц, если не определен [`titleTemplate`](#titletemplate). Окончательный заголовок отдельной страницы будет представлять собой текстовое содержимое её первого заголовка `<h1>`, объединённое с глобальным `title` в качестве суффикса. Например, со следующей конфигурацией и содержимым страницы:
```ts
export default {
title: 'Мой замечательный сайт'
}
```
```md
# Привет
```
Заголовок страницы будет таким: `Привет | Мой замечательный сайт`.
### titleTemplate {##titletemplate}
- Тип: `string | boolean`
- Можно переопределить для каждой страницы с помощью [метаданных](./frontmatter-config#titletemplate)
Позволяет настраивать суффикс заголовка каждой страницы или весь заголовок. Например:
```ts
export default {
title: 'Мой замечательный сайт',
titleTemplate: 'Пользовательский суффикс'
}
```
```md
# Привет
```
Заголовок страницы будет таким: `Привет | Пользовательский суффикс`.
Чтобы полностью настроить отображение заголовка, вы можете использовать символ `:title` в `titleTemplate`:
```ts
export default {
titleTemplate: ':title - Пользовательский суффикс'
}
```
Здесь `:title` будет заменён текстом, выведенным из первого заголовка страницы `<h1>`. Заголовок страницы предыдущего примера будет `Привет - Пользовательский суффикс`.
Опция может быть установлена в значение `false`, чтобы отключить суффиксы заголовков.
### description {#description}
- Тип: `string`
- По умолчанию: `A VitePress site`
- Можно переопределить для каждой страницы с помощью [метаданных](./frontmatter-config#description)
Описание для сайта. Это будет отображаться как тег `<meta>` в HTML-странице.
```ts
export default {
description: 'A VitePress site'
}
```
### head {#head}
- Тип: `HeadConfig[]`
- По умолчанию: `[]`
- Можно добавлять на страницу через [метаданные](./frontmatter-config#head)
Дополнительные элементы для отображения в теге `<head>` в HTML-странице. Добавленные пользователем теги выводятся перед закрывающим тегом `head`, после тегов VitePress.
```ts
type HeadConfig =
| [string, Record<string, string>]
| [string, Record<string, string>, string]
```
#### Пример: Добавление значка сайта {#example-adding-a-favicon}
```ts
export default {
head: [['link', { rel: 'icon', href: '/favicon.ico' }]]
} // поместите favicon.ico в публичную директорию; если установлен параметр base, используйте /base/favicon.ico
/* Отрисуется так:
<link rel="icon" href="/favicon.ico">
*/
```
#### Пример: Добавление шрифтов Google {#example-adding-google-fonts}
```ts
export default {
head: [
['link', { rel: 'preconnect', href: 'https://fonts.googleapis.com' }],
[
'link',
{ rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: '' }
],
[
'link',
{
href: 'https://fonts.googleapis.com/css2?family=Roboto&display=swap',
rel: 'stylesheet'
}
]
]
}
/* Отрисуется так:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
*/
```
#### Пример: Регистрация сервис-воркера {#example-registering-a-service-worker}
```ts
export default {
head: [
[
'script',
{ id: 'register-sw' },
`;(() => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
}
})()`
]
]
}
/* Отрисуется так:
<script id="register-sw">
;(() => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
}
})()
</script>
*/
```
#### Пример: Использование Google Analytics {#example-using-google-analytics}
```ts
export default {
head: [
[
'script',
{ async: '', src: 'https://www.googletagmanager.com/gtag/js?id=TAG_ID' }
],
[
'script',
{},
`window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'TAG_ID');`
]
]
}
/* Отрисуется так:
<script async src="https://www.googletagmanager.com/gtag/js?id=TAG_ID"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'TAG_ID');
</script>
*/
```
### lang {#lang}
- Тип: `string`
- По умолчанию: `en-US`
Атрибут lang для сайта. Будет выглядеть как тег `<html lang="en-US">` в HTML-странице.
```ts
export default {
lang: 'en-US'
}
```
### base {#base}
- Тип: `string`
- По умолчанию: `/`
Базовый URL-адрес, по которому будет развёрнут сайт. Этот параметр необходимо задать, если вы планируете развернуть свой сайт по подпути, например, для страниц GitHub. Если вы планируете развернуть свой сайт на `https://foo.github.io/bar/`, то вам следует установить base на `'/bar/'`. Он всегда должен начинаться и заканчиваться косой чертой.
Параметр `base` автоматически добавляется ко всем URL, которые начинаются с `/` в других опциях, поэтому вам нужно указать его только один раз.
```ts
export default {
base: '/base/'
}
```
## Маршрутизация {#routing}
### cleanUrls {#cleanurls}
- Тип: `boolean`
- По умолчанию: `false`
Если установить значение `true`, VitePress будет удалять из URL-адресов завершающий `.html`. Также смотрите [Создание чистого URL-адреса](../guide/routing#generating-clean-url).
::: warning Требуется поддержка сервера
Для включения этой функции может потребоваться дополнительная настройка на вашей хостинговой платформе. Чтобы это сработало, ваш сервер должен быть способен обслуживать `/foo.html` при посещении `/foo` **без редиректа**.
:::
### rewrites {#rewrites}
- Тип: `Record<string, string>`
Определяет сопоставление пользовательских каталогов с URL-адресами. Дополнительную информацию см. в секции [Маршрутизация: перезапись маршрутов](../guide/routing#route-rewrites).
```ts
export default {
rewrites: {
'source/:page': 'destination/:page'
}
}
```
## Сборка {#build}
### srcDir {#srcdir}
- Тип: `string`
- По умолчанию: `.`
Каталог, в котором хранятся ваши страницы в формате Markdown, относительно корня проекта. Также смотрите [Корневая директория и директория с исходными файлами](../guide/routing#root-and-source-directory).
```ts
export default {
srcDir: './src'
}
```
### srcExclude {#srcexclude}
- Тип: `string`
- По умолчанию: `undefined`
[Шаблон](https://github.com/mrmlnc/fast-glob#pattern-syntax) для поиска файлов, которые должны быть исключены из исходного содержимого.
```ts
export default {
srcExclude: ['**/README.md', '**/TODO.md']
}
```
### outDir {#outdir}
- Тип: `string`
- По умолчанию: `./.vitepress/dist`
Расположение вывода сборки для сайта, относительно [корня проекта](../guide/routing#root-and-source-directory).
```ts
export default {
outDir: '../public'
}
```
### assetsDir {#assetsdir}
- Тип: `string`
- По умолчанию: `assets`
Укажите каталог, в котором будут храниться сгенерированные ресурсы. Путь должен находиться внутри [`outDir`](#outdir) и разрешается относительно него.
```ts
export default {
assetsDir: 'static'
}
```
### cacheDir {#cachedir}
- Тип: `string`
- По умолчанию: `./.vitepress/cache`
Каталог для файлов кэша, относительно [корня проекта](../guide/routing#root-and-source-directory). См. также: [cacheDir](https://vitejs.dev/config/shared-options.html#cachedir).
```ts
export default {
cacheDir: './.vitepress/.vite'
}
```
### ignoreDeadLinks {#ignoredeadlinks}
- Тип: `boolean | 'localhostLinks' | (string | RegExp | ((link: string) => boolean))[]`
- По умолчанию: `false`
Если установлено значение `true`, VitePress не будет завершать сборку из-за неработающих ссылок.
Если установить значение `'localhostLinks'`, сборка будет завершаться при наличии неработающих ссылок, но не будет проверять ссылки `localhost`.
```ts
export default {
ignoreDeadLinks: true
}
```
Это также может быть массив точных строк url, шаблонов regex или пользовательских функций фильтрации.
```ts
export default {
ignoreDeadLinks: [
// игнорировать url "/playground"
'/playground',
// игнорировать все ссылки на localhost
/^https?:\/\/localhost/,
// игнорировать все ссылки, включающие "/repl/""
/\/repl\//,
// пользовательская функция, игнорирует все ссылки, включающие "ignore"
(url) => {
return url.toLowerCase().includes('ignore')
}
]
}
```
### metaChunk <Badge type="warning" text="экспериментально" /> {#metachunk}
- Тип: `boolean`
- По умолчанию: `false`
Если установлено значение `true`, метаданные страницы извлекаются в отдельный фрагмент JavaScript, а не вставляются в исходный HTML. Это уменьшает полезную нагрузку HTML каждой страницы и делает метаданные страниц кэшируемыми, что позволяет снизить пропускную способность сервера при наличии большого количества страниц на сайте.
### mpa <Badge type="warning" text="экспериментально" /> {#mpa}
- Тип: `boolean`
- По умолчанию: `false`
Если установлено значение `true`, производственное приложение будет создано в [режиме MPA](../guide/mpa-mode). В режиме MPA по умолчанию используется 0 КБ JavaScript, что приводит к отключению навигации на стороне клиента и требует явного согласия на интерактивность.
## Тема {#theming}
### appearance {#appearance}
- Тип: `boolean | 'dark' | 'force-dark' | import('@vueuse/core').UseDarkOptions`
- По умолчанию: `true`
Включать ли тёмный режим (путём добавления класса `.dark` к элементу `<html>`).
- Если опция имеет значение `true`, тема по умолчанию будет определяться цветовой гаммой, предпочитаемой пользователем.
- Если опция имеет значение `dark`, тема по умолчанию будет тёмной, если пользователь не переключит её вручную.
- Если установить значение `false`, пользователи не смогут переключать тему.
Эта опция вставляет встроенный скрипт, который восстанавливает настройки пользователей из локального хранилища с помощью ключа `vitepress-theme-appearance`. Это гарантирует, что класс `.dark` будет применён до отрисовки страницы, чтобы избежать мерцания.
`appearance.initialValue` может быть только `'dark' | undefined`. Ссылки или геттеры не поддерживаются.
### lastUpdated {#lastupdated}
- Тип: `boolean`
- По умолчанию: `false`
Получать ли временную метку последнего обновления для каждой страницы с помощью Git. Временная метка будет включена в данные каждой страницы, доступные через [`useData`](./runtime-api#usedata).
При использовании темы по умолчанию включение этой опции приведёт к отображению времени последнего обновления каждой страницы. Вы можете настроить текст с помощью опции [`themeConfig.lastUpdatedText`](./default-theme-config#lastupdatedtext).
## Кастомизация {#customization}
### markdown {#markdown}
- Тип: `MarkdownOption`
Настройте параметры парсера Markdown. VitePress использует [Markdown-it](https://github.com/markdown-it/markdown-it) в качестве парсера и [Shiki](https://github.com/shikijs/shiki) для подсветки синтаксиса языка. Внутри этой опции вы можете передать различные параметры, связанные с Markdown, в соответствии с вашими потребностями.
```js
export default {
markdown: {...}
}
```
Проверьте [объявление типа и jsdocs](https://github.com/vuejs/vitepress/blob/main/src/node/markdown/markdown.ts) на наличие всех доступных опций.
### vite {#vite}
- Тип: `import('vite').UserConfig`
Передаёт необработанную [конфигурацию Vite](https://vitejs.dev/config/) внутреннему серверу разработки / сборщику Vite.
```js
export default {
vite: {
// параметры конфигурации Vite
}
}
```
### vue {#vue}
- Тип: `import('@vitejs/plugin-vue').Options`
Передаёт необработанные [параметры `@vitejs/plugin-vue`](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue#options) внутреннему экземпляру плагина.
```js
export default {
vue: {
// параметры @vitejs/plugin-vue
}
}
```
## Хуки сборки {#build-hooks}
Хуки для сборки VitePress позволяют добавлять на сайт новую функциональность и поведение:
- Карта сайта
- Поисковая индексация
- PWA
- Телепорты
### buildEnd {#buildend}
- Тип: `(siteConfig: SiteConfig) => Awaitable<void>`
`buildEnd` — это хук CLI сборки, который будет запущен после завершения сборки (SSG), но до выхода из процесса VitePress CLI.
```ts
export default {
async buildEnd(siteConfig) {
// ...
}
}
```
### postRender {#postrender}
- Тип: `(context: SSGContext) => Awaitable<SSGContext | void>`
`postRender` — это хук сборки, вызываемый после завершения рендеринга SSG. Это позволит вам обрабатывать содержимое телепортов во время SSG.
```ts
export default {
async postRender(context) {
// ...
}
}
```
```ts
interface SSGContext {
content: string
teleports?: Record<string, string>
[key: string]: any
}
```
### transformHead {#transformhead}
- Тип: `(context: TransformContext) => Awaitable<HeadConfig[]>`
`transformHead` — это хук сборки для преобразования заголовка перед генерацией каждой страницы. Это позволит вам добавить в конфигурацию VitePress записи, которые не могут быть добавлены статически. Вам нужно только вернуть дополнительные записи, они будут автоматически объединены с существующими.
::: warning ПРЕДУПРЕЖДЕНИЕ
Не мутируйте ничего внутри `context`.
:::
```ts
export default {
async transformHead(context) {
// ...
}
}
```
```ts
interface TransformContext {
page: string // например, index.md (относительно srcDir)
assets: string[] // все ресурсы, не относящиеся к js/css, в виде полностью разрешённых публичных URL-адресов
siteConfig: SiteConfig
siteData: SiteData
pageData: PageData
title: string
description: string
head: HeadConfig[]
content: string
}
```
Обратите внимание, что этот хук вызывается только при статической генерации сайта. Он не вызывается во время разработки. Если вам нужно добавить динамические записи в голову во время разработки, вместо этого вы можете использовать хук [`transformPageData`](#transformpagedata):
```ts
export default {
transformPageData(pageData) {
pageData.frontmatter.head ??= []
pageData.frontmatter.head.push([
'meta',
{
name: 'og:title',
content:
pageData.frontmatter.layout === 'home'
? `VitePress`
: `${pageData.title} | VitePress`
}
])
}
}
```
#### Пример: Добавление канонического URL-адреса `<link>` {#example-adding-a-canonical-url-link}
```ts
export default {
transformPageData(pageData) {
const canonicalUrl = `https://example.com/${pageData.relativePath}`
.replace(/index\.md$/, '')
.replace(/\.md$/, '.html')
pageData.frontmatter.head ??= []
pageData.frontmatter.head.push([
'link',
{ rel: 'canonical', href: canonicalUrl }
])
}
}
```
### transformHtml {#transformhtml}
- Тип: `(code: string, id: string, context: TransformContext) => Awaitable<string | void>`
`transformHtml` — это хук сборки для преобразования содержимого каждой страницы перед сохранением на диск.
::: warning ПРЕДУПРЕЖДЕНИЕ
Не мутируйте ничего внутри `контекста`. Кроме того, изменение html-содержимого может вызвать проблемы с гидратацией во время выполнения.
:::
```ts
export default {
async transformHtml(code, id, context) {
// ...
}
}
```
### transformPageData {#transformpagedata}
- Тип: `(pageData: PageData, context: TransformPageContext) => Awaitable<Partial<PageData> | { [key: string]: any } | void>`
`transformPageData` — это хук для преобразования `pageData` каждой страницы. Вы можете напрямую изменять `pageData` или возвращать изменённые значения, которые будут объединены с данными страницы.
::: warning ПРЕДУПРЕЖДЕНИЕ
Не мутируйте ничего внутри `context` и будьте осторожны, это может повлиять на производительность dev-сервера, особенно если у вас есть некоторые сетевые запросы или тяжёлые вычисления (например, генерация изображений) в хуке. Вы можете проверить `process.env.NODE_ENV === 'production'` для условной логики.
:::
```ts
export default {
async transformPageData(pageData, { siteConfig }) {
pageData.contributors = await getPageContributors(pageData.relativePath)
}
// или возвращаем данные для объединения
async transformPageData(pageData, { siteConfig }) {
return {
contributors: await getPageContributors(pageData.relativePath)
}
}
}
```
```ts
interface TransformPageContext {
siteConfig: SiteConfig
}
```

@ -91,7 +91,7 @@ import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/*.md', /* options */)
```
该辅助函数接受一个相对于[项目根目录](./routing#project-root)的 glob 模式,并返回一个 `{ watch, load }` 数据加载对象,该对象可以用作数据加载文件中的默认导出。它还基于文件修改时间戳实现了缓存以提高开发性能。
该辅助函数接受一个相对于[源目录](./routing#source-directory)的 glob 模式,并返回一个 `{ watch, load }` 数据加载对象,该对象可以用作数据加载文件中的默认导出。它还基于文件修改时间戳实现了缓存以提高开发性能。
请注意,数据加载仅适用于 Markdown 文件——匹配的非 Markdown 文件将被跳过。

@ -156,7 +156,7 @@ Cache-Control: max-age=31536000,immutable
uses: actions/checkout@v4
with:
fetch-depth: 0 # 如果未启用 lastUpdated则不需要
# - uses: pnpm/action-setup@v2 # 如果使用 pnpm请取消注释
# - uses: pnpm/action-setup@v3 # 如果使用 pnpm请取消注释
# - uses: oven-sh/setup-bun@v1 # 如果使用 Bun请取消注释
- name: Setup Node
uses: actions/setup-node@v4

@ -71,8 +71,12 @@ $ npx vitepress init
$ pnpm vitepress init
```
```sh [yarn]
$ yarn vitepress init
```
```sh [bun]
$ bunx vitepress init
$ bun vitepress init
```
:::
@ -182,11 +186,15 @@ $ npx vitepress dev docs
```
```sh [pnpm]
$ pnpm exec vitepress dev docs
$ pnpm vitepress dev docs
```
```sh [yarn]
$ yarn vitepress dev docs
```
```sh [bun]
$ bunx vitepress dev docs
$ bun vitepress dev docs
```
:::

@ -2,9 +2,9 @@
可以通过命令行输入 `vitepress build --mpa` 或在配置文件中指定 `mpa: true` 配置选项来启用 MPA (Multi-Page Application) 模式。
在 MPA 模式下,所有页面都会默认不包含任何 JavaScript。因此站点可能评估工具中获得更好的初始访问性能分数。
在 MPA 模式下,所有页面都默认不会包含任何 JavaScript。因此站点也许可以在评估工具中获得更好的初始访问性能分数。
但是,由于 SPA 导航的缺失跨页面链接将导致重新加载整个页面。MPA 模式下的导航不会像 SPA 模式那样立即响应。
但是,由于缺少 SPA 路由,在 MPA 模式下切换页面时会重新加载整个页面,而不会像 SPA 模式那样立即响应。
同时请注意,默认情况下不使用 JavaScript 意味着你实际上只是将 Vue 作为服务器端模板语言。浏览器不会附加任何事件处理程序,因此将不会有任何交互性。要加载客户端 JavaScript需要使用特殊的 `<script client>` 标签:
@ -20,4 +20,4 @@ document.querySelector('h1').addEventListener('click', () => {
`<script client>` 是 VitePress 独有的功能,而不是 Vue 的功能。它可以在 `.md``.vue` 文件中使用,但只能在 MPA 模式下使用。所有主题组件中的客户端脚本将被打包在一起,而特定页面的客户端脚本将会分开处理。
请注意,`<script client>` **不会被视为 Vue 组件代码**:它将是普通的 JavaScript 模块。因此,只有在站点需要绝对最小的客户端交互性时,才应该使用 MPA 模式。
请注意,`<script client>` **不会被视为 Vue 组件代码**,它只是普通的 JavaScript 模块。因此,只有在站点需要极少的客户端交互时,才应该使用 MPA 模式。

@ -34,11 +34,11 @@ VitePress 旨在使用 Markdown 生成内容时提供出色的开发体验。
## 性能 {#performance}
与许多传统的 SSG 不同,VitePress 生成的站点实际上是一个[单页应用程序](https://en.wikipedia.org/wiki/Single-page_application) (SPA)。
与许多传统的 SSG 不同,每次导航都会导致页面完全重新加载VitePress 生成的网站在初次访问时提供静态 HTML但它变成了[单页应用程序](https://en.wikipedia.org/wiki/Single-page_application)SPA进行站点内的后续导航。我们认为这种模式为性能提供了最佳平衡
- **快速的初始加载**
对任何页面的初次访问都将会是静态的、预呈现的 HTML以实现极快的加载速度和最佳的 SEO。然后页面加载一个 JavaScript bundle将页面变成 Vue SPA (这被称为“激活”)。激活是非常快的:在 [PageSpeed Insights](https://pagespeed.web.dev/report?url=https%3A%2F%2Fvitepress.dev%2F) 上,典型的 VitePress 站点即使在网络速度较慢的低端移动设备上也能获得近乎完美的性能分数。
对任何页面的初次访问都将会是静态的、预呈现的 HTML以实现极快的加载速度和最佳的 SEO。然后页面加载一个 JavaScript bundle将页面变成 Vue SPA (这被称为“激活”)。与 SPA 激活缓慢的常见假设不同,由于 Vue 3 良好的原始性能和编译优化,这个过程实际上非常快。在 [PageSpeed Insights](https://pagespeed.web.dev/report?url=https%3A%2F%2Fvitepress.dev%2F) 上,典型的 VitePress 站点即使在网络速度较慢的低端移动设备上也能获得近乎完美的性能分数。
- **加载完成后可以快速切换**

@ -7,9 +7,12 @@ titleTemplate: 由 Vite 和 Vue 驱动的静态站点生成器
hero:
name: VitePress
text: 由 Vite 和 Vue 驱动的静态站点生成器
tagline: 简单、强大、快速。就是你想要的现代 SSG 框架!
tagline: 将 Markdown 变成优雅的文档,只需几分钟
actions:
- theme: brand
text: 什么是 VitePress?
link: /zh/guide/what-is-vitepress
- theme: alt
text: 快速开始
link: /zh/guide/getting-started
- theme: alt

@ -191,5 +191,5 @@ npx vitepress init
````
::: info
VitePress 并不总是为 `layout: home` 页面里额外的内容自动添加样式。要回到以前的行为,可以在 fortmatter 中添加 `markdownStyles: false`
VitePress 并不总是为 `layout: home` 页面里额外的内容自动添加样式。要回到以前的行为,可以在 frontmatter 中添加 `markdownStyles: false`
:::

@ -377,7 +377,7 @@ export default {
- 类型:`string`
- 默认值: `.`
markdown 页面的目录,相对于项目根目录。另请参阅[根目录和源目录](../guide/routing#root-and-source-directory)。
相对于项目根目录的 markdown 文件所在的文件夹。另请参阅[根目录和源目录](../guide/routing#root-and-source-directory)。
```ts
export default {
@ -390,7 +390,7 @@ export default {
- 类型:`string`
- 默认值: `undefined`
用于匹配应作为源内容输出的 markdown 文件的 [全局模式](https://github.com/mrmlnc/fast-glob#pattern-syntax)。
用于匹配应排除作为源内容输出的 markdown 文件,语法详见 [glob pattern](https://github.com/mrmlnc/fast-glob#pattern-syntax)。
```ts
export default {
@ -471,6 +471,13 @@ export default {
}
```
### metaChunk <Badge type="warning" text="experimental" />
- 类型:`boolean`
- 默认值:`false`
当设置为 `true` 时,将页面元数据提取到单独的 JavaScript 块中,而不是内联在初始 HTML 中。这使每个页面的 HTML 负载更小,并使页面元数据可缓存,从而当站点中有很多页面时可以减少服务器带宽。
### mpa <Badge type="warning" text="experimental" />
- 类型:`boolean`

@ -0,0 +1,34 @@
/**
* vue-demi v0.14.7
* Copyright (c) 2020-present, Anthony Fu
* @license MIT
*/
import * as Vue from 'vue'
var isVue2 = false
var isVue3 = true
var Vue2 = undefined
function install() {}
export function set(target, key, val) {
if (Array.isArray(target)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
target[key] = val
return val
}
export function del(target, key) {
if (Array.isArray(target)) {
target.splice(key, 1)
return
}
delete target[key]
}
export * from 'vue'
export { Vue, Vue2, isVue2, isVue3, install }

@ -1,11 +1,23 @@
{
"name": "vitepress",
"version": "1.0.0-rc.45",
"version": "1.1.0",
"description": "Vite & Vue powered static site generator",
"keywords": [
"vite",
"vue",
"vitepress"
],
"homepage": "https://github.com/vuejs/vitepress/tree/main/#readme",
"bugs": {
"url": "https://github.com/vuejs/vitepress/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vuejs/vitepress.git"
},
"license": "MIT",
"author": "Evan You",
"type": "module",
"packageManager": "pnpm@8.15.3",
"main": "dist/node/index.js",
"types": "types/index.d.ts",
"exports": {
".": {
"types": "./types/index.d.ts",
@ -24,8 +36,13 @@
"./theme-without-fonts": {
"types": "./theme-without-fonts.d.ts",
"default": "./dist/client/theme-default/without-fonts.js"
},
"./vue-demi": {
"default": "./lib/vue-demi.mjs"
}
},
"main": "dist/node/index.js",
"types": "types/index.d.ts",
"bin": {
"vitepress": "bin/vitepress.js"
},
@ -36,23 +53,9 @@
"template",
"client.d.ts",
"theme.d.ts",
"theme-without-fonts.d.ts"
"theme-without-fonts.d.ts",
"lib"
],
"repository": {
"type": "git",
"url": "git+https://github.com/vuejs/vitepress.git"
},
"keywords": [
"vite",
"vue",
"vitepress"
],
"author": "Evan You",
"license": "MIT",
"homepage": "https://github.com/vuejs/vitepress/tree/main/#readme",
"bugs": {
"url": "https://github.com/vuejs/vitepress/issues"
},
"scripts": {
"dev": "rimraf dist && run-s dev:shared dev:start",
"dev:start": "run-p dev:client dev:node dev:watch",
@ -91,50 +94,45 @@
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
"release": "node scripts/release.js"
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
},
"lint-staged": {
"*": "prettier --write --ignore-unknown",
"package.json": "sort-package-json"
},
"dependencies": {
"@docsearch/css": "^3.5.2",
"@docsearch/js": "^3.5.2",
"@types/markdown-it": "^13.0.7",
"@docsearch/css": "^3.6.0",
"@docsearch/js": "^3.6.0",
"@shikijs/core": "^1.3.0",
"@shikijs/transformers": "^1.3.0",
"@types/markdown-it": "^14.0.1",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/devtools-api": "^7.0.14",
"@vueuse/core": "^10.7.2",
"@vueuse/integrations": "^10.7.2",
"@vue/devtools-api": "^7.0.25",
"@vueuse/core": "^10.9.0",
"@vueuse/integrations": "^10.9.0",
"focus-trap": "^7.5.4",
"mark.js": "8.11.1",
"minisearch": "^6.3.0",
"shiki": "^1.1.5",
"@shikijs/core": "^1.1.5",
"@shikijs/transformers": "^1.1.5",
"vite": "^5.1.3",
"vue": "^3.4.19"
},
"peerDependencies": {
"markdown-it-mathjax3": "^4.3.2",
"postcss": "^8.4.35"
},
"peerDependenciesMeta": {
"markdown-it-mathjax3": {
"optional": true
},
"postcss": {
"optional": true
}
"shiki": "^1.3.0",
"vite": "^5.2.8",
"vue": "^3.4.22"
},
"devDependencies": {
"@clack/prompts": "^0.7.0",
"@mdit-vue/plugin-component": "^2.0.0",
"@mdit-vue/plugin-frontmatter": "^2.0.0",
"@mdit-vue/plugin-headers": "^2.0.0",
"@mdit-vue/plugin-sfc": "^2.0.0",
"@mdit-vue/plugin-title": "^2.0.0",
"@mdit-vue/plugin-toc": "^2.0.0",
"@mdit-vue/shared": "^2.0.0",
"@mdit-vue/plugin-component": "^2.1.2",
"@mdit-vue/plugin-frontmatter": "^2.1.2",
"@mdit-vue/plugin-headers": "^2.1.2",
"@mdit-vue/plugin-sfc": "^2.1.2",
"@mdit-vue/plugin-title": "^2.1.2",
"@mdit-vue/plugin-toc": "^2.1.2",
"@mdit-vue/shared": "^2.1.2",
"@polka/compression": "1.0.0-next.25",
"@rollup/plugin-alias": "^5.1.0",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-replace": "^5.0.5",
"@types/compression": "^1.7.5",
"@types/cross-spawn": "^6.0.6",
"@types/debug": "^4.1.12",
"@types/escape-html": "^1.0.4",
@ -142,30 +140,29 @@
"@types/lodash.template": "^4.5.3",
"@types/mark.js": "^8.11.12",
"@types/markdown-it-attrs": "^4.1.3",
"@types/markdown-it-container": "^2.0.9",
"@types/markdown-it-emoji": "^2.0.4",
"@types/micromatch": "^4.0.6",
"@types/markdown-it-container": "^2.0.10",
"@types/markdown-it-emoji": "^2.0.5",
"@types/micromatch": "^4.0.7",
"@types/minimist": "^1.2.5",
"@types/node": "^20.11.19",
"@types/node": "^20.12.7",
"@types/postcss-prefix-selector": "^1.16.3",
"@types/prompts": "^2.4.9",
"@vue/shared": "^3.4.19",
"@vue/shared": "^3.4.22",
"chokidar": "^3.6.0",
"compression": "^1.7.4",
"conventional-changelog-cli": "^4.1.0",
"cross-spawn": "^7.0.3",
"debug": "^4.3.4",
"esbuild": "^0.20.1",
"esbuild": "^0.20.2",
"escape-html": "^1.0.3",
"execa": "^8.0.1",
"fast-glob": "^3.3.2",
"fs-extra": "^11.2.0",
"get-port": "^7.0.0",
"get-port": "^7.1.0",
"gray-matter": "^4.0.3",
"lint-staged": "^15.2.2",
"lodash.template": "^4.5.0",
"lru-cache": "^10.2.0",
"markdown-it": "^14.0.0",
"markdown-it": "^14.1.0",
"markdown-it-anchor": "^8.6.7",
"markdown-it-attrs": "^4.1.6",
"markdown-it-container": "^4.0.0",
@ -173,51 +170,61 @@
"markdown-it-mathjax3": "^4.3.2",
"micromatch": "^4.0.5",
"minimist": "^1.2.8",
"nanoid": "^5.0.5",
"nanoid": "^5.0.7",
"npm-run-all": "^4.1.5",
"ora": "^8.0.1",
"p-map": "^7.0.1",
"path-to-regexp": "^6.2.1",
"p-map": "^7.0.2",
"path-to-regexp": "^6.2.2",
"picocolors": "^1.0.0",
"pkg-dir": "^8.0.0",
"playwright-chromium": "^1.41.2",
"polka": "1.0.0-next.24",
"playwright-chromium": "^1.43.1",
"polka": "1.0.0-next.25",
"postcss-prefix-selector": "^1.16.0",
"prettier": "^3.2.5",
"prompts": "^2.4.2",
"punycode": "^2.3.1",
"rimraf": "^5.0.5",
"rollup": "^4.12.0",
"rollup": "^4.14.3",
"rollup-plugin-dts": "^6.1.0",
"rollup-plugin-esbuild": "^6.1.1",
"semver": "^7.6.0",
"simple-git-hooks": "^2.9.0",
"simple-git-hooks": "^2.11.1",
"sirv": "^2.0.4",
"sitemap": "^7.1.1",
"sort-package-json": "^2.10.0",
"supports-color": "^9.4.0",
"typescript": "^5.3.3",
"vitest": "^1.3.0",
"vue-tsc": "^1.8.27",
"typescript": "^5.4.5",
"vitest": "^1.5.0",
"vue-tsc": "^2.0.13",
"wait-on": "^7.2.0"
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
"peerDependencies": {
"markdown-it-mathjax3": "^4",
"postcss": "^8"
},
"lint-staged": {
"*": [
"prettier --write --ignore-unknown"
]
"peerDependenciesMeta": {
"markdown-it-mathjax3": {
"optional": true
},
"postcss": {
"optional": true
}
},
"packageManager": "pnpm@8.15.7",
"pnpm": {
"overrides": {
"ora>string-width": "^5"
},
"peerDependencyRules": {
"ignoreMissing": [
"@algolia/client-search",
"search-insights",
"postcss"
]
},
"overrides": {
"ora>string-width": "^5"
},
"patchedDependencies": {
"@types/markdown-it@14.0.1": "patches/@types__markdown-it@14.0.1.patch",
"markdown-it-anchor@8.6.7": "patches/markdown-it-anchor@8.6.7.patch"
}
}
}

@ -0,0 +1,13 @@
diff --git a/package.json b/package.json
index 3b3cdfc4427a1a64fdd3b37604a7174e4646e423..afaea16b115554fcf15a905642562e881ece7ca6 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,7 @@
}
],
"main": "",
- "types": "index.d.ts",
+ "types": "index.d.mts",
"exports": {
".": {
"import": "./index.d.mts",

@ -0,0 +1,29 @@
diff --git a/types/index.d.ts b/types/index.d.ts
index 7c94aae194faa66ca006ace98cdb0dee82a3e471..0377cace7c4a9653d4ecf963babffd4bd68494b0 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -1,10 +1,10 @@
-import MarkdownIt = require('markdown-it');
-import Token = require('markdown-it/lib/token');
-import State = require('markdown-it/lib/rules_core/state_core');
+import MarkdownIt from 'markdown-it';
+import Token from 'markdown-it/lib/token.mjs';
+import StateCore from 'markdown-it/lib/rules_core/state_core.mjs';
declare namespace anchor {
- export type RenderHref = (slug: string, state: State) => string;
- export type RenderAttrs = (slug: string, state: State) => Record<string, string | number>;
+ export type RenderHref = (slug: string, state: StateCore) => string;
+ export type RenderAttrs = (slug: string, state: StateCore) => Record<string, string | number>;
export interface PermalinkOptions {
class?: string,
@@ -37,7 +37,7 @@ declare namespace anchor {
placement?: 'before' | 'after'
}
- export type PermalinkGenerator = (slug: string, opts: PermalinkOptions, state: State, index: number) => void;
+ export type PermalinkGenerator = (slug: string, opts: PermalinkOptions, state: StateCore, index: number) => void;
export interface AnchorInfo {
slug: string;

File diff suppressed because it is too large Load Diff

@ -6,12 +6,14 @@ import {
readonly,
ref,
shallowRef,
watch,
type InjectionKey,
type Ref
} from 'vue'
import {
APPEARANCE_KEY,
createTitle,
inBrowser,
resolveSiteDataByRoute,
type PageData,
type SiteData
@ -47,6 +49,10 @@ export interface VitePressData<T = any> {
dir: Ref<string>
localeIndex: Ref<string>
isDark: Ref<boolean>
/**
* Current location hash
*/
hash: Ref<string>
}
// site data is a singleton
@ -82,6 +88,21 @@ export function initData(route: Route): VitePressData {
})
: ref(false)
const hashRef = ref(inBrowser ? location.hash : '')
if (inBrowser) {
window.addEventListener('hashchange', () => {
hashRef.value = location.hash
})
}
watch(
() => route.data,
() => {
hashRef.value = inBrowser ? location.hash : ''
}
)
return {
site,
theme: computed(() => site.value.themeConfig),
@ -95,7 +116,8 @@ export function initData(route: Route): VitePressData {
description: computed(
() => route.data.description || site.value.description
),
isDark
isDark,
hash: computed(() => hashRef.value)
}
}

@ -66,16 +66,10 @@ export function createRouter(
async function go(href: string = inBrowser ? location.href : '/') {
href = normalizeHref(href)
if ((await router.onBeforeRouteChange?.(href)) === false) return
if (inBrowser) {
const currentUrl = new URL(location.href)
if (href !== normalizeHref(currentUrl.href)) {
// save scroll position before changing url
history.replaceState({ scrollPosition: window.scrollY }, document.title)
history.pushState(null, '', href)
if (new URL(href, fakeHost).hash !== currentUrl.hash) {
window.dispatchEvent(new Event('hashchange'))
}
}
if (inBrowser && href !== normalizeHref(location.href)) {
// save scroll position before changing url
history.replaceState({ scrollPosition: window.scrollY }, '')
history.pushState({}, '', href)
}
await loadPage(href)
await router.onAfterRouteChanged?.(href)
@ -117,7 +111,7 @@ export function createRouter(
if (actualPathname !== targetLoc.pathname) {
targetLoc.pathname = actualPathname
href = actualPathname + targetLoc.search + targetLoc.hash
history.replaceState(null, '', href)
history.replaceState({}, '', href)
}
if (targetLoc.hash && !scrollPosition) {
@ -168,6 +162,9 @@ export function createRouter(
}
if (inBrowser) {
if (history.state === null) {
history.replaceState({}, '')
}
window.addEventListener(
'click',
(e) => {
@ -209,9 +206,14 @@ export function createRouter(
// scroll between hash anchors in the same page
// avoid duplicate history entries when the hash is same
if (hash !== currentUrl.hash) {
history.pushState(null, '', href)
history.pushState({}, '', href)
// still emit the event so we can listen to it in themes
window.dispatchEvent(new Event('hashchange'))
window.dispatchEvent(
new HashChangeEvent('hashchange', {
oldURL: currentUrl.href,
newURL: href
})
)
}
if (hash) {
// use smooth scroll when clicking on header anchor links
@ -229,6 +231,9 @@ export function createRouter(
)
window.addEventListener('popstate', async (e) => {
if (e.state === null) {
return
}
await loadPage(
normalizeHref(location.href),
(e.state && e.state.scrollPosition) || 0

@ -59,7 +59,7 @@ function scrollToTop() {
ref="main"
>
<button @click="toggle" :class="{ open }" v-if="headers.length > 0">
{{ resolveTitle(theme) }}
<span class="menu-text">{{ resolveTitle(theme) }}</span>
<span class="vpi-chevron-right icon" />
</button>
<button @click="scrollToTop" v-else>

@ -2,7 +2,6 @@
import { useWindowScroll } from '@vueuse/core'
import { ref, watchPostEffect } from 'vue'
import { useData } from '../composables/data'
import { useLocalNav } from '../composables/local-nav'
import { useSidebar } from '../composables/sidebar'
import VPNavBarAppearance from './VPNavBarAppearance.vue'
import VPNavBarExtra from './VPNavBarExtra.vue'
@ -23,7 +22,6 @@ defineEmits<{
const { y } = useWindowScroll()
const { hasSidebar } = useSidebar()
const { hasLocalNav } = useLocalNav()
const { frontmatter } = useData()
const classes = ref<Record<string, boolean>>({})
@ -31,8 +29,8 @@ const classes = ref<Record<string, boolean>>({})
watchPostEffect(() => {
classes.value = {
'has-sidebar': hasSidebar.value,
'has-local-nav': hasLocalNav.value,
top: frontmatter.value.layout === 'home' && y.value === 0,
'home': frontmatter.value.layout === 'home',
'top': y.value === 0,
}
})
</script>
@ -79,16 +77,16 @@ watchPostEffect(() => {
transition: background-color 0.5s;
}
.VPNavBar.has-local-nav {
.VPNavBar:not(.home) {
background-color: var(--vp-nav-bg-color);
}
@media (min-width: 960px) {
.VPNavBar.has-local-nav {
.VPNavBar:not(.home) {
background-color: transparent;
}
.VPNavBar:not(.has-sidebar):not(.top) {
.VPNavBar:not(.has-sidebar):not(.home.top) {
background-color: var(--vp-nav-bg-color);
}
}
@ -188,12 +186,12 @@ watchPostEffect(() => {
}
@media (min-width: 960px) {
.VPNavBar:not(.top) .content-body {
.VPNavBar:not(.home.top) .content-body {
position: relative;
background-color: var(--vp-nav-bg-color);
}
.VPNavBar:not(.has-sidebar):not(.top) .content-body {
.VPNavBar:not(.has-sidebar):not(.home.top) .content-body {
background-color: transparent;
}
}
@ -253,16 +251,16 @@ watchPostEffect(() => {
transition: background-color 0.5s;
}
.VPNavBar.has-local-nav .divider-line {
.VPNavBar:not(.home) .divider-line {
background-color: var(--vp-c-gutter);
}
@media (min-width: 960px) {
.VPNavBar:not(.top) .divider-line {
.VPNavBar:not(.home.top) .divider-line {
background-color: var(--vp-c-gutter);
}
.VPNavBar:not(.has-sidebar):not(.top) .divider {
.VPNavBar:not(.has-sidebar):not(.home.top) .divider {
background-color: var(--vp-c-gutter);
}
}

@ -17,9 +17,8 @@ const closeScreen = inject('close-screen') as () => void
:target="item.target"
:rel="item.rel"
@click="closeScreen"
>
{{ item.text }}
</VPLink>
v-html="item.text"
/>
</template>
<style scoped>

@ -82,7 +82,7 @@ function onCaretClick() {
<component v-else :is="textTag" class="text" v-html="item.text" />
<div
v-if="item.collapsed != null"
v-if="item.collapsed != null && item.items && item.items.length"
class="caret"
role="button"
aria-label="toggle section"

@ -1,12 +0,0 @@
import { inBrowser } from '../../shared'
import { ref } from 'vue'
const hashRef = ref(inBrowser ? location.hash : '')
if (inBrowser) {
window.addEventListener('hashchange', () => {
hashRef.value = location.hash
})
}
export { hashRef }

@ -1,13 +1,12 @@
import { computed } from 'vue'
import { ensureStartingSlash } from '../support/utils'
import { useData } from './data'
import { hashRef } from './hash'
export function useLangs({
removeCurrent = true,
correspondingLink = false
} = {}) {
const { site, localeIndex, page, theme } = useData()
const { site, localeIndex, page, theme, hash } = useData()
const currentLang = computed(() => ({
label: site.value.locales[localeIndex.value]?.label,
link:
@ -29,7 +28,7 @@ export function useLangs({
currentLang.value.link.length - 1
),
!site.value.cleanUrls
) + hashRef.value
) + hash.value
}
)
)

@ -8,7 +8,10 @@ export function usePrevNext() {
return computed(() => {
const sidebar = getSidebar(theme.value.sidebar, page.value.relativePath)
const candidates = getFlatSideBarLinks(sidebar)
const links = getFlatSideBarLinks(sidebar)
// ignore inner-page links with hashes
const candidates = uniqBy(links, (link) => link.link.replace(/[?#].*$/, ''))
const index = candidates.findIndex((link) => {
return isActive(page.value.relativePath, link.link)
@ -61,3 +64,11 @@ export function usePrevNext() {
}
})
}
function uniqBy<T>(array: T[], keyFn: (item: T) => any): T[] {
const seen = new Set()
return array.filter((item) => {
const k = keyFn(item)
return seen.has(k) ? false : seen.add(k)
})
}

@ -18,7 +18,6 @@ import {
getSidebarGroups
} from '../support/sidebar'
import { useData } from './data'
import { hashRef } from './hash'
export interface SidebarControl {
collapsed: Ref<boolean>
@ -138,7 +137,7 @@ export function useCloseSidebarOnEscape(
export function useSidebarControl(
item: ComputedRef<DefaultTheme.SidebarItem>
): SidebarControl {
const { page } = useData()
const { page, hash } = useData()
const collapsed = ref(false)
@ -155,7 +154,7 @@ export function useSidebarControl(
isActiveLink.value = isActive(page.value.relativePath, item.value.link)
}
watch([page, item, hashRef], updateIsActiveLink)
watch([page, item, hash], updateIsActiveLink)
onMounted(updateIsActiveLink)
const hasActiveLink = computed(() => {

@ -7,153 +7,143 @@ html body {
/* webfont-marker-end */
@font-face {
font-family: 'Inter var';
font-weight: 100 900;
font-display: swap;
font-family: Inter;
font-style: normal;
font-named-instance: 'Regular';
src: url('../fonts/inter-roman-cyrillic.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
font-family: 'Inter var';
font-weight: 100 900;
font-display: swap;
font-style: normal;
font-named-instance: 'Regular';
src: url('../fonts/inter-roman-cyrillic-ext.woff2') format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F,
U+FE2E-FE2F;
}
@font-face {
font-family: 'Inter var';
font-family: Inter;
font-style: normal;
font-weight: 100 900;
font-display: swap;
font-style: normal;
font-named-instance: 'Regular';
src: url('../fonts/inter-roman-greek.woff2') format('woff2');
unicode-range: U+0370-03FF;
src: url('../fonts/inter-roman-cyrillic.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
font-family: 'Inter var';
font-family: Inter;
font-style: normal;
font-weight: 100 900;
font-display: swap;
font-style: normal;
font-named-instance: 'Regular';
src: url('../fonts/inter-roman-greek-ext.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
@font-face {
font-family: 'Inter var';
font-family: Inter;
font-style: normal;
font-weight: 100 900;
font-display: swap;
font-style: normal;
font-named-instance: 'Regular';
src: url('../fonts/inter-roman-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
U+FEFF, U+FFFD;
src: url('../fonts/inter-roman-greek.woff2') format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
U+03A3-03FF;
}
@font-face {
font-family: 'Inter var';
font-family: Inter;
font-style: normal;
font-weight: 100 900;
font-display: swap;
font-style: normal;
font-named-instance: 'Regular';
src: url('../fonts/inter-roman-latin-ext.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
src: url('../fonts/inter-roman-vietnamese.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1,
U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
U+1EA0-1EF9, U+20AB;
}
@font-face {
font-family: 'Inter var';
font-family: Inter;
font-style: normal;
font-weight: 100 900;
font-display: swap;
font-style: normal;
font-named-instance: 'Regular';
src: url('../fonts/inter-roman-vietnamese.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1,
U+01AF-01B0, U+1EA0-1EF9, U+20AB;
src: url('../fonts/inter-roman-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Inter var';
font-family: Inter;
font-style: normal;
font-weight: 100 900;
font-display: swap;
font-style: italic;
font-named-instance: 'Italic';
src: url('../fonts/inter-italic-cyrillic.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
src: url('../fonts/inter-roman-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Inter var';
font-family: Inter;
font-style: italic;
font-weight: 100 900;
font-display: swap;
font-style: italic;
font-named-instance: 'Italic';
src: url('../fonts/inter-italic-cyrillic-ext.woff2') format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F,
U+FE2E-FE2F;
}
@font-face {
font-family: 'Inter var';
font-family: Inter;
font-style: italic;
font-weight: 100 900;
font-display: swap;
font-style: italic;
font-named-instance: 'Italic';
src: url('../fonts/inter-italic-greek.woff2') format('woff2');
unicode-range: U+0370-03FF;
src: url('../fonts/inter-italic-cyrillic.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
font-family: 'Inter var';
font-family: Inter;
font-style: italic;
font-weight: 100 900;
font-display: swap;
font-style: italic;
font-named-instance: 'Italic';
src: url('../fonts/inter-italic-greek-ext.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
@font-face {
font-family: 'Inter var';
font-family: Inter;
font-style: italic;
font-weight: 100 900;
font-display: swap;
font-style: italic;
font-named-instance: 'Italic';
src: url('../fonts/inter-italic-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
U+FEFF, U+FFFD;
src: url('../fonts/inter-italic-greek.woff2') format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
U+03A3-03FF;
}
@font-face {
font-family: 'Inter var';
font-family: Inter;
font-style: italic;
font-weight: 100 900;
font-display: swap;
src: url('../fonts/inter-italic-vietnamese.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1,
U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
U+1EA0-1EF9, U+20AB;
}
@font-face {
font-family: Inter;
font-style: italic;
font-named-instance: 'Italic';
font-weight: 100 900;
font-display: swap;
src: url('../fonts/inter-italic-latin-ext.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Inter var';
font-family: Inter;
font-style: italic;
font-weight: 100 900;
font-display: swap;
font-style: italic;
font-named-instance: 'Italic';
src: url('../fonts/inter-italic-vietnamese.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1,
U+01AF-01B0, U+1EA0-1EF9, U+20AB;
src: url('../fonts/inter-italic-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Chinese quotes rendering fix. 中英文弯引号共享 Unicode 码位,确保引号使用中文字体渲染 */
@ -163,3 +153,5 @@ html body {
local('Source Han Sans SC');
unicode-range: U+2018, U+2019, U+201C, U+201D; /* 分别是 ‘’“” */
}
/* Generate the subsetted fonts using: `pyftsubset <file>.woff2 --unicodes="<range>" --output-file="inter-<style>-<subset>.woff2" --flavor=woff2` */

@ -261,12 +261,13 @@
* -------------------------------------------------------------------------- */
:root {
--vp-font-family-base: 'Chinese Quotes', 'Inter var', 'Inter', ui-sans-serif,
system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', Helvetica, Arial, 'Noto Sans', sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
--vp-font-family-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco,
Consolas, 'Liberation Mono', 'Courier New', monospace;
--vp-font-family-base: 'Chinese Quotes', Inter, ui-sans-serif, system-ui,
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
--vp-font-family-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
font-optical-sizing: auto;
}
/**

@ -41,6 +41,10 @@ export function resolveAliases(
{
find: /^vitepress\/theme$/,
replacement: join(DIST_CLIENT_PATH, '/theme-default/index.js')
},
{
find: /^vue-demi$/,
replacement: require.resolve('vitepress/vue-demi')
}
]

@ -30,9 +30,6 @@ export interface ScaffoldOptions {
const getPackageManger = () => {
const name = process.env?.npm_config_user_agent || 'npm'
if (name === 'npm') {
return 'npm'
}
return name.split('/')[0]
}
@ -211,9 +208,9 @@ export function scaffold({
`${getPackageManger()} run docs:dev`
)} and start writing.${tip}`
} else {
const execCommand = getPackageManger() === 'bun' ? 'bunx' : 'npx'
const pm = getPackageManger()
return `You're all set! Now run ${cyan(
`${execCommand} vitepress dev${dir}`
`${pm === 'npm' ? 'npx' : pm} vitepress dev${dir}`
)} and start writing.${tip}`
}
}

@ -15,9 +15,10 @@ import { titlePlugin } from '@mdit-vue/plugin-title'
import { tocPlugin, type TocPluginOptions } from '@mdit-vue/plugin-toc'
import { slugify } from '@mdit-vue/shared'
import MarkdownIt from 'markdown-it'
import type { Options } from 'markdown-it'
import anchorPlugin from 'markdown-it-anchor'
import attrsPlugin from 'markdown-it-attrs'
// @ts-ignore
// @ts-expect-error: types of markdown-it-emoji are not up-to-date
import { full as emojiPlugin } from 'markdown-it-emoji'
import type {
BuiltinTheme,
@ -47,7 +48,7 @@ export type ThemeOptions =
dark: ThemeRegistrationAny | BuiltinTheme
}
export interface MarkdownOptions extends MarkdownIt.Options {
export interface MarkdownOptions extends Options {
/* ==================== General Options ==================== */
/**

@ -1,6 +1,6 @@
import type MarkdownIt from 'markdown-it'
import type { RenderRule } from 'markdown-it/lib/renderer'
import type Token from 'markdown-it/lib/token'
import type { RenderRule } from 'markdown-it/lib/renderer.mjs'
import type Token from 'markdown-it/lib/token.mjs'
import container from 'markdown-it-container'
import { nanoid } from 'nanoid'
import {

@ -1,6 +1,6 @@
import fs from 'fs-extra'
import type MarkdownIt from 'markdown-it'
import type { RuleBlock } from 'markdown-it/lib/parser_block'
import type { RuleBlock } from 'markdown-it/lib/parser_block.mjs'
import path from 'path'
import type { MarkdownEnv } from '../../shared'

@ -384,6 +384,7 @@ export async function createVitePressPlugin(
try {
await resolveUserConfig(siteConfig.root, 'serve', 'development')
} catch (err: any) {
siteConfig.logger.error(err)
return
}

@ -235,8 +235,9 @@ function* splitPageIntoSections(html: string) {
const anchor = headingResult?.[2] ?? ''
const content = result[i + 2]
if (!title || !content) continue
const titles = parentTitles.slice(0, level)
let titles = parentTitles.slice(0, level)
titles[level] = title
titles = titles.filter(Boolean)
yield { anchor, titles, text: getSearchableText(content) }
if (level === 0) {
parentTitles = [title]

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save