惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Jina AI
Jina AI
Hugging Face - Blog
Hugging Face - Blog
博客园 - 三生石上(FineUI控件)
博客园 - 【当耐特】
大猫的无限游戏
大猫的无限游戏
IT之家
IT之家
宝玉的分享
宝玉的分享
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
Apple Machine Learning Research
Apple Machine Learning Research
酷 壳 – CoolShell
酷 壳 – CoolShell
阮一峰的网络日志
阮一峰的网络日志
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
爱范儿
爱范儿
小众软件
小众软件
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
S
SegmentFault 最新的问题
博客园 - Franky
博客园_首页
T
Tailwind CSS Blog
雷峰网
雷峰网
罗磊的独立博客

唯知笔记

唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站 唯知笔记 | 一个高效的知识分享网站
唯知笔记 | 一个高效的知识分享网站
weizwz@foxma · 2026-05-24 · via 唯知笔记

VitePress 添加友链界面 ​

提示:项目使用了 Tailwind 进行了重构,本文组件样式都对此有所依赖,参见 我的 Tailwind 配置。如果不想使用 Tailwind,可以直接把代码扔给 AI,让其转化为普通 CSS 样式

效果预览

友链数据存储 ​

友链数据通常是经常需要添加和修改的,所以我们不能直接写死到页面上。这里我们单独提一个文件去存储友链数据,并且友链数据的格式基本也是类似的。

新建 links.md 文件,内容如下:

docs/pages/links.md

md

---
layout: weiz-link
title: 我的友链
description: 这是唯知笔记网站的友链界面,xxxxxx

links:
  - title: 鸣谢
    desc: 建站中学习和使用了以下博客/网站的技术和分享,特别鸣谢!🫡
    list:
      - name: VitePress
        link: https://vitepress.dev/zh/
        avatar: https://vitepress.dev/vitepress-logo-mini.svg
        descr: 由 Vite 和 Vue 驱动的静态站点生成器
      - name: VitePress 快速上手教程
        link: https://vitepress.yiov.top/
        avatar: https://vitepress.dev/vitepress-logo-mini.svg
        descr: 如果你也想搭建它,那跟我一起做吧
  - title: 传送门
    desc: 聚集众多优秀独立博客,随机传送 🚀
    list:
      - name: 十年之约
        link: https://foreverblog.cn/go.html
        avatar: https://img.foreverblog.cn/logo_en_default.png
        descr: 十年之约,奔赴某个独立博客的十年
      - name: Web Teleporter
        link: https://webteleporter.top/
        avatar: https://webteleporter.top/img/logo.png
        descr: 独立博客传送门
outLinks:
  - title: 去远方
    desc: 去了远方的伙伴,回来的时候请记得联络我📩
    list:
      - name: 随风起
        link: https://blog.bywind.xyz/
        avatar: https://blog.bywind.xyz/img/avatar.png
        descr: 爱意随风起,风止意难平
---

说明:outLinks 字段是网站断联的名单,非必须

友链页面创建 ​

Vitepress 中创建单独页面的方法,可以参考 VitePress 新建页面和注册组件

新建组件 ​

新建一个文件夹 docs/.vitepress/theme/components/WLink 用来存放友链组件

WLink/index.vueWLink/LinkSite.vue.vitepress/theme/type/WLink.ts

vue

<template>
  <div id="main" class="mx-auto w-full max-w-384 p-6 lg:p-8">
    <h1 class="text-text1 text-2xl! font-bold!">我的友链</h1>
    <div class="mb-12">
      <div class="link" v-for="(item, index) of linksData" :key="index">
        <div class="relative mt-12 mb-6 h-px bg-slate-200 dark:bg-neutral-700">
          <h3 class="bg-bg-soft absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 px-6 text-center text-xl! font-bold!">
            {{ item.title }}
          </h3>
        </div>
        <p class="mb-8! text-center font-medium!">{{ item.desc }}</p>
        <div class="link-container grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
          <div v-for="temp of item.list" :key="temp.link" class="link-wrapper">
            <LinkSite :data="temp" />
          </div>
        </div>
      </div>

      <div class="link" v-for="(item, index) of outLinksData" :key="'out-' + index">
        <div class="relative mt-12 mb-6 h-px bg-slate-200 dark:bg-neutral-700">
          <h3 class="bg-bg-soft absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 px-6 text-center text-xl! font-bold!">
            {{ item.title }}
          </h3>
        </div>
        <p class="mb-4! text-center font-medium!">{{ item.desc }}</p>

        <!-- Toggle Button -->
        <div class="mb-8 flex justify-center">
          <div
            @click="toggleOutLink(index)"
            class="bg-bg hover:bg-main text-text1 border-border group flex cursor-pointer items-center gap-2 rounded-md border px-4 py-1.5 text-sm font-bold transition-all duration-300 hover:text-white"
          >
            <span class="pl-2">{{ collapsedStates[index] ? '查看' : '收起' }}</span>
            <WIcon tag="right" class="transition-transform duration-300 group-hover:text-white!" :class="collapsedStates[index] ? 'rotate-90' : '-rotate-90'" />
          </div>
        </div>

        <div v-show="!collapsedStates[index]" class="animate-fade-in grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
          <div v-for="temp of item.list" :key="temp.link" class="link-wrapper">
            <LinkSite :data="temp" :noAvatar="true" />
          </div>
        </div>
      </div>
    </div>
    <div class="message">
      <div class="relative mt-12 mb-6 h-px bg-slate-200 dark:bg-neutral-700">
        <h3 class="bg-bg-soft absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 px-8 text-center text-xl! font-bold!">留链</h3>
      </div>
      <p class="mb-8! text-center font-medium!">留恋的小伙伴,快来和我做友链 💞</p>
      <div class="bg-bg rounded-2xl p-6 shadow-[0_1px_2px_0_rgba(0,0,0,0.05)] dark:shadow-[0_1px_2px_0_rgba(255,255,255,0.05)]">
        <p class="mb-3!">
          非常高兴能和很多朋友交流,如果你也想加入
          <a href="https://note.weizwz.com" target="_blank"><b>唯知笔记</b></a> 友链,可以添加我站后在下方留言,我会尽快添加。
        </p>
        <div class="note custom-block github-alert mb-3">
          <p class="custom-block-title">本站信息</p>
          <p>
            标题:唯知笔记 <br />
            地址:https://note.weizwz.com <br />
            头像:https://p.weizwz.com/logo_a4353391cbf0889b.webp <br />
            描述:探索知识的无限可能 <br />
            截图:https://p.weizwz.com/siteshot_note.webp
          </p>
        </div>
        <div class="mb-6 rounded-lg border border-l-[5px] border-(--vp-c-tip-1) bg-(--vp-c-tip-soft) p-4">
          <details class="group">
            <summary class="flex cursor-pointer list-none items-center font-medium">
              <span class="mr-2 text-lg text-(--vp-c-tip-1) transition-transform duration-200 group-open:rotate-90">▶</span>
              友链须知
            </summary>
            <div class="mt-2 mb-2 pl-1">
              <p>1. 本站友链只添加博客类型的网站,其他类型网站不在友链范围之内</p>
              <p>2. 为了友链页面的统一性和美观性,可能会对你的昵称进行缩短处理</p>
              <p>3. 友链头像使用原博主公布的链接,请不要加入防盗链避免请求失败</p>
              <p>4. 友链信息(头像地址,网站地址等)如有修改,请在本页的评论中添加,我会尽快更新</p>
              <p>5. 本站将不定期清理失效网站,如网站重新上线后请在本页的评论中添加,我会尽快更新</p>
            </div>
          </details>
        </div>
        <Twikoo />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { useData } from 'vitepress'
import { ref } from 'vue'
import LinkSite from './LinkSite.vue'
import Twikoo from '../WTwikoo/index.vue'
import { LinkList } from '../../type/WLink'
import WIcon from '../WIcon.vue'

const { frontmatter: fm } = useData()

const linksData = (fm.value.links as LinkList[]) || []
const outLinksData = (fm.value.outLinks as LinkList[]) || []

// 折叠状态管理
const collapsedStates = ref<boolean[]>(outLinksData.map(() => true))

const toggleOutLink = (index: number) => {
  collapsedStates.value[index] = !collapsedStates.value[index]
}
</script>

<style scoped>
@media (min-width:  96rem) {
  .link-container {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }
}

.animate-fade-in {
  animation: fadeIn 0.5s ease-out;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
</style>

vue

<template>
  <a
    class="bg-bg shadow-shadow group relative flex h-[100px] w-full items-center justify-between gap-4 rounded-2xl p-4 shadow-xs transition-all duration-600 hover:shadow-xl"
    :href="data.link"
    target="_blank"
  >
    <div v-if="!noAvatar" class="flex h-16 w-16 items-center justify-center overflow-hidden transition-all duration-600">
      <img
        v-if="data.avatar && !imageFailed"
        :class="[
          'h-full w-full align-top transition-all duration-600 object-cover',
          data.avatar?.startsWith('https://vitepress.dev/') ? 'rounded-none' : 'rounded-full'
        ]"
        :src="data.avatar"
        @error="handleImageError()"
        :alt="data.name"
      />
      <!-- 替代内容:显示首字母 -->
      <div v-else class="bg-main flex h-full w-full items-center justify-center rounded-full text-3xl font-bold text-white transition-all duration-600">
        {{ data.name.charAt(0).toLocaleUpperCase() }}
      </div>
    </div>
    <div class="flex h-full flex-1 flex-col justify-center transition-all duration-600">
      <h3
        class="mb-2! w-full overflow-hidden bg-[linear-gradient(to_right,var(--color-main)_50%,var(--color-text1)_50%)] bg-size-[200%_100%] bg-clip-text bg-position-[100%_0] text-base! font-semibold! whitespace-nowrap text-transparent transition-[background-position] duration-1000 group-hover:bg-position-[0_0]"
      >
        {{ data.name }}
      </h3>
      <div class="text-text2 line-clamp-2 w-full overflow-hidden leading-5 font-semibold break-all" :title="data.descr">
        {{ data.descr }}
      </div>
    </div>
  </a>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { Link } from '../../type/WLink'

const props = defineProps<{ data: Link; noAvatar?: boolean }>()
const data = props.data
const noAvatar = props.noAvatar

// 记录图片加载状态
const imageFailed = ref(false)

// 处理图片加载失败
const handleImageError = () => {
  imageFailed.value = true // 更新加载状态
}
</script>

ts

export interface LinkList {
  // 标题
  title: string
  // 描述
  desc: string
  // 列表
  list: Link[]
}

export interface Link {
  // 站点名称
  name: string
  // 站点链接
  link: string
  // 头像/站点logo
  avatar: string
  // 站点描述
  descr: string
  // 站点缩略图
  siteshot?: string
}

组件注册 ​

新建的组件需要在主题配置中心进行注册,注册后就可以在 md页面 中使用了。比如我们 links.md 文件中的开头 layout: weiz-link

docs/.vitepress/theme/index.ts

ts

import WLink from './components/WLink/index.vue' // 友链页

export default {
  enhanceApp({ app }) {
    // 注册全局组件
    app.component('weiz-link', WLink)
  }
}

附带评论 ​

在组件内集成评论,直接使用评论组件,比如我的是 <Twikoo /> 评论。
Twikoo 评论的集成方式,可参考 VitePress 集成 Twikoo 评论