一些组件
返回顶部
新建docs/.vitepress/theme/components/BackToTop.vue
文件
点击查看BackToTop.vue
文件内容
<template>
<Transition name="fade">
<div v-show="showBackTop" class="vitepress-backTop-main" title="返回顶部" @click="scrollToTop(300)">
<svg
t="1720595052079"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="4279"
width="200"
height="200"
>
<path
d="M752.736 431.063C757.159 140.575 520.41 8.97 504.518 0.41V0l-0.45 0.205-0.41-0.205v0.41c-15.934 8.56-252.723 140.165-248.259 430.653-48.21 31.457-98.713 87.368-90.685 184.074 8.028 96.666 101.007 160.768 136.601 157.287 35.595-3.482 25.232-30.31 25.232-30.31l12.206-50.095s52.47 80.569 69.304 80.528c15.114-1.23 87-0.123 95.6 0h0.82c8.602-0.123 80.486-1.23 95.6 0 16.794 0 69.305-80.528 69.305-80.528l12.165 50.094s-10.322 26.83 25.272 30.31c35.595 3.482 128.574-60.62 136.602-157.286 8.028-96.665-42.475-152.617-90.685-184.074z m-248.669-4.26c-6.758-0.123-94.781-3.359-102.891-107.192 2.95-98.714 95.97-107.438 102.891-107.93 6.964 0.492 99.943 9.216 102.892 107.93-8.11 103.833-96.174 107.07-102.892 107.192z m-52.019 500.531c0 11.838-9.42 21.382-21.012 21.382a21.217 21.217 0 0 1-21.054-21.34V821.74c0-11.797 9.421-21.382 21.054-21.382 11.591 0 21.012 9.585 21.012 21.382v105.635z m77.333 57.222a21.504 21.504 0 0 1-21.34 21.626 21.504 21.504 0 0 1-21.34-21.626V827.474c0-11.96 9.543-21.668 21.299-21.668 11.796 0 21.38 9.708 21.38 21.668v157.082z m71.147-82.043c0 11.796-9.42 21.34-21.053 21.34a21.217 21.217 0 0 1-21.013-21.34v-75.367c0-11.755 9.421-21.299 21.013-21.299 11.632 0 21.053 9.544 21.053 21.3v75.366z"
fill="#FFF"
p-id="4280"
></path>
</svg>
</div>
</Transition>
</template>
<script setup>
import { onBeforeUnmount, onMounted, ref } from 'vue';
// 是否显示返回顶部
const showBackTop = ref(false);
let animationFrameId = null; // 存储 requestAnimationFrame 的 ID
function scrollToTop(duration = 500) {
// window.scrollTo({
// top: 0,
// behavior: 'smooth',
// });
const start = window.scrollY;
const startTime = performance.now();
function animation(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1); // 计算进度
const ease = 1 - Math.pow(1 - progress, 3); // 缓动函数,调整滚动效果
window.scrollTo(0, start * (1 - ease)); // 根据进度调整滚动位置
if (progress < 1) {
animationFrameId = requestAnimationFrame(animation); // 继续动画
}
}
animationFrameId = requestAnimationFrame(animation); // 开始动画
}
// 节流
function throttle(fn, delay = 50) {
let lastTime = 0;
return function () {
let nowTime = +new Date();
if (nowTime - lastTime > delay) {
fn.apply(this, arguments);
lastTime = nowTime;
}
};
}
const onScroll = throttle(() => {
showBackTop.value = Boolean(window.scrollY > 100);
});
// 监听滚动事件
onMounted(() => window.addEventListener('scroll', onScroll));
// 移除监听事件并取消动画
onBeforeUnmount(() => {
window.removeEventListener('scroll', onScroll);
if (animationFrameId) {
cancelAnimationFrame(animationFrameId); // 停止动画
}
});
</script>
<style lang="css" scoped>
.vitepress-backTop-main {
z-index: 999;
position: fixed;
bottom: 20px;
right: 20px;
cursor: pointer;
width: 50px;
height: 50px;
border-radius: 50%;
background-color: var(--vp-c-indigo-1);
padding: 10px;
box-shadow: 2px 2px 10px 4px rgba(0, 0, 0, 0.15);
}
.vitepress-backTop-main:hover {
opacity: 0.6;
}
svg {
width: 100%;
height: 100%;
}
/* 旋转动画 */
@keyframes bounce {
0% {
transform: translateY(0) rotateY(0);
}
50% {
transform: translateY(-10px) rotateY(180deg);
}
100% {
transform: translateY(0) rotateY(360deg);
}
}
/* 进入 退出动画 */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
同级目录CustomLayoutSlot.vue
文件内引入BackToTop.vue
组件
<template>
<Layout>
<!-- ...其他插槽内容 -->
<!-- TAG返回顶部 -->
<template #doc-footer-before>
<BackToTop />
</template>
</Layout>
</template>
<script setup>
// ...其他内容
// 返回顶部
import BackToTop from './BackToTop.vue';
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
访问量统计
Vercount 计数统计
地址:https://github.com/EvanNotFound/vercount
配置
新建docs/utils/useLoadScript.ts
文件
点击查看 useLoadScript.ts
文件内容
/**
* 加载外部脚本。
* @param {string} url - 脚本的 URL 地址。
* @param {Object} [props] - 脚本属性的对象。
*/
function useLoadScript(url: string, props?: Record<string, string>) {
if (document.querySelector(`script[src="${url}"]`)) {
return; // 脚本已加载
}
const script = document.createElement('script');
script.defer = true;
script.async = true;
script.src = url;
// 将 props 对象中的属性添加到 script 元素上
if (props) {
Object.entries(props).forEach(([key, value]) => {
script.setAttribute(key, value);
});
}
script.onerror = error => {
console.error(`加载 ${url} 失败:`, error);
};
document.head.appendChild(script);
}
export default useLoadScript;
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
使用
注意
本地开发出现数字即算成功,部署后会显示正确的数值
script 标签引用使用
<script defer src="https://cn.vercount.one/js"></script>
本文总阅读量 <span id="vercount_value_page_pv"></span> 次 本站总访问量 <span id="vercount_value_site_pv"></span> 次
本站总访客数 <span id="vercount_value_site_uv"></span> 人
2
3
4
配置组件插槽使用
新建
docs/.vitepress/theme/components/Visits.vue
文件点击查看
Visits.vue
文件内容Visits.vuevue<template> <div class="visits"> <div v-if="site_uv" class="visits-item" id="vercount_container_site_uv" style="display: none"> 本站总访客数 <span id="vercount_value_site_uv" /> </div> <div v-if="site_pv" class="visits-item" id="vercount_container_site_pv" style="display: none"> 本站总访问量<span id="vercount_value_site_pv" /> </div> <div v-if="page_pv" class="visits-item" id="vercount_container_page_pv" style="display: none"> 本文总阅读量<span id="vercount_value_page_pv" /> </div> </div> </template> <script setup lang="ts"> // 访问量统计 import { inBrowser, useRoute } from 'vitepress'; import { ref, onMounted, watch } from 'vue'; import useLoadScript from '../../../utils/useLoadScript'; const { site_uv = false, site_pv = false, page_pv = false, } = defineProps({ site_uv: Boolean, site_pv: Boolean, page_pv: Boolean }); const route = useRoute(); // 忽略名单 let ignorePageArr = []; // 监听路由变化 watch( () => route.path, (to, from) => { // 忽略一些页面,不统计访问量(例如['/config-doc']就是不统计config-doc文档配置页面) if (ignorePageArr.some(e => to.indexOf(e) !== -1)) { let element = document.getElementById('vercount_container_page_pv'); if (element) { element.style.display = 'none'; } return; } // 加载访问量脚本 if (inBrowser && to !== from) { useLoadScript('https://cn.vercount.one/js'); } }, { immediate: true, } ); </script> <style lang="scss"> .visits { /* color: gray; */ text-align: center; font-size: 0.85rem; line-height: 1.25rem; .visits-item { margin: 5px; font-weight: 500; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; // font-family: 'Times New Roman', Times, serif; padding: 2px 8px; background-color: var(--vp-c-bg-soft); } } .visits-home { position: relative; top: 200px; z-index: 99; } .visits-doc { margin-top: 20px; } /* 媒体查询小于500 */ @media (min-width: 500px) { .visits { display: flex; justify-content: center; } .visits-home { top: 238px; } } </style>
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86CustomLayoutSlot.vue
文件中配置CustomLayoutSlot.vuevue<template> <Layout> <!-- ...其他插槽内容 --> <!-- TAG访问量统计(首页) --> <template #home-features-after> <Visits class="visits-home" site_uv site_pv /> </template> <!-- TAG访问量统计(文档) --> <template #doc-after> <Visits class="visits-doc" page_pv /> </template> </Layout> </template> <script setup> // ...其他内容 // TAG浏览量统计 import Visits from './Visits.vue'; </script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Umami 现代分析平台
Umami 是一个网站分析工具,它可以分析出一个网站的详细访问数据,包括请求 PV、UV、国家来源、来源于哪个网站、用户的操作系统、浏览器等等。 注册账号,添加网站成功后,开启共享 URL,然后将这个 url 放到自己的网站即可。 参考这个链接启用。 https://umami.is/docs/enable-share-url
配置
官网注册登录
添加要统计的网站站点
收集数据,复制生产的 script 地址
index.tstsimport { onMounted } from 'vue'; import useLoadScript from '../../utils/useLoadScript'; export default { // ...其他内容 setup() { // ...其他内容 onMounted(() => { // ...其他内容 // 加载脚本umami统计 // 可以配置只在线上生产环境生效process.env.NODE_ENV === 'production' useLoadScript('填入上图的src值', { 'data-website-id': '填入上图的data-website-id值', }); }); }, };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19启用共享 URL
排除我自己的访问 你想访问自己的网站,但你不希望你的访问出现在你的统计数据。要做到这一点,您需要在浏览器中添加一个设置。
在浏览器中打开开发者控制台:Settings -> More tools -> Developer tools
在控制台中,输入以下代码并点击 Enter:
jslocalStorage.setItem('umami.disabled', 1);
1此设置适用于每个网站,因此您需要为每个要排除的网站执行此操作。
要删除该设置,请输入以下代码并点击 Enter:
jslocalStorage.removeItem('umami.disabled');
1
代码展示效果插件
地址:https://github.com/zh-lx/vitepress-demo-plugin?tab=readme-ov-file
文档:https://vitepress-demo.fe-dev.cn/guide/start.html
安装
pnpm add vitepress-demo-plugin -D
配置
docs
下 新建 views
文件夹,用来存放要展示代码效果的文件
在 .vitepress/config.mts
中添加如下代码以引入 vitepressDemoPlugin
插件:
import { defineConfig } from 'vitepress';
import { vitepressDemoPlugin } from 'vitepress-demo-plugin';
export default defineConfig({
//...其他内容
markdown: {
config(md) {
md.use(vitepressDemoPlugin, {
// 指定demo文件的地址(也可以不指定,直接写文件路径)
demoDir: path.resolve(__dirname, '../views'),
});
},
},
});
2
3
4
5
6
7
8
9
10
11
12
13
14
使用
简单展示代码效果
新建 docs/views/Demo1.vue
文件
点击查看Demo1.vue
文件内容
<template>
<div class="container">
<div class="title">Demo1</div>
<div class="btn-container">
<button class="btn" @click="increment">+1</button>
<button class="btn" @click="decrement">-1</button>
</div>
<div>Current count: {{ count }}</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const count = ref<number>(0);
const increment = () => {
count.value++;
};
const decrement = () => {
count.value--;
};
</script>
<style scoped>
.container {
font-family: 'PingFang SC', 'Microsoft YaHei', 'SimHei', 'SimSun', 'sans-serif';
font-size: 14px;
line-height: 20px;
}
.title {
font-size: 24px;
font-weight: 600;
line-height: 32px;
}
.btn-container {
display: flex;
align-items: center;
column-gap: 24px;
}
.btn {
cursor: pointer;
background-color: #007bff;
color: #fff;
border: none;
font-size: 14px;
border-radius: 4px;
line-height: 20px;
padding: 4px 16px;
margin: 12px 0;
}
</style>
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
在任意 .md
文件中通过 <demo vue="xxx/path" />
指定一个 .vue
文件的路径,渲染该 vue 组件并展示其代码:
<demo vue="Demo1.vue" />
其对应的渲染效果如下(可以点击按钮操作):
完整展示代码效果
提醒
只能指定 vue、react、html 文件
vue="Demo1.vue"
react=“Demo1.jsx”
html="demo.html"
:vueFiles
是指定 vue 文件下的其他依赖文件。react 和 html 同理
title
和descriptio
n 可以再展示效果下方增加标题和描述
order
是指定 vue、react、html 文件的显示顺序
select
是指定默认选中哪种类型文件
stackblitz
和 codesandbox
是可以配置跳转到在线代码编辑平台
再新建 docs/views/demo.html
文件、 docs/views/demo.js
文件。
点击查看文件内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<style>
.container {
font-family: 'PingFang SC', 'Microsoft YaHei', 'SimHei', 'SimSun', 'sans-serif';
font-size: 14px;
line-height: 20px;
}
.title {
font-size: 24px;
font-weight: 600;
line-height: 32px;
}
.btn-container {
display: flex;
align-items: center;
column-gap: 24px;
}
.btn {
cursor: pointer;
background-color: #007bff;
color: #fff;
border: none;
font-size: 14px;
border-radius: 4px;
line-height: 20px;
padding: 4px 16px;
margin: 12px 0;
}
</style>
<div class="container">
<div class="title">Demo1的html类型文件</div>
<div class="btn-container">
<button class="btn" id="incrementBtn">+1</button>
<button class="btn" id="decrementBtn">-1</button>
</div>
<div id="countDisplay">Current count: 0</div>
</div>
<script type="module">
(function () {
let count = 0;
const increment = () => {
count++;
updateCountDisplay();
};
const decrement = () => {
count--;
updateCountDisplay();
};
document.getElementById('incrementBtn').addEventListener('click', increment);
document.getElementById('decrementBtn').addEventListener('click', decrement);
const updateCountDisplay = () => {
document.getElementById('countDisplay').innerText = `Current count: ${count}`;
};
})();
</script>
</body>
</html>
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
63
64
65
66
67
68
export const students = ['Amy', 'John', 'Lily', 'Tom'];
<demo
vue="Demo1.vue"
html="demo.html"
:vueFiles="['Demo1.vue', 'demo.js']"
title="混合语法 DEMO"
description="这是一个混合 demo 的示例,你可以使用 title 和 description 来指定 demo 的标题和描述"
order="html,vue"
select="vue"
stackblitz="true"
codesandbox="true"
/>
2
3
4
5
6
7
8
9
10
11
注意
如果是想自定义展示的文件名,可以将
:vueFiles="['Demo1.vue', 'demo.js']"
修改为
:vueFiles="{
'Demo1 文件': 'Demo1.vue',
'demo 的 js 文件': 'demo.js',
}"
2
3
4