docs: revise writing section

pull/2044/head
Evan You 2 years ago
parent b9aed5695a
commit 6c4dea4b63

@ -1,16 +1,16 @@
# Asset Handling
All Markdown files are compiled into Vue components and processed by [Vite](https://github.com/vitejs/vite). You can, **and should**, reference any assets using relative URLs:
All Markdown files are compiled into Vue components and processed by [Vite](https://vitejs.dev/guide/assets.html). You can, **and should**, reference any assets using relative URLs:
```md
![An image](./image.png)
```
You can reference static assets in your markdown files, your `*.vue` components in the theme, styles and plain `.css` files either using absolute public paths (based on project root) or relative paths (based on your file system). The latter is similar to the behavior you are used to if you have used `vue-cli` or webpack's `file-loader`.
You can reference static assets in your markdown files, your `*.vue` components in the theme, styles and plain `.css` files either using absolute public paths (based on project root) or relative paths (based on your file system). The latter is similar to the behavior you are used to if you have used Vite, Vue CLI, or webpack's `file-loader`.
Common image, media, and font filetypes are detected and included as assets automatically.
All referenced assets, including those using absolute paths, will be copied to the dist folder with a hashed file name in the production build. Never-referenced assets will not be copied. Similar to `vue-cli`, image assets smaller than 4kb will be base64 inlined.
All referenced assets, including those using absolute paths, will be copied to the dist folder with a hashed file name in the production build. Never-referenced assets will not be copied. Image assets smaller than 4kb will be base64 inlined - this can be configured via the [`vite`](/reference/site-config#vite) config option.
All **static** path references, including absolute paths, should be based on your working directory structure.

@ -1,228 +1 @@
# Theme Introduction
VitePress comes with its default theme providing many features out of the box. Learn more about each feature on its dedicated page listed below.
- [Nav](/reference/default-theme-nav)
- [Sidebar](/reference/default-theme-sidebar)
- [Prev Next Link](/reference/default-theme-prev-next-link)
- [Edit Link](/reference/default-theme-edit-link)
- [Last Updated](/reference/default-theme-last-updated)
- [Layout](/reference/default-theme-layout)
- [Home Page](/reference/default-theme-home-page)
- [Team Page](/reference/default-theme-team-page)
- [Badge](/reference/default-theme-badge)
- [Footer](/reference/default-theme-footer)
- [Search](/reference/default-theme-search)
- [Carbon Ads](/reference/default-theme-carbon-ads)
If you don't find the features you're looking for, or you would rather create your own theme, you may customize VitePress to fit your requirements. In the following sections, we'll go through each way of customizing the VitePress theme.
## Using a Custom Theme
You can enable a custom theme by adding the `.vitepress/theme/index.js` or `.vitepress/theme/index.ts` file (the "theme entry file").
```
.
├─ docs
│ ├─ .vitepress
│ │ ├─ theme
│ │ │ └─ index.js
│ │ └─ config.js
│ └─ index.md
└─ package.json
```
A VitePress custom theme is simply an object containing four properties and is defined as follows:
```ts
interface Theme {
Layout: Component // Vue 3 component
NotFound?: Component
enhanceApp?: (ctx: EnhanceAppContext) => Awaitable<void>
setup?: () => void
}
interface EnhanceAppContext {
app: App // Vue 3 app instance
router: Router // VitePress router instance
siteData: Ref<SiteData>
}
```
The theme entry file should export the theme as its default export:
```js
// .vitepress/theme/index.js
import Layout from './Layout.vue'
export default {
// root component to wrap each page
Layout,
// this is a Vue 3 functional component
NotFound: () => 'custom 404',
enhanceApp({ app, router, siteData }) {
// app is the Vue 3 app instance from `createApp()`.
// router is VitePress' custom router. `siteData` is
// a `ref` of current site-level metadata.
},
setup() {
// this function will be executed inside VitePressApp's
// setup hook. all composition APIs are available here.
}
}
```
...where the `Layout` component could look like this:
```vue
<!-- .vitepress/theme/Layout.vue -->
<template>
<h1>Custom Layout!</h1>
<!-- this is where markdown content will be rendered -->
<Content />
</template>
```
The default export is the only contract for a custom theme. Inside your custom theme, it works just like a normal Vite + Vue 3 application. Do note the theme also needs to be [SSR-compatible](./using-vue#browser-api-access-restrictions).
To distribute a theme, simply export the object in your package entry. To consume an external theme, import and re-export it from the custom theme entry:
```js
// .vitepress/theme/index.js
import Theme from 'awesome-vitepress-theme'
export default Theme
```
## Extending the Default Theme
If you want to extend and customize the default theme, you can import it from `vitepress/theme` and augment it in a custom theme entry. Here are some examples of common customizations:
### Registering Global Components
```js
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
export default {
...DefaultTheme,
enhanceApp(ctx) {
// extend default theme custom behaviour.
DefaultTheme.enhanceApp(ctx)
// register your custom global components
ctx.app.component('MyGlobalComponent' /* ... */)
}
}
```
Since we are using Vite, you can also leverage Vite's [glob import feature](https://vitejs.dev/guide/features.html#glob-import) to auto register a directory of components.
### Customizing CSS
The default theme CSS is customizable by overriding root level CSS variables:
```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: #646cff;
--vp-c-brand-light: #747bff;
}
```
See [default theme CSS variables](https://github.com/vuejs/vitepress/blob/main/src/clien/reference/default-theme-default/styles/vars.css) that can be overridden.
### Layout Slots
The default theme's `<Layout/>` component has a few slots that can be used to inject content at certain locations of the page. Here's an example of injecting a component into the before outline:
```js
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import MyLayout from './MyLayout.vue'
export default {
...DefaultTheme,
// override the Layout with a wrapper component that
// injects the slots
Layout: MyLayout
}
```
```vue
<!--.vitepress/theme/MyLayout.vue-->
<script setup>
import DefaultTheme from 'vitepress/theme'
const { Layout } = DefaultTheme
</script>
<template>
<Layout>
<template #aside-outline-before>
My custom sidebar top content
</template>
</Layout>
</template>
```
Or you could use render function as well.
```js
// .vitepress/theme/index.js
import { h } from 'vue'
import DefaultTheme from 'vitepress/theme'
import MyComponent from './MyComponent.vue'
export default {
...DefaultTheme,
Layout() {
return h(DefaultTheme.Layout, null, {
'aside-outline-before': () => h(MyComponent)
})
}
}
```
Full list of slots available in the default theme layout:
- When `layout: 'doc'` (default) is enabled via frontmatter:
- `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`
- When `layout: 'home'` is enabled via frontmatter:
- `home-hero-before`
- `home-hero-info`
- `home-hero-image`
- `home-hero-after`
- `home-features-before`
- `home-features-after`
- Always:
- `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`
# Building a Custom Theme

@ -0,0 +1,220 @@
# Extending the Default Theme
VitePress comes with its default theme providing many features out of the box. You can check out the full features in the [Default Theme Config Overview](/reference/default-theme-config).
There are several cases where you may want to extend the default theme:
1. You want to modify the Vue app, for example register global components;
2. You want to tweak the CSS styling;
3. You want to inject custom content into the theme via layout slots.
## Using a Custom Theme
You can enable a custom theme by adding the `.vitepress/theme/index.js` or `.vitepress/theme/index.ts` file (the "theme entry file").
```
.
├─ docs
│ ├─ .vitepress
│ │ ├─ theme
│ │ │ └─ index.js
│ │ └─ config.js
│ └─ index.md
└─ package.json
```
A VitePress custom theme is simply an object containing four properties and is defined as follows:
```ts
interface Theme {
Layout: Component // Vue 3 component
NotFound?: Component
enhanceApp?: (ctx: EnhanceAppContext) => Awaitable<void>
setup?: () => void
}
interface EnhanceAppContext {
app: App // Vue 3 app instance
router: Router // VitePress router instance
siteData: Ref<SiteData>
}
```
The theme entry file should export the theme as its default export:
```js
// .vitepress/theme/index.js
import Layout from './Layout.vue'
export default {
// root component to wrap each page
Layout,
// this is a Vue 3 functional component
NotFound: () => 'custom 404',
enhanceApp({ app, router, siteData }) {
// app is the Vue 3 app instance from `createApp()`.
// router is VitePress' custom router. `siteData` is
// a `ref` of current site-level metadata.
},
setup() {
// this function will be executed inside VitePressApp's
// setup hook. all composition APIs are available here.
}
}
```
...where the `Layout` component could look like this:
```vue
<!-- .vitepress/theme/Layout.vue -->
<template>
<h1>Custom Layout!</h1>
<!-- this is where markdown content will be rendered -->
<Content />
</template>
```
The default export is the only contract for a custom theme. Inside your custom theme, it works just like a normal Vite + Vue 3 application. Do note the theme also needs to be [SSR-compatible](./using-vue#browser-api-access-restrictions).
To distribute a theme, simply export the object in your package entry. To consume an external theme, import and re-export it from the custom theme entry:
```js
// .vitepress/theme/index.js
import Theme from 'awesome-vitepress-theme'
export default Theme
```
## Extending the Default Theme
If you want to extend and customize the default theme, you can import it from `vitepress/theme` and augment it in a custom theme entry. Here are some examples of common customizations:
## Registering Global Components
```js
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
export default {
...DefaultTheme,
enhanceApp(ctx) {
// extend default theme custom behaviour.
DefaultTheme.enhanceApp(ctx)
// register your custom global components
ctx.app.component('MyGlobalComponent' /* ... */)
}
}
```
Since we are using Vite, you can also leverage Vite's [glob import feature](https://vitejs.dev/guide/features.html#glob-import) to auto register a directory of components.
## Customizing CSS
The default theme CSS is customizable by overriding root level CSS variables:
```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: #646cff;
--vp-c-brand-light: #747bff;
}
```
See [default theme CSS variables](https://github.com/vuejs/vitepress/blob/main/src/clien/reference/default-theme-default/styles/vars.css) that can be overridden.
## Layout Slots
The default theme's `<Layout/>` component has a few slots that can be used to inject content at certain locations of the page. Here's an example of injecting a component into the before outline:
```js
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import MyLayout from './MyLayout.vue'
export default {
...DefaultTheme,
// override the Layout with a wrapper component that
// injects the slots
Layout: MyLayout
}
```
```vue
<!--.vitepress/theme/MyLayout.vue-->
<script setup>
import DefaultTheme from 'vitepress/theme'
const { Layout } = DefaultTheme
</script>
<template>
<Layout>
<template #aside-outline-before>
My custom sidebar top content
</template>
</Layout>
</template>
```
Or you could use render function as well.
```js
// .vitepress/theme/index.js
import { h } from 'vue'
import DefaultTheme from 'vitepress/theme'
import MyComponent from './MyComponent.vue'
export default {
...DefaultTheme,
Layout() {
return h(DefaultTheme.Layout, null, {
'aside-outline-before': () => h(MyComponent)
})
}
}
```
Full list of slots available in the default theme layout:
- When `layout: 'doc'` (default) is enabled via frontmatter:
- `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`
- When `layout: 'home'` is enabled via frontmatter:
- `home-hero-before`
- `home-hero-info`
- `home-hero-image`
- `home-hero-after`
- `home-features-before`
- `home-features-after`
- Always:
- `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`

@ -9,7 +9,9 @@ editLink: true
---
```
Between the triple-dashed lines, you can set [predefined variables](/reference/frontmatter-config), or even create custom ones of your own. These variables can be used via the special <code>$frontmatter</code> variable.
Many site or default theme config options have corresponding options in frontmatter. You can use frontmatter to override specific behavior for the current page only. For details, see [Frontmatter Config Reference](/reference/frontmatter-config),
You can also define custom frontmatter data of your own, to be used in dynamic Vue expressions on the page. Frontmatter data can be accessed via the special `$frontmatter` global variable:
Here's an example of how you could use it in your Markdown file:

@ -61,6 +61,10 @@ You will be greeted with a few simple questions:
<img src="./vitepress-init.png" alt="vitepress init screenshot" style="border-radius:8px">
</p>
:::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.
:::
## File Structure
If you are building a standalone VitePress site, you can scaffold the site in your current directory (`./`). However, if you are installing VitePress in an existing project alongside other source code, it is recommended to scaffold the site in a nested directory (e.g. `./docs`) so that it is separate from the rest of the project.

@ -1,8 +1,8 @@
# Using Vue in Markdown
In VitePress, each markdown file is compiled into HTML and then processed as a Vue Single-File Component. This means you can use any Vue features inside the markdown, including dynamic templating, using Vue components, or arbitrary in-page Vue component logic by adding a `<script>` tag.
In VitePress, each Markdown file is compiled into HTML and then processed as a [Vue Single-File Component](https://vuejs.org/guide/scaling-up/sfc.html). This means you can use any Vue features inside the Markdown, including dynamic templating, using Vue components, or arbitrary in-page Vue component logic by adding a `<script>` tag.
It is also important to know that VitePress leverages Vue 3's compiler to automatically detect and optimize the purely static parts of the markdown. Static contents are optimized into single placeholder nodes and eliminated from the page's JavaScript payload. They are also skipped during client-side hydration. In short, you only pay for the dynamic parts on any given page.
It's worth noting that VitePress leverages Vue's compiler to automatically detect and optimize the purely static parts of the Markdown content. Static contents are optimized into single placeholder nodes and eliminated from the page's JavaScript payload for initial visits. They are also skipped during client-side hydration. In short, you only pay for the dynamic parts on any given page.
## Templating
@ -22,7 +22,7 @@ Each Markdown file is first compiled into HTML and then passed on as a Vue compo
### Directives
Directives also work:
Directives also work (note that by design, raw HTML is also valid in Markdown):
**Input**
@ -34,9 +34,32 @@ Directives also work:
<div class="language-text"><pre><code><span v-for="i in 3">{{ i }} </span></code></pre></div>
### Access to Site & Page Data
## `<script>` and `<style>`
You can use the [`useData` helper](/reference/runtime-api#usedata) in a `<script>` block and expose the data to the page.
Root-level `<script>` and `<style>` tags in Markdown files work just like they do in Vue SFCs, including `<script setup>`, `<style scoped>`, `<style module>`, etc. The main difference here is that there is no `<template>` tag: all other root-level content is Markdown.
```html
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
## Markdown Content
The count is: {{ count }}
<button @click="count++">Increment</button>
<style scoped>
button {
color: red;
font-weight: bold;
}
</style>
```
You also have access to VitePress' runtime APIs such as the [`useData` helper](/reference/runtime-api#usedata), which provides access to current page's metadata:
**Input**
@ -56,35 +79,18 @@ const { page } = useData()
{
"path": "/using-vue.html",
"title": "Using Vue in Markdown",
"frontmatter": {}
"frontmatter": {},
...
}
```
## Escaping
By default, fenced code blocks are automatically wrapped with `v-pre`, unless you have set some language with `-vue` suffix like `js-vue` (in that case you can use Vue-style interpolation inside fences). To display raw mustaches or Vue-specific syntax inside inline code snippets or plain text, you need to wrap a paragraph with the `v-pre` custom container:
**Input**
```md
::: v-pre
`{{ This will be displayed as-is }}`
:::
```
**Output**
::: v-pre
`{{ This will be displayed as-is }}`
:::
## Using Components
When you need to have more flexibility, VitePress allows you to extend your authoring toolbox with your own Vue Components.
You can import and use Vue components directly in Markdown files.
### Importing components in markdown
### Importing in Markdown
If your components are going to be used in only a few places, the recommended way to use them is to importing the components in the file where it is used.
If a component is only used by a few pages, it's recommended to explicitly import them where they are used. This allows them to be properly code-split and only loaded when the relevant pages are shown:
```md
<script setup>
@ -102,31 +108,9 @@ This is a .md using a custom component
...
```
### Registering global components in the theme
### Registering Components Globally
If the components are going to be used across several pages in the docs, they can be registered globally in the theme (or as part of extending the default VitePress theme). Check out the [Customization Guide](./custom-theme) for more information.
In `.vitepress/theme/index.js`, the `enhanceApp` function receives the Vue `app` instance so you can [register components](https://vuejs.org/guide/components/registration.html) as you would do in a regular Vue application.
```js
import DefaultTheme from 'vitepress/theme'
export default {
...DefaultTheme,
enhanceApp(ctx) {
DefaultTheme.enhanceApp(ctx)
ctx.app.component('VueClickAwayExample', VueClickAwayExample)
}
}
```
Later in your markdown files, the component can be interleaved between the content
```md
# Vue Click Away
<VueClickAwayExample />
```
If a component is going to be used on most of the pages, they can be registered globally by customizing the Vue app instance. See relevant section in [Extending Default Theme](/guide/extending-default-theme#registering-global-components) for an example.
::: warning IMPORTANT
Make sure a custom component's name either contains a hyphen or is in PascalCase. Otherwise, it will be treated as an inline element and wrapped inside a `<p>` tag, which will lead to hydration mismatch because `<p>` does not allow block elements to be placed inside it.
@ -144,8 +128,61 @@ You can use Vue components in the headers, but note the difference between the f
The HTML wrapped by `<code>` will be displayed as-is; only the HTML that is **not** wrapped will be parsed by Vue.
::: tip
The output HTML is accomplished by [markdown-it](https://github.com/markdown-it/markdown-it), while the parsed headers are handled by VitePress (and used for both the sidebar and document title).
The output HTML is accomplished by [Markdown-it](https://github.com/Markdown-it/Markdown-it), while the parsed headers are handled by VitePress (and used for both the sidebar and document title).
:::
## Escaping
You can escape Vue interpolations by wrapping them in a `<span>` or other elements with the `v-pre` directive:
**Input**
```md
This <span v-pre>{{ will be displayed as-is }}</span>
```
**Output**
<div class="escape-demo">
<p>This <span v-pre>{{ will be displayed as-is }}</span></p>
</div>
Alternatively, you can wrap the entire paragraph in a `v-pre` custom container:
```md
::: v-pre
{{ This will be displayed as-is }}`
:::
```
**Output**
<div class="escape-demo">
::: v-pre
{{ This will be displayed as-is }}
:::
</div>
## Unescape in Code Blocks
By default, all fenced code blocks are automatically wrapped with `v-pre`, so no Vue syntax will be processd inside. To enable Vue-style interpolation inside fences, you can append the language with the `-vue` suffix, e.g. `js-vue`:
**Input**
````md
```js-vue
Hello {{ 1 + 1 }}
```
````
**Output**
```js-vue
Hello {{ 1 + 1 }}
```
## Using CSS Pre-processors
@ -171,39 +208,6 @@ Then you can use the following in Markdown and theme components:
</style>
```
## Script & Style Hoisting
Sometimes you may need to apply some JavaScript or CSS only to the current page. In those cases, you can directly write root-level `<script>` or `<style>` blocks in the Markdown file. These will be hoisted out of the compiled HTML and used as the `<script>` and `<style>` blocks for the resulting Vue single-file component:
<p class="demo" :class="$style.example"></p>
<style module>
.example {
color: #41b883;
}
</style>
<script>
import ComponentInHeader from '../components/ComponentInHeader.vue'
export default {
props: ['slot-key'],
components: { ComponentInHeader },
mounted () {
document.querySelector(`.${this.$style.example}`)
.textContent = 'This is rendered by inline script and styled by inline CSS'
}
}
</script>
## Built-In Components
VitePress provides Built-In Vue Components like `ClientOnly`, check out the [Runtime API Reference](/reference/runtime-api) for more information.
**Also see:**
- [Using Components In Headers](#using-components-in-headers)
## Browser API Access Restrictions
Because VitePress applications are server-rendered in Node.js when generating static builds, any Vue usage must conform to the [universal code requirements](https://vuejs.org/guide/scaling-up/ssr.html). In short, make sure to only access Browser / DOM APIs in `beforeMount` or `mounted` hooks.
@ -284,3 +288,11 @@ Vitepress currently has SSG support for teleports to body only. For other target
<script setup>
import ModalDemo from '../components/ModalDemo.vue'
</script>
<style>
.escape-demo {
border: 1px solid var(--vp-c-border);
border-radius: 8px;
padding: 0 20px;
}
</style>

@ -379,6 +379,12 @@ export default {
}
```
### vite
- Type: `ViteUserConfig`
Pass raw [Vite Config](https://vitejs.dev/config/) to underlying Vite dev server / bundler.
### Build Hooks
VitePress build hooks allow you to add new functionality and behaviors to your website:

Loading…
Cancel
Save