深浅模式
布局插槽
更新时间:2023-12-20
简介
Vitepress基于Vue3用到了 <slot>
插槽,在 <Layout/>
布局组件中预留了一些插槽,可以对页面布局进行自定义修改
由于也是使用组件,请了解过 组件的使用 了再来看
说明
布局插槽就好比一个插线板,将电器的插头插入对应的插线孔就可以工作了
示例
开始前,请确保你安装了 vue
,已安装的无视
sh
pnpm add -D vue
sh
yarn add -D vue
sh
npm i vue
sh
bun add -D vue
在 .vitepress/theme/components
目录新建一个 MyLayout.vue
组件
md
docs
├─ .vitepress
│ └─ config.mts
│ └─ theme
│ │ ├─ components
│ │ │ └─ MyLayout.vue <-- 插槽组件
│ │ └─ index.ts
└─ index.md
示例1:Layout
在 MyLayout.vue
中粘贴如下代码
vue
<script setup>
import DefaultTheme from 'vitepress/theme'
const { Layout } = DefaultTheme
</script>
<template>
<Layout>
<template #aside-outline-before>
<div class="title">aside-outline-before</div>
</template>
<template #doc-before>
<div class="title">doc-before</div>
</template>
</Layout>
</template>
<style scoped>
.title {
color: red;
}
</style>
然后在 .vitepress/theme/index.mts
中引入
ts
// .vitepress/theme/index.mts
import DefaultTheme from 'vitepress/theme'
import MyLayout from './components/MyLayout.vue'
export default {
extends: DefaultTheme,
Layout: MyLayout,
}
示例2:h函数
在 MyLayout.vue
中粘贴如下代码
vue
<script setup>
</script>
<template>
<div class="title">aside-outline-before</div>
</template>
<style scoped>
.title {
color: red;
}
</style>
ts
// .vitepress/theme/index.mts
import DefaultTheme from 'vitepress/theme'
import { h } from 'vue'
import MyLayout from './components/MyLayout.vue'
// import MyLayout2 from './components/MyLayout2.vue' // 第2个组件
export default {
extends: DefaultTheme,
Layout() {
return h(DefaultTheme.Layout, null, {
'aside-outline-before': () => h(MyLayout),
//'doc-before': () => h(MyLayout2), // 第2个组件使用doc-before插槽
})
}
}
插槽表
不同的页面,可使用的插槽不同
doc
当 Frontmatter 配置 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
home
当 Frontmatter 配置 layout: home
(默认)时插槽及位置
- home-hero-before
- home-hero-info
- home-hero-image
- home-hero-after
- home-features-before
- home-features-after
page
当 Frontmatter 配置 layout: page
(默认)时插槽及位置
- page-top
- page-bottom
404
在未找到 (404) 页面上
- not-found
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
使用演示
分别演示两种使用情况,Frontmatter使用 和 常规使用
Frontmatter使用
本方法参考 掘金 @Younglina的文章
通过VitePress官网给出的 useDate 返回页面数据,可以看到返回对象的类型
ts
interface VitePressData {
site: Ref<SiteData>
page: Ref<PageData>
theme: Ref<any> // themeConfig from .vitepress/config.js
frontmatter: Ref<PageData['frontmatter']>
lang: Ref<string>
title: Ref<string>
description: Ref<string>
localePath: Ref<string>
}
我这里仅演示 frontmatter
使用,其他的同理
在 .vitepress/theme/components
目录新建一个 tags.vue
组件
md
docs
├─ .vitepress
│ └─ config.mts
│ └─ theme
│ │ ├─ components
│ │ │ └─ tags.vue
│ │ └─ index.ts
└─ index.md
粘贴如下代码,此处的插槽使用的是 doc-before
vue
<script setup>
import DefaultTheme from 'vitepress/theme'
import { useData } from 'vitepress'
const { Layout } = DefaultTheme
const { frontmatter } = useData()
</script>
<template>
<Layout>
<template #doc-before>
<span class="date">🔥 更新时间:{{ frontmatter.date }}</span>
</template>
</Layout>
</template>
<style>
.date{
font-size: 15px;
color: #7f7f7f;
margin-right: 10px;
}
</style>
然后在引入
ts
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import { h } from 'vue'
import tags from './tags.vue'
export default {
extends: DefaultTheme,
Layout: tags,
}
然后在任意 *.md
文章顶部使用 Frontmatter
md
---
date: 2023-12-19 08:09
---
查看效果
常规使用
在 untils
目录新建一个 sponsors.ts
文件
md
docs
├─ .vitepress
│ └─ config.mts
│ └─ theme
│ │ ├─ components
│ │ ├─ untils
│ │ │ └─ sponsors.ts <-- ts文件
│ │ └─ index.ts
└─ index.md
粘贴如下代码,保存
ts
import { ref, onMounted } from 'vue'
interface Sponsors {
special: Sponsor[]
platinum: Sponsor[]
platinum_china: Sponsor[]
gold: Sponsor[]
silver: Sponsor[]
bronze: Sponsor[]
}
interface Sponsor {
name: string
img: string
url: string
}
// shared data across instances so we load only once.
const data = ref()
const dataHost = 'https://sponsors.vuejs.org'
const dataUrl = `${dataHost}/vite.json`
const viteSponsors: Pick<Sponsors, 'special' | 'gold'> = {
special: [
// sponsors patak-dev
{
name: 'StackBlitz',
url: 'https://stackblitz.com',
img: '/svg/stackblitz.svg',
},
// sponsors antfu
{
name: 'NuxtLabs',
url: 'https://nuxtlabs.com',
img: '/svg/nuxtlabs.svg',
},
// sponsors bluwy
{
name: 'Astro',
url: 'https://astro.build',
img: '/svg/astro.svg',
},
],
gold: [
// through GitHub -> OpenCollective
{
name: 'Remix',
url: 'https://remix.run/',
img: '/svg/remix.svg',
},
],
}
export function useSponsor() {
onMounted(async () => {
if (data.value) {
return
}
const result = await fetch(dataUrl)
const json = await result.json()
data.value = mapSponsors(json)
})
return {
data,
}
}
function mapSponsors(sponsors: Sponsors) {
return [
{
tier: 'Special Sponsors',
size: 'big',
items: viteSponsors['special'],
},
{
tier: 'Platinum Sponsors',
size: 'big',
items: mapImgPath(sponsors['platinum']),
},
{
tier: 'Gold Sponsors',
size: 'medium',
items: viteSponsors['gold'].concat(mapImgPath(sponsors['gold'])),
},
]
}
const viteSponsorNames = new Set(
Object.values(viteSponsors).flatMap((sponsors) =>
sponsors.map((s) => s.name),
),
)
/**
* Map Vue/Vite sponsors data to objects and filter out Vite-specific sponsors
*/
function mapImgPath(sponsors: Sponsor[]) {
return sponsors
.filter((sponsor) => !viteSponsorNames.has(sponsor.name))
.map((sponsor) => ({
...sponsor,
img: `${dataHost}/images/${sponsor.img}`,
}))
}
然后我们将赞助商的图片放入 public - svg
文件夹
md
docs
├─ .vitepress
│ └─ config.mts
│ └─ theme
├─ public
│ └─ svg <-- 赞助商svg文件
└─ index.md
在 components
目录新建 HomeSponsors.vue
组件
md
docs
├─ .vitepress
│ └─ config.mts
│ └─ theme
│ │ ├─ components
│ │ │ └─ HomeSponsors.vue <-- 插槽组件
│ │ └─ index.ts
└─ index.md
粘贴如下代码,保存
ts
<script setup lang="ts">
import { VPHomeSponsors } from 'vitepress/theme'
import { useSponsor } from '../untils/sponsor'
const { data } = useSponsor()
</script>
<template>
<VPHomeSponsors
v-if="data"
message="Vite is free and open source, made possible by wonderful sponsors."
:data="data"
/>
<div class="action">
<a
class="sponsor"
href="https://github.com/sponsors/vitejs"
target="_blank"
rel="noreferrer"
>
Sponsor Vite
</a>
<a
class="sponsor"
href="https://github.com/sponsors/yyx990803"
target="_blank"
rel="noreferrer"
>
Sponsor Evan You
</a>
</div>
</template>
<style scoped>
.action {
display: flex;
justify-content: center;
gap: 1rem;
padding-top: 4rem;
}
.sponsor {
/* .VPButton */
display: inline-block;
border: 1px solid transparent;
text-align: center;
font-weight: 600;
white-space: nowrap;
transition:
color 0.25s,
border-color 0.25s,
background-color 0.25s;
/* .VPButton.medium */
border-radius: 20px;
padding: 0 20px;
line-height: 38px;
font-size: 14px;
/* .VPButton.sponsor */
border-color: var(--vp-button-sponsor-border);
color: var(--vp-button-sponsor-text);
background-color: var(--vp-button-sponsor-bg);
}
.sponsor:hover {
/* .VPButton.sponsor:hover */
border-color: var(--vp-button-sponsor-hover-border);
color: var(--vp-button-sponsor-hover-text);
background-color: var(--vp-button-sponsor-hover-bg);
}
</style>
最后我们使用 home-features-after
插槽并引入配置文件index.ts
ts
// .vitepress/theme/index.ts
import { h } from 'vue'
import DefaultTheme from 'vitepress/theme'
import HomeSponsors from './components/HomeSponsors.vue'
export default {
extends: DefaultTheme,
Layout() {
return h(DefaultTheme.Layout, null, {
'home-features-after': () => h(HomeSponsors),
})
},
}