跳转至

以下内容基本上是 AI 生成的,我还没校对,可能质量不高

语雀 Blog

A blog site using SvelteKit and UnoCSS.

用语雀当 CMS 的 SvelteKit 博客模板。写文章在语雀,自动同步到网站。

CNSeniorious000/yuque-blog muspimerol.site

No database needed

直接用语雀 API,SSR 渲染。

What It Does

把语雀的知识库变成博客。文章在语雀写,网站自动拉取渲染。支持 RSS feed、SEO、评论(Giscus)。

Tech Stack

Implementation

API Integration

src/lib/serverConstants.ts 定义了 API 基础 URL 和认证 token:

export const apiBaseurl = `${BASE_URL}/api/v2`;
export const headers = { "X-Auth-Token": ak };

SSR Handlers

src/routes/blog/+layout.server.ts 处理博客页面加载:

  • 列表页:listPosts() 拉取文章列表
  • 详情页:getPost(slug) 拉取单篇文章

用 Cheerio 解析 HTML,提取纯文本摘要,替换图片链接(NLark 代理)。

RSS Feed

src/routes/feed/+server.ts 生成 Atom feed:

  • 遍历所有文章
  • 解析 HTML 内容,清理语雀特有属性
  • 输出标准 Atom XML

My Clever Bits

  • NLark 图片代理:语雀图片用 CDN,但网站可能跨域。用 /nlark/ 路由代理请求,避免 CORS 问题
  • AI Bot 识别:路由里检测 User-Agent,如果是 AI bot 就 302 重定向到 /llms.txt,为训练场景提供纯文本
  • 渐进式加载:用 ISR(Incremental Static Regeneration),feed 每 5 分钟更新一次
  • 多语言日期formatDate 函数根据浏览器语言格式化日期,fallback 到中文

Source Dive

SEO 深入

ISR 缓存策略

src/routes/feed/+server.ts

export const config = { isr: { expiration: 300 } };

RSS feed 每 5 分钟更新,图片缓存 24 小时。

HTML 清理管道

src/routes/feed/+server.ts

语雀导出的 HTML 包含大量内联属性和特殊标签,用 Cheerio 清理:

const $ = load(parseDocument(body_html));

// 移除草稿特有的属性
for (const attribute of ["id", "class", "data-lake-index-type", "data-href"]) {
  $(`[${attribute}]`).each((_, el) => $(el).removeAttr(attribute));
}

// 展开 span 标签(语雀用 span 包裹大量内容)
$("span").each(function () {
  const content = $(this).html();
  if (content) $(this).replaceWith(content);
});

AI Bot 重定向

src/routes/+layout.server.ts

检测 AI 爬虫,重定向到纯文本版本 /llms.txt

const UA = UAParser(request.headers.get("user-agent") || "");

if (UA.bot?.name) {
  throw redirect(302, "/llms.txt");
}

这为 LLM 训练场景提供干净的纯文本,减少 token 浪费。

多语言日期

src/lib/utils.ts

同年文章不显示年份:

const options: Intl.DateTimeFormatOptions = {
  month: "long",
  day: "numeric",
  year: date.getFullYear() === new Date().getFullYear() ? undefined : "numeric",
};

语言检测在 SSR 和 CSR 阶段不同。

src/routes/+layout.server.ts

// SSR: 从 HTTP 请求头获取
const language = headers.get("accept-language")?.split(",")[0];

src/lib/Chinese.svelte

// CSR: 从浏览器 API 获取
this.defaultIsChinese = Boolean(
  browser ? navigator.languages.join().includes("zh") : acceptLanguage?.includes("zh"),
);

browser 来自 $app/environment,是 SvelteKit 提供的环境判断。