Skip to content

集成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 Theme

3. 引入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
})