集成Valine评论系统
1. 创建Valine组件
首先,创建一个Vue组件,用于集成Valine评论系统。 在docs/.vitepress/theme/components目录下创建一个Valine.vue文件,内容如下:
html
<template>
<div class="reload-cx">
<i @click="reloadBtn" title="重载评论"><svg t="1767702908918" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4900" width="28" height="28"><path d="M909.1 209.3l-56.4 44.1C775.8 155.1 656.2 92 521.9 92 290 92 102.3 279.5 102 511.5 101.7 743.7 289.8 932 521.9 932c181.3 0 335.8-115 394.6-276.1 1.5-4.2-0.7-8.9-4.9-10.3l-56.7-19.5c-4.1-1.4-8.6 0.7-10.1 4.8-1.8 5-3.8 10-5.9 14.9-17.3 41-42.1 77.8-73.7 109.4-31.6 31.6-68.4 56.4-109.3 73.8-42.3 17.9-87.4 27-133.8 27-46.5 0-91.5-9.1-133.8-27-40.9-17.3-77.7-42.1-109.3-73.8-31.6-31.6-56.4-68.4-73.7-109.4-17.9-42.4-27-87.4-27-133.9s9.1-91.5 27-133.9c17.3-41 42.1-77.8 73.7-109.4 31.6-31.6 68.4-56.4 109.3-73.8 42.3-17.9 87.4-27 133.8-27 46.5 0 91.5 9.1 133.8 27 40.9 17.3 77.7 42.1 109.3 73.8 9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47c-5.3 4.1-3.5 12.5 3 14.1l175.6 43c5 1.2 9.9-2.6 9.9-7.7l0.8-180.9c-0.1-6.6-7.8-10.3-13-6.2z" p-id="4901" fill="#707070"></path></svg></i>
</div>
<div v-if="loadingValue" class="loading-img"><svg t="1767707953881" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5542" width="128" height="128"><path d="M469.333333 85.333333m42.666667 0l0 0q42.666667 0 42.666667 42.666667l0 128q0 42.666667-42.666667 42.666667l0 0q-42.666667 0-42.666667-42.666667l0-128q0-42.666667 42.666667-42.666667Z" fill="#000000" opacity=".8" p-id="5543"></path><path d="M469.333333 725.333333m42.666667 0l0 0q42.666667 0 42.666667 42.666667l0 128q0 42.666667-42.666667 42.666667l0 0q-42.666667 0-42.666667-42.666667l0-128q0-42.666667 42.666667-42.666667Z" fill="#000000" opacity=".4" p-id="5544"></path><path d="M938.666667 469.333333m0 42.666667l0 0q0 42.666667-42.666667 42.666667l-128 0q-42.666667 0-42.666667-42.666667l0 0q0-42.666667 42.666667-42.666667l128 0q42.666667 0 42.666667 42.666667Z" fill="#000000" opacity=".2" p-id="5545"></path><path d="M298.666667 469.333333m0 42.666667l0 0q0 42.666667-42.666667 42.666667l-128 0q-42.666667 0-42.666667-42.666667l0 0q0-42.666667 42.666667-42.666667l128 0q42.666667 0 42.666667 42.666667Z" fill="#000000" opacity=".6" p-id="5546"></path><path d="M783.530667 180.138667m30.169889 30.169889l0 0q30.169889 30.169889 0 60.339779l-90.509668 90.509668q-30.169889 30.169889-60.339779 0l0 0q-30.169889-30.169889 0-60.339779l90.509668-90.509668q30.169889-30.169889 60.339779 0Z" fill="#000000" opacity=".1" p-id="5547"></path><path d="M330.965333 632.661333m30.16989 30.16989l0 0q30.169889 30.169889 0 60.339778l-90.509668 90.509668q-30.169889 30.169889-60.339779 0l0 0q-30.169889-30.169889 0-60.339778l90.509668-90.509668q30.169889-30.169889 60.339779 0Z" fill="#000000" opacity=".5" p-id="5548"></path><path d="M843.861333 783.530667m-30.169889 30.169889l0 0q-30.169889 30.169889-60.339779 0l-90.509668-90.509668q-30.169889-30.169889 0-60.339779l0 0q30.169889-30.169889 60.339779 0l90.509668 90.509668q30.169889 30.169889 0 60.339779Z" fill="#000000" opacity=".3" p-id="5549"></path><path d="M391.338667 330.965333m-30.16989 30.16989l0 0q-30.169889 30.169889-60.339778 0l-90.509668-90.509668q-30.169889-30.169889 0-60.339779l0 0q30.169889-30.169889 60.339778 0l90.509668 90.509668q30.169889 30.169889 0 60.339779Z" fill="#000000" opacity=".7" p-id="5550"></path></svg></div>
<div id="vcomments"></div>
</template>
<script setup>
import { computed, onMounted, ref, watch } from "vue";
import { useData } from "vitepress";
const { page } = useData();
const loadingValue = ref(true);
// current page path
const path = computed(() => page.value.relativePath);
const reloadBtn = () => {
document.getElementById("vcomments").innerHTML = "";
loadingValue.value = true;
setTimeout(() => {
initValine();
}, 1500);
};
const initValine = async () => {
if (typeof Valine !== "undefined") {
loadingValue.value = false;
new Valine({
el: "#vcomments",
appId: "9iOmqWQl7****",
appKey: "U2cCMaQ****",
path: path.value,
avatar: "wavatar",
avatar_cdn: "https://cravatar.cn/avatar/",
placeholder: "说点什么吧...",
visitor: true,
});
}
}
// watch page path change
watch(() => page.value.relativePath, async (newPath, oldPath) => {
if (oldPath !== newPath) {
document.getElementById("vcomments").innerHTML = "";
loadingValue.value = true;
setTimeout(() => {
initValine();
}, 1000);
}
});
onMounted(async () => {
setTimeout(() => {
initValine();
}, 1000);
});
</script>
<style scoped>
.reload-cx {
margin-top: 1em;
display: flex;
justify-content: flex-end;
}
.reload-cx i{
cursor: pointer;
}
.loading-img {
margin-top: 2em;
display: flex;
justify-content: center;
}
.loading-img svg {
width: 40px;
height: 40px;
animation: rotate 1s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
#vcomments {
margin-top: 1em;
}
</style>2. 注册组件
在docs/.vitepress/theme/index.ts文件中,注册刚才创建的Valine.vue组件:
js
// https://vitepress.dev/guide/custom-theme
import { h } from 'vue'
import type { Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import Valine from './components/Valine.vue' // import Valine component
import './style.css'
export default {
extends: DefaultTheme,
Layout: () => {
return h(DefaultTheme.Layout, null, {
// https://vitepress.dev/guide/extending-default-theme#layout-slots
'doc-after': () => h(Valine) // register Valine component
})
},
enhanceApp({ app, router, siteData }) {
// ...
}
} satisfies Theme3. 引入Valine.min.js
在docs/.vitepress/config.mts文件中,引入Valine.min.js文件:
ts
import { defineConfig } from 'vitepress'
export default defineConfig({
vite: {
server: {
host: '0.0.0.0'
}
},
head: [
// 引入外部脚本
['script', { src: '//cdn.nodjoy.com/js/Valine.min.js'}],
]
// ...other config
})