很久以前就想在我的vitepress站点上加入官方没有的字数统计以及目录中加入一个返回上一级的按钮
受VitePress 更新日期显示组件 | Ceil.Top的启发,加上最近正好处于熟悉vue的阶段,就正式开始了我开发流程
先贴几个用的到的官方文档
官方提供的Api:运行时 API | VitePress
扩展默认主题、插槽扩展默认主题 | VitePress
扩展主题
先按照文档中扩展自己的布局
在...\docs\.vitepress\theme
中定义一个MyLayout.vue
1 2 3 4 5 6 7 8 9
| <script setup> import DefaultTheme from 'vitepress/theme' const { Layout } = DefaultTheme </script>
<template> <Layout> </Layout> </template>
|
找到index.ts
or index.js
,修改其中的 Layout
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import { h } from 'vue' import MyLayout from './MyLayout.vue' import type { Theme } from 'vitepress' import DefaultTheme from 'vitepress/theme'
export default { extends: DefaultTheme, Layout: MyLayout, enhanceApp({ app, router, siteData }) { } } satisfies Theme
|
此时就可以在MyLayout.vue
使用插槽添加扩展自己的布局了
自定义布局需要参考自定义主题 | VitePress,使用<Content />
转化文章,而不是使用<Layout>
默认布局
添加字数统计
效果预览:
在你能找到的地方添加一个vue文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <script setup> import { ref, onMounted, watch } from 'vue' import { useRouter, useRoute } from 'vitepress'
const wordCount = ref(0) const router = useRouter() const route = useRoute()
const calculateWordCount = () => { const content = document.querySelector('.vp-doc') if (content) { const text = content.innerText.match(/\S/g); wordCount.value = text?text.length:0;
} }
onMounted(() => { calculateWordCount() watch( () => route.path, (newPath, oldPath) => { calculateWordCount() } ) }) </script>
<template> <div> <hr> <p>本篇笔记字数: {{ wordCount }}</p> </div> </template>
|
实现原理,通过获取Dom元素(document.querySelector('.vp-doc')
)得到文章的innerText长度,并通过正则表达式过滤,只留下非空字符(const text = content.innerText.match(/\S/g);
)。
组件挂载时回调函数获取字数并通过模板语法插入模板,并通过监听路由变化实现不同文章的字数统计切换
在刚刚的MyLayout.vue
中引入字数统计的vue文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <script setup> import DefaultTheme from 'vitepress/theme'
import ContentSize from './extend/ContentSize.vue'
const { Layout } = DefaultTheme </script>
<template> <Layout>
<template #aside-ads-after> //插槽插入指定位置 <ContentSize /> //c </template>
</Layout> </template>
|
官网介绍的插槽可使用的位置
默认主题布局的全部可用插槽如下:
当layout: 'doc'
(默认) 在 frontmatter 中被启用时:
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'
在 frontmatter 中被启用时:
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'
在 frontmatter 中被启用时:
当未找到页面 (404) 时:
总是启用:
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
返回上一级
效果预览:
返回上一级的vue文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| <script setup> import { ref, watch,onMounted } from 'vue' import { useRouter, useRoute } from 'vitepress'
const router = useRouter() const route = useRoute() const BeforeUrl = ref(router.path) const isNoMain = ref(route.path==='/main'? false:true)
onMounted(() => {
watch(() => route.path, (newPath, oldPath) => { const path = route.path; isNoMain.value = path==='/main'? false:true;
BeforeUrl.value = getStringBeforeNSlashes(path); }) })
function getStringBeforeNSlashes(str) { let currentIndex = str.length; let last = str.lastIndexOf('/') let start = str.indexOf('/') if(last === start){ return '/main'; }
for (let i = 0; i < 1; i++) { const slashIndex = str.lastIndexOf('/', currentIndex - 1); if (slashIndex === -1) { return '/main'; } currentIndex = slashIndex; }
return str.substring(0, currentIndex); }
const goBack = () => { if (BeforeUrl.value) { router.go(BeforeUrl.value) } } </script>
<template > <!-- 不是main时触发 --> <div v-if="isNoMain"> <hr> <button @click="()=>goBack()">返回上一级</button> <hr> </div>
</template>
|
此时需要在MyLoyot
加入这个vue文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <script setup> import DefaultTheme from 'vitepress/theme' import ContentSize from './extend/ContentSize.vue' import Return from './extend/Return.vue' const { Layout } = DefaultTheme </script>
<template> <Layout> <template #sidebar-nav-before> <Return /> </template> <template #aside-ads-after> <ContentSize /> </template> </Layout> </template>
|
解析有时间再出