diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index a504fee1..b1db2393 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -61,9 +61,8 @@ function getGuideSidebar() { text: 'Advanced', children: [ { text: 'Frontmatter', link: '/guide/frontmatter' }, - { text: 'Global Computed', link: '/guide/global-computed' }, - { text: 'Global Component', link: '/guide/global-component' }, - { text: 'Customization', link: '/guide/customization' }, + { text: 'Theming', link: '/guide/theming' }, + { text: 'API Reference', link: '/guide/api' }, { text: 'Differences from Vuepress', link: '/guide/differences-from-vuepress' diff --git a/docs/guide/api.md b/docs/guide/api.md new file mode 100644 index 00000000..c6f29bef --- /dev/null +++ b/docs/guide/api.md @@ -0,0 +1,95 @@ +# API Reference + +## Helper Methods + +The following methods are globally importable from `vitepress` and are typically used in custom theme Vue components. However, they are also usable inside `.md` pages because markdown files are compiled into Vue single-file components. + +Methods that start with `use*` indicates that it is a [Vue 3 Composition API](https://v3.vuejs.org/guide/composition-api-introduction.html) function that can only be used inside `setup()` or ` + + +``` + +### `useRoute` + +Returns the current route object with the following type: + +```ts +interface Route { + path: string + data: PageData + component: Component | null +} +``` + +### `useRouter` + +Returns the VitePress router instance so you can programmatically navigate to another page. + +```ts +interface Router { + route: Route + go: (href?: string) => Promise +} +``` + +### `withBase` + +- **Type**: `(path: string) => string` + + Appends the configured [`base`](/config/basics.html#base) to a given URL path. Also see [Base URL](/guide/assets.html#base-url). + +## Global Components + +VitePress comes with few built-in component that can be used globally. You may use these components in your markdown or your custom theme configuration. + +### `` + +The `` component displays the rendered markdown contents. Useful [when creating your own theme](https://vitepress.vuejs.org/guide/customization.html). + +```vue + +``` + +### `` + +The `` component renderes its slot only at client side. + +Because VitePress applications are server-rendered in Node.js when generating static builds, any Vue usage must conform to the universal code requirements. In short, make sure to only access Browser / DOM APIs in beforeMount or mounted hooks. + +If you are using or demoing components that are not SSR-friendly (for example, contain custom directives), you can wrap them inside the `ClientOnly` component. + +```html + + + +``` diff --git a/docs/guide/assets.md b/docs/guide/assets.md index acbbe57d..9b2a5992 100644 --- a/docs/guide/assets.md +++ b/docs/guide/assets.md @@ -26,10 +26,30 @@ Note that you should reference files placed in `public` using root absolute path If your site is deployed to a non-root URL, you will need to set the `base` option in `.vitepress/config.js`. For example, if you plan to deploy your site to `https://foo.github.io/bar/`, then `base` should be set to `'/bar/'` (it should always start and end with a slash). -With a base URL, to reference an image in `public`, you'd have to use URLs like `/bar/image.png`. But this is brittle if you ever decide to change the base. To help with that, VitePress provides a built-in helper `$withBase` (injected onto Vue's prototype) that generates the correct path: +All your static asset paths are automatically processed to adjust for different `base` config values. For example, if you have an absolute reference to an asset under `public` in your markdown: -```html -foo +```md +![An image](/image-inside-public.png) +``` + +You do **not** need to update it when you change the `base` config value in this case. + +However, if you are authoring a theme component that links to assets dynamically, e.g. an image whose `src` is based on a theme config value: + +```vue + ``` -Note you can use the above syntax not only in theme components, but in your Markdown files as well. +In this case it is recommended to wrap the path with the [`withBase` helper](/guide/api.html#withbase) provided by VitePress: + +```vue + + + +``` diff --git a/docs/guide/customization.md b/docs/guide/customization.md deleted file mode 100644 index 10cf4e0d..00000000 --- a/docs/guide/customization.md +++ /dev/null @@ -1,51 +0,0 @@ -# Customization - -You can develop your custom theme by adding the `.vitepress/theme/index.js` file. - -```bash -. -├─ docs -│ ├─ .vitepress -│ │ ├─ theme -│ │ │ └─ index.js -│ │ └─ config.js -│ └─ index.md -└─ package.json -``` - -In `.vitepress/theme/index.js`, you must export theme object and register your own theme layout. Let's say you have your own `Layout` component like this. - -```vue - - -``` - -Then include it in `.vitepress/theme/index.js`. - -```js -// .vitepress/theme/index.js -import Layout from './Layout.vue' - -export default { - Layout, - NotFound: () => 'custom 404', // <- this is a Vue 3 functional component - 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. - } -} -``` - -If you want to extend the default theme, you can import it from `vitepress/theme`. - -```js -// .vitepress/theme/index.js -import DefaultTheme from 'vitepress/theme' - -export default { - ...DefaultTheme -} -``` diff --git a/docs/guide/differences-from-vuepress.md b/docs/guide/differences-from-vuepress.md index 22da6b76..6e059691 100644 --- a/docs/guide/differences-from-vuepress.md +++ b/docs/guide/differences-from-vuepress.md @@ -22,8 +22,8 @@ In case you decide to move your project to VitePress, this is a list of differen - Components in `.vitepress/components` [are not auto registered as global components](https://vuepress.vuejs.org/) - Differences - [Public files](https://vuepress.vuejs.org/guide/assets.html#public-files) that are directly copied to dist root moved from `.vitepress/public/` is `public/` - - [styling](https://vuepress.vuejs.org/config/#styling) `.vitepress/styles/index.styl` and `.vitepress/styles/palette.styl` is `.vitepress/style.styl` - - [App Level Enhancements](https://vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements) API, app enhancements `.vitepress/enhanceApp.js` is `.vitepress/theme/index.js`. + - [styling](https://vuepress.vuejs.org/config/#styling) `.vitepress/styles/index.styl` and `.vitepress/styles/palette.styl` is not supported. See [Customizing CSS](/guide/theming.html#customizing-css). + - [App Level Enhancements](https://vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements) API, app enhancements `.vitepress/enhanceApp.js` is now done in `.vitepress/theme/index.js`. See [Extending the Default Theme](/guide/theming.html#extending-the-default-theme). ## Markdown @@ -31,7 +31,6 @@ In case you decide to move your project to VitePress, this is a list of differen - Support for [toml in frontmatter](https://vuepress.vuejs.org/guide/frontmatter.html#alternative-frontmatter-formats) - [details block](https://vuepress.vuejs.org/guide/markdown.html#custom-containers) - [markdown slots](https://vuepress.vuejs.org/guide/markdown-slot.html) - guide/using-vue.html#using-components). - `~` prefix to explicitly specify a url is a [webpack module request](https://vuepress.vuejs.org/guide/assets.html#relative-urls) ## Site Config diff --git a/docs/guide/frontmatter.md b/docs/guide/frontmatter.md index a62c2b2c..3e18009f 100644 --- a/docs/guide/frontmatter.md +++ b/docs/guide/frontmatter.md @@ -9,7 +9,7 @@ editLink: true --- ``` -Between the triple-dashed lines, you can set [predefined variables](#predefined-variables), or even create custom ones of your own. These variables can be used via the $frontmatter variable. +Between the triple-dashed lines, you can set [predefined variables](#predefined-variables), or even create custom ones of your own. These variables can be used via the special $frontmatter variable. Here’s an example of how you could use it in your Markdown file: diff --git a/docs/guide/global-computed.md b/docs/guide/global-computed.md deleted file mode 100644 index ea46cf4f..00000000 --- a/docs/guide/global-computed.md +++ /dev/null @@ -1,89 +0,0 @@ -# Global Computed - -In VitePress, some core [computed properties](https://v3.vuejs.org/guide/computed.html#computed-properties) can be used by the default theme or custom themes. Or directly in Markdown pages using vue, for example using `$frontmatter.title` to access the title defined in the frontmatter section of the page. - -## $site - -This is the `$site` value of the site you're currently reading: - -```js -{ - base: '/', - lang: 'en-US', - title: 'VitePress', - description: 'Vite & Vue powered static site generator.', - head: [], - locales: {}, - themeConfig: $themeConfig -} -``` - -## $themeConfig - -Refers to `$site.themeConfig`. - -```js -{ - locales: {}, - repo: 'vuejs/vitepress', - docsDir: 'docs', - editLinks: true, - editLinkText: 'Edit this page on GitHub', - lastUpdated: 'Last Updated', - nav: [...], - sidebar: { ... } -} -``` - -## $page - -This is the `$page` value of the page you're currently reading: - -```js -{ - relativePath: 'guide/global-computed.md', - title: 'Global Computed', - headers: [ - { level: 2, title: '$site', slug: 'site' }, - { level: 2, title: '$page', slug: '$page' }, - ... - ], - frontmatter: $frontmatter, - lastUpdated: 1606297645000 -} -``` - -## $frontmatter - -Reference of `$page.frontmatter`. - -```js -{ - title: 'Docs with VitePress', - editLink: true -} -``` - -## $lang - -The language of the current page. Default: `en-US`. - -## $localePath - -The locale path prefix for the current page. Default: `/`. - -## $title - -Value of the `` label used for the current page. - -## $description - -The content value of the `<meta name= "description" content= "...">` for the current page. - -## $withBase - -Helper method to generate correct path by prepending the `base` path configured in `.vitepress/config.js`. It's useful when you want to link to [public files with base path](./assets#public-files). - -```html -<img :src="$withBase('/foo.png')" alt="foo" /> -``` diff --git a/docs/guide/theming.md b/docs/guide/theming.md new file mode 100644 index 00000000..f3be70d1 --- /dev/null +++ b/docs/guide/theming.md @@ -0,0 +1,157 @@ +# Theming + +## Using a Custom Theme + +You can enable a custom theme by adding the `.vitepress/theme/index.js` file (the "theme entry file"). + +```bash +. +├─ docs +│ ├─ .vitepress +│ │ ├─ theme +│ │ │ └─ index.js +│ │ └─ config.js +│ └─ index.md +└─ package.json +``` + +A VitePress custom theme is simply an object containing three properties and is defined as follows: + +```ts +interface Theme { + Layout: Component // Vue 3 component + NotFound?: Component + enhanceApp?: (ctx: EnhanceAppContext) => void +} + +interface EnhanceAppContext { + app: App // Vue 3 app instance + router: Router // VitePress router instance + siteData: Ref<SiteData> +} +``` + +The theme entry file shoud export the theme as its default export: + +```js +// .vitepress/theme/index.js +import Layout from './Layout.vue' + +export default { + Layout, + NotFound: () => 'custom 404', // <- this is a Vue 3 functional component + 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. + } +} +``` + +...where the `Layout` component could like this: + +```vue +<!-- .vitepress/theme/Layout.vue --> +<template> + <h1>Custom Layout!</h1> + <Content /><!-- this is where markdown content will be rendered --> +</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](/guide/using-vue.html#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({ app }) { + // register global components + 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 { + --c-brand: #646cff; + --c-brand-light: #747bff; +} +``` + +See [default theme CSS variables](https://github.com/vuejs/vitepress/blob/master/src/client/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 top of the sidebar: + +```js +// .vitepress/theme/index.js +import DefaultTheme from 'vitepress/theme' +import MyLayout fro './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 #sidebar-top> + My custom sidebar top content + </template> + </Layout> +</template> +``` + +Full list of slots available in the default theme layout: + +- `navbar-search` +- `sidebar-top` +- `sidebar-bottom` +- `page-top-ads` +- `page-top` +- `page-bottom` +- `page-bottom-ads` +- Only when `home: true` is enabled via frontmatter: + - `home-hero` + - `home-features` + - `home-footer` diff --git a/docs/guide/using-vue.md b/docs/guide/using-vue.md index daa70775..383eb3d9 100644 --- a/docs/guide/using-vue.md +++ b/docs/guide/using-vue.md @@ -1,59 +1,8 @@ # Using Vue in Markdown -## 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://ssr.vuejs.org/en/universal.html). In short, make sure to only access Browser / DOM APIs in `beforeMount` or `mounted` hooks. - -If you are using or demoing components that are not SSR-friendly (for example, contain custom directives), you can wrap them inside the built-in `<ClientOnly>` component: - -``` md -<ClientOnly> - <NonSSRFriendlyComponent/> -</ClientOnly> -``` - -Note this does not fix components or libraries that access Browser APIs **on import**. To use code that assumes a browser environment on import, you need to dynamically import them in proper lifecycle hooks: - -``` vue -<script> -export default { - mounted () { - import('./lib-that-access-window-on-import').then(module => { - // use code - }) - } -} -</script> -``` - -If your module `export default` a Vue component, you can register it dynamically: - -```vue -<template> - <component v-if="dynamicComponent" :is="dynamicComponent"></component> -</template> - -<script> -export default { - data() { - return { - dynamicComponent: null - } - }, - - mounted () { - import('./lib-that-access-window-on-import').then(module => { - this.dynamicComponent = module.default - }) - } -} -</script> -``` - -**Also see:** - -- [Vue.js > Dynamic Components](https://v3.vuejs.org/guide/component-dynamic-async.html) +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. +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. ## Templating @@ -63,7 +12,7 @@ Each Markdown file is first compiled into HTML and then passed on as a Vue compo **Input** -``` md +```md {{ 1 + 1 }} ``` @@ -77,7 +26,7 @@ Directives also work: **Input** -``` md +```md <span v-for="i in 3">{{ i }} </span> ``` @@ -87,17 +36,22 @@ Directives also work: ### Access to Site & Page Data -The compiled component has access to the [site metadata and computed properties](./global-computed.md). For example: +You can use the [`useData` helper](/guide/api.html#usedata) in a `<script>` block and expose the data to the page. **Input** -``` md -{{ $page }} +```md +<script setup> +import { useData } from 'vitepress' +const { page } = useData() +</script> + +<pre>{{ page }}</pre> ``` **Output** -``` json +```json { "path": "/using-vue.html", "title": "Using Vue in Markdown", @@ -111,7 +65,7 @@ By default, fenced code blocks are automatically wrapped with `v-pre`. To displa **Input** -``` md +```md ::: v-pre `{{ This will be displayed as-is }}` ::: @@ -129,9 +83,13 @@ When you need to have more flexibility, VitePress allows you to extend your auth ### Importing components 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 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. ```md +<script setup> +import CustomComponent from '../components/CustomComponent.vue' +</script> + # Docs This is a .md using a custom component @@ -141,10 +99,6 @@ This is a .md using a custom component ## More docs ... - -<script setup> -import CustomComponent from '../components/CustomComponent.vue' -</script> ``` ### Registering global components in the theme @@ -180,9 +134,9 @@ Make sure a custom component’s name either contains a hyphen or is in PascalCa You can use Vue components in the headers, but note the difference between the following syntaxes: -| Markdown | Output HTML | Parsed Header | -|--------|-------------|----------------| -| <pre v-pre><code> # text <Tag/> </code></pre> | `<h1>text <Tag/></h1>` | `text` | +| Markdown | Output HTML | Parsed Header | +| ------------------------------------------------------- | ----------------------------------------- | ------------- | +| <pre v-pre><code> # text <Tag/> </code></pre> | `<h1>text <Tag/></h1>` | `text` | | <pre v-pre><code> # text \`<Tag/>\` </code></pre> | `<h1>text <code><Tag/></code></h1>` | `text <Tag/>` | The HTML wrapped by `<code>` will be displayed as-is; only the HTML that is **not** wrapped will be parsed by Vue. @@ -208,7 +162,7 @@ npm install -D stylus Then you can use the following in Markdown and theme components: -``` vue +```vue <style lang="sass"> .title font-size: 20px @@ -246,4 +200,58 @@ VitePress provides Built-In Vue Components like `ClientOnly` and `OutboundLink`, **Also see:** -- [Using Components In Headers](#using-components-in-headers) \ No newline at end of file +- [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://ssr.vuejs.org/en/universal.html). In short, make sure to only access Browser / DOM APIs in `beforeMount` or `mounted` hooks. + +If you are using or demoing components that are not SSR-friendly (for example, contain custom directives), you can wrap them inside the built-in `<ClientOnly>` component: + +```md +<ClientOnly> + <NonSSRFriendlyComponent/> +</ClientOnly> +``` + +Note this does not fix components or libraries that access Browser APIs **on import**. To use code that assumes a browser environment on import, you need to dynamically import them in proper lifecycle hooks: + +```vue +<script> +export default { + mounted() { + import('./lib-that-access-window-on-import').then((module) => { + // use code + }) + } +} +</script> +``` + +If your module `export default` a Vue component, you can register it dynamically: + +```vue +<template> + <component v-if="dynamicComponent" :is="dynamicComponent"></component> +</template> + +<script> +export default { + data() { + return { + dynamicComponent: null + } + }, + + mounted() { + import('./lib-that-access-window-on-import').then((module) => { + this.dynamicComponent = module.default + }) + } +} +</script> +``` + +**Also see:** + +- [Vue.js > Dynamic Components](https://v3.vuejs.org/guide/component-dynamic-async.html) diff --git a/docs/index.md b/docs/index.md index d68c05f9..666c7e7c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,11 @@ +--- +sidebarDepth: 1 +--- + # What is VitePress? ::: warning WARNING -VitePress is early WIP! Currently the focus is on making Vite stable and feature complete first. It is not recommended to use this for anything serious yet. +VitePress is currently in 0.x status. It is already suitable for out-of-the-box documentation use, but the config and theming API may still change between minor releases. ::: VitePress is [VuePress](https://vuepress.vuejs.org/)' little brother, built on top of [Vite](https://github.com/vitejs/vite). @@ -10,8 +14,6 @@ VitePress is [VuePress](https://vuepress.vuejs.org/)' little brother, built on t We love VuePress v1, but being built on top of Webpack, the time it takes to spin up the dev server for a simple doc site with a few pages is just becoming unbearable. Even HMR updates can take up to seconds to reflect in the browser! -As a reference, the [Composition API RFC repo](https://github.com/vuejs/composition-api-rfc) is just two pages, but it takes 4 seconds to spin up the server and almost 2 seconds for any edit to reflect in the browser. - Fundamentally, this is because VuePress v1 is a Webpack app under the hood. Even with just two pages, it's a full on Webpack project (including all the theme source files) being compiled. It gets even worse when the project has many pages – every page must first be fully compiled before the server can even display anything! Incidentally, Vite solves these problems really well: nearly instant server start, an on-demand compilation that only compiles the page being served, and lightning-fast HMR. Plus, there are a few additional design issues I have noted in VuePress v1 over time but never had the time to fix due to the amount of refactoring it would require.