Skip to content

搭建免费的个人博客网站

1. 概述

静态博客工具可以把 Markdown 文档构建成可部署的网站,并提供代码高亮、目录、Frontmatter、图片资源管理等常用能力。

常见选择包括 VuePressVitePressHugoJekyllHexoDocsify

整体流程很简单:选择博客框架,编写 Markdown,构建静态页面,再部署到个人服务器或第三方静态托管平台。

本文以基于 VitePressTeek 的博客项目为例,整理本地写作、图标规范、图片管理、Algolia 搜索集成,以及部署到 Cloudflare PagesNetlifyGitee Pages 的完整步骤。

2. 本地环境搭建

2.1. VitePress

根据 VitePress 官方文档 初始化项目,再参考 Teek 主题文档 完成主题接入。项目初始化后,建议先确认本地构建命令可以正常运行,再继续配置图片、图标和部署平台。

2.2. IDE

本地可以安装 TyporaVS Code 作为 Markdown 编辑器。

编辑器建议

Typora 对 Markdown 图片、表格和图床集成比较友好,适合日常写作和截图整理。

2.3. 图床

图床需选择 HTTPS 外链

浏览器默认会限制 https 页面加载 http 图片资源。选择图床时,优先确认图片外链是否支持 https。如果确实需要临时加载 http 图片,可以在浏览器中手动配置:隐私和安全 -> 更多内容设置 -> 不安全内容 -> 允许显示不安全内容

加载图片异常

  • 聚合图床

    可用于存储博客图片,并生成外链地址。

    • 集成 Typora

      1. 配置 PicGo。Token 可以在 用户中心 获取。

      2. 在 Typora 中配置图片上传服务。

      PicGo集成typora

    • 定期备份

      第三方图床存在访问失败或服务调整的可能,重要图片建议定期备份。

      1. 复制图片 URL。

      复制图片URL

      1. 运行脚本下载到本地。
      sh
      # 图片URL列表
      urls=("https://pic.imgdb.cn/item/65f5b2bd9f345e8d03f022c0.png"
      "https://pic.imgdb.cn/item/65f5b2679f345e8d03ed0734.png")
      
      # 循环下载
      for url in "${urls[@]}"; do
          filename=$(basename "$url")
          curl -o "./$filename" --location --request GET "$url" \
          --header "Host: pic.imgdb.cn" \
          --header "Connection: keep-alive" \
          --header "Referer: $url"
      done
  • 项目目录

    也可以直接将图片存放在当前 Markdown 同级的 assets 目录。Typora 中开启 复制图片到 ./assets 文件夹 后,截图和本地图片会自动归档到文章目录下。

    图像设置

2.4. 图标

博客中的导航、卡片、标题、目录和资源清单都需要图标。当前项目已经注册统一组件 AppIcon,底层封装 Teek 官方 TkIcon,页面里直接使用即可。

图标选择遵循一个原则:通用语义优先用 Emoji,品牌和技术 Logo 再用本地资源或 Iconify 离线集合补充,避免同一页面出现过多风格。

当前支持清单:

图标源是否启用用途
Emoji默认主风格,适合导航、标题、目录、卡片和通用语义。
本地 SVG/PNG平台 Logo、项目 Logo、公司 Logo、MyBatis 这类缺口图标。
LogosMySQL、Redis、Nginx、Kafka 等官方或开源项目 Logo。
DeviconJava、Jenkins 等编程语言、运行时和开发工具 Logo。
Skill Icons是,少量使用Spring、Docker、Linux、Maven 等技术栈补充。
Simple Icons是,少量使用知乎等社交平台或品牌图标补充,本地 SVG 优先。
Iconfont.cn 离线文件是,作为备选接口兼容后续导出的自定义图标,统一使用 Symbol 模式。

2.4.1. 推荐目录

text
src/public/assets/icons/              # 本地 SVG / PNG,例如 MyBatis、私有项目 Logo
src/public/assets/iconfont/           # Iconfont.cn 导出的 iconfont.js
src/.vitepress/theme/icons/aliases.ts # 图标别名统一维护
src/.vitepress/theme/icons/iconify.ts # 从已安装的 @iconify-json/* 依赖读取离线图标
src/.vitepress/theme/icons/local.ts   # 本地 SVG / PNG 路径规则

日常按这个优先级选择:

  1. emoji:xxx:默认选择,适合通用语义,保持站内风格统一。
  2. img / 本地 SVG:适合平台、公司、项目 Logo,避免品牌图标来源重复。
  3. logos / devicon / skill-icons / simple-icons:只作为官方/开源/技术 Logo 补充。
  4. iconfont:适合 Iconfont.cn 离线导出的补充图标,本站统一解析为 Symbol 模式。
  5. iconifyOffline:适合少量手写或单独传入的 Iconify 图标对象。

2.4.2. 基础用法

md
<AppIcon icon="emoji:📝" />
<AppIcon icon="/assets/logo/jerry-up/project-blog.svg" />
<AppIcon icon="local:/assets/icons/custom.svg" />
<AppIcon icon="iconfont:MyBatis" />
<AppIcon icon="logos:mysql-icon" />
<AppIcon icon="devicon:java" />

普通页面、组件和 Frontmatter 不直接写底层图标源,先把可复用图标收进别名表:

md
<AppIcon icon="site-notes" />
<AppIcon icon="mysql" />
<AppIcon icon="java" />
<AppIcon icon="mybatis" />

页面、侧边栏和目录卡片的图标写在文档 Frontmatter 中:

md
---
title: 搭建免费的个人博客网站
icon: vitepress
---

正文标题和右侧目录图标写在标题末尾:

md
## 本地环境搭建 {icon="settings"}
### VitePress {icon="vitepress"}
### Iconfont Symbol 示例 {icon="iconfont:icon-iconfont"}

{icon="..."} 不会显示在标题文字里,只作为渲染图标的数据来源。

如果图标会被重复使用,优先写进别名表:

ts
// src/.vitepress/theme/icons/aliases.ts
export const iconAliases = {
  mybatis: "iconfont:MyBatis",
  github: "logos:github-icon",
  java: "skill-icons:java-light",
};

页面中就可以简化为:

md
<AppIcon icon="mybatis" />
<AppIcon icon="github" />
<AppIcon icon="java" />

2.4.3. 支持类型样例

下面展示本站启用的图标类型。Iconfont.cn 只保留 symbol 样例;iconifyOnline 不作为运行时样例渲染。

svg<AppIcon :icon="demoSvgIcon" icon-type="svg" />适合少量自定义内联 SVG。
symbol<AppIcon icon="iconfont:icon-iconfont" />本站 Iconfont.cn 唯一推荐样式。
img<AppIcon icon="/assets/logo/jerry-up/project-blog.svg" />适合站点标识、截图标记和本地图片。
component<AppIcon :icon="DemoIcon" icon-type="component" />适合需要交互或特殊结构的 Vue 图标。
iconifyOffline<AppIcon :icon="offlineIcon" icon-type="iconifyOffline" />适合把常用 Iconify 图标打进项目。

2.4.4. 不同平台样例

下面这些都走 AppIcon 的统一入口。卡片标题可以点击跳转到对应官网或资源地址。Iconify 只展示本站允许的 Logo 类集合;通用语义继续使用 Emoji。

Emoji参考<AppIcon icon="emoji:📝" />默认主风格,适合导航、标题、目录和通用语义。参考:Emojipedia渲染:系统 Emoji 字体接入:emoji: 前缀
站点标识资源<AppIcon icon="/assets/logo/jerry-up/project-blog.svg" />适合站点 Logo、项目标识和本地资源展示。资源:/assets/logo/jerry-up/project-blog.svg目录:src/public/assets/logo/jerry-up/接入:public 静态资源
本地 SVG资源<AppIcon icon="local:/assets/logo/jerry-up/logo.svg" />适合 MyBatis 等缺口图标。资源:/assets/logo/jerry-up/logo.svg目录:src/public/assets/logo/jerry-up/接入:local:/ 路径
Iconfont.cn官网<AppIcon icon="iconfont:icon-iconfont" />统一使用 Symbol 离线模式。资源:/assets/iconfont/iconfont.js模式:Symbol 离线接入:iconfont: 前缀
Logos官网<AppIcon icon="logos:mysql-icon" />开源项目、产品和技术官方 Logo。在线:api.iconify.design/logos/mysql-icon.svg离线:node_modules/@iconify-json/logos/icons.jsonnpm包:@iconify-json/logos@^1.2.0
Devicon官网<AppIcon icon="devicon:java" />编程语言、运行时和开发工具 Logo。在线:api.iconify.design/devicon/java.svg离线:node_modules/@iconify-json/devicon/icons.jsonnpm包:@iconify-json/devicon@^1.2.0
Skill Icons官网<AppIcon icon="skill-icons:docker" />技能墙风格补充,少量用于技术栈展示。在线:api.iconify.design/skill-icons/docker.svg离线:node_modules/@iconify-json/skill-icons/icons.jsonnpm包:@iconify-json/skill-icons@^1.2.0
Simple Icons官网<AppIcon icon="simple-icons:zhihu" />社交平台或品牌图标补充,本地 SVG 优先。在线:api.iconify.design/simple-icons/zhihu.svg离线:node_modules/@iconify-json/simple-icons/icons.jsonnpm包:@iconify-json/simple-icons@^1.2.0

2.4.5. 安装图标库依赖

只有 Iconify 集合需要安装对应的 @iconify-json/{prefix} 依赖。当前项目会先把已引用的 Iconify 图标导出为静态 SVG;静态目录没有命中时,再从本地依赖读取 icons.json 并用 iconifyOffline 兜底渲染。Emoji、Iconfont.cn、本地图片和本地 SVG 不需要 npm 包。

来源地址规则用途
Iconify 图标集https://www.npmjs.com/package/@iconify-json/{prefix}某个平台的离线 JSON npm 包。
单个 SVGhttps://api.iconify.design/{prefix}/{name}.svg仅用于人工核对或临时获取,不参与运行时加载。
Iconfont.cnhttps://www.iconfont.cn/在 Iconfont 项目中导出 symbol 离线文件。
本地图片/SVG项目内 /assets/... 路径已经是本地资源,无需在线下载。
sh
pnpm add -D @iconify-json/logos @iconify-json/devicon @iconify-json/skill-icons @iconify-json/simple-icons

新增或调整 Iconify 图标后,重新导出静态资源和清单:

sh
pnpm run icons:export

安装后先收敛到别名,再在页面使用别名:

md
<AppIcon icon="mysql" />
<AppIcon icon="java" />
<AppIcon icon="docker" />

当前项目只保留这四类 Iconify 集合:

text
https://www.npmjs.com/package/@iconify-json/logos
https://www.npmjs.com/package/@iconify-json/simple-icons
https://www.npmjs.com/package/@iconify-json/devicon
https://www.npmjs.com/package/@iconify-json/skill-icons

2.4.6. 本地离线加载控制

当前项目只使用本地离线图标。样例中的 在线 链接只用于人工核对,客户端渲染不会读取 api.iconify.design

写法加载方式示例
emoji:xxx本地文本渲染,不请求资源<AppIcon icon="emoji:📝" />
/xxx.pnglocal:/xxx.svgimg:/xxx.png本地图片资源<AppIcon icon="local:/assets/icons/custom.svg" />
iconfont:xxx本地 Iconfont.cn Symbol 文件<AppIcon icon="iconfont:MyBatis" />
:icon="offlineIcon" + icon-type="iconifyOffline"本地打包的 Iconify 数据<AppIcon :icon="offlineIcon" icon-type="iconifyOffline" />
平台:图标名优先走静态 SVG,缺失时回退到本地 Iconify JSONlogos:mysql-icon -> /assets/icons/logos/mysql-icon.svg
别名aliases.ts 决定mybatis -> iconfont:MyBatis

平台样例里的 在线 链接使用 Iconify API 的 SVG 直链,例如 https://api.iconify.design/logos/mysql-icon.svg,只用于核对图标长什么样。项目中的 <AppIcon icon="mysql" /> 会通过别名解析到 logos:mysql-icon,优先命中静态文件 /assets/icons/logos/mysql-icon.svg;如果静态清单中没有这个图标,再读取本地依赖 node_modules/@iconify-json/logos/icons.json 兜底渲染。

远程图片地址也不会作为图标加载,例如 https://example.com/icon.svg 会被忽略。需要使用时先下载到 src/public/assets/icons/,再通过 local:/assets/icons/... 引用。

只要使用 平台:图标名 写法,就必须安装对应依赖包。依赖不存在或图标名称写错时,图标会留空,不会回退到在线请求。当前允许的本地依赖如下:

text
src/public/assets/icons/{collection}/{name}.svg
src/.vitepress/theme/icons/static.ts
node_modules/@iconify-json/logos/icons.json
node_modules/@iconify-json/devicon/icons.json
node_modules/@iconify-json/skill-icons/icons.json
node_modules/@iconify-json/simple-icons/icons.json

少量高频图标可以继续通过别名映射到本地,适合 MyBatis、项目 Logo、平台 Logo 等需要统一维护的场景。

ts
// src/.vitepress/theme/icons/aliases.ts
export const iconAliases = {
  java: "skill-icons:java-light",
  github: "logos:github-icon",
  mybatis: "iconfont:MyBatis",
};
md
<AppIcon icon="java" />

简单说:local://assets/iconfont:iconifyOfflinelogos:devicon:skill-icons:simple-icons: 都是本地离线加载。logos: 等平台写法优先使用 src/public/assets/icons/ 下的静态 SVG,缺失时才回退到本地 npm 图标数据。文档中的在线 SVG 地址只用于人工核对,不参与客户端渲染。

2.5. 搜索

VitePress 默认主题搜索支持本地搜索,也支持接入 Algolia DocSearch 做在线全文检索。个人博客文章逐渐变多后,Algolia 的优势是索引独立、搜索体验成熟、可以在后台查看索引记录和搜索效果。

先看集成路径

Algolia 集成可以按四步理解:创建应用和索引,配置 Crawler 抓取公开站点,把 Application ID / Search-Only API Key / Index Name 写进 VitePress,最后部署并运行爬虫验证。

步骤操作产物
1在 Algolia 创建 Application 和 IndexappIdindexName
2在 Crawler 中添加公开站点地址可定时抓取的爬虫任务
3在 VitePress 中切换 provider: "algolia"前端搜索框接入 Algolia
4部署站点并运行一次 Crawl搜索弹窗能搜到文章
配置前提

Crawler 需要能访问公开页面。建议先完成一次 Cloudflare Pages 或 Netlify 部署,再回到 Algolia 配置域名和爬虫。

2.5.1. 创建 Algolia 应用和索引

  1. 登录 Algolia Dashboard,创建一个新的 Application。

    image-20260524013027627

  2. 在应用内创建 Index,命名建议和站点保持一致,例如 jerry-up-blog

    text
    Application: jerry-up
    Index Name: jerry-up-blog

    image-20260524013323382

  3. 进入 Settings -> API Keys,记录这三个值。

    字段用途是否可以放到前端
    Application IDVitePress 和 Crawler 都需要
    Search-Only API Key前端搜索时读取索引
    Admin API KeyCrawler 写入索引
    密钥边界

    Search-Only API Key 会出现在前端配置里,这是正常的。Admin API Key 只能放在 Algolia Crawler 后台,不要写进仓库。

    image-20260524013114371

2.5.2. 配置 Crawler 抓取站点

Algolia 的搜索框只负责查询,真正把文章写进索引的是 Crawler。进入 Algolia Crawler,先添加博客域名并完成验证,再创建一个新的 Crawler 任务。

  1. 添加站点域名,Start URL 填写博客首页。

    text
    https://jerry-up-blog.pages.dev

    image-20260524013610234

  2. 选择一种验证方式。个人博客最常用的是 HTML fileMeta tagDNS TXT

    验证方式适合场景
    HTML file可以上传静态文件到站点根目录
    Meta tag可以修改 VitePress head 配置
    DNS TXT域名在自己手里,且能修改 DNS 记录

    如果选择 Meta tag,可以在 .vitepress/config.mts 中统一维护 head。验证码最终会公开在页面源码里,可以直接写在配置常量中;环境变量只作为临时覆盖手段:

    ts
    import { defineConfig } from "vitepress";
    
    const getEnvValue = (key: string): string => process.env[key]?.trim() ?? "";
    const algoliaPublicConfig = {
      siteVerification: "ALGOLIA_SITE_VERIFICATION_CODE",
    };
    
    const algoliaSiteVerification =
      getEnvValue("ALGOLIA_SITE_VERIFICATION") || algoliaPublicConfig.siteVerification;
    const siteHead: [string, Record<string, string>][] = [
      ["link", { rel: "icon", href: "/favicon.svg", type: "image/svg+xml" }],
      ...(algoliaSiteVerification
        ? [["meta", { name: "algolia-site-verification", content: algoliaSiteVerification }] as [string, Record<string, string>]]
        : []),
    ];
    
    export default defineConfig({
      head: siteHead,
    });

    需要临时替换验证码时,macOS / Linux 可以这样启动:

    sh
    ALGOLIA_SITE_VERIFICATION=ALGOLIA_VERIFICATION_CODE pnpm run dev

    Windows PowerShell 可以这样启动:

    powershell
    $env:ALGOLIA_SITE_VERIFICATION="ALGOLIA_VERIFICATION_CODE"; pnpm run dev

    如果已经把验证码写进配置常量,部署到 Cloudflare Pages 或 Netlify 时不需要再设置 ALGOLIA_SITE_VERIFICATION。只有临时替换验证码时,才把它加到构建环境变量里再重新部署。

    构建时可以用下面的命令确认最终页面会带上 Meta tag。当前仓库的真实脚本是 build,不要写成 blog:build

    sh
    pnpm install && pnpm run build

    如果想在本地构建时临时替换验证码,macOS / Linux 使用:

    sh
    ALGOLIA_SITE_VERIFICATION=ALGOLIA_VERIFICATION_CODE pnpm run build

    Windows PowerShell 使用:

    powershell
    $env:ALGOLIA_SITE_VERIFICATION="ALGOLIA_VERIFICATION_CODE"; pnpm run build

    image-20260524151605445

  3. 域名验证通过后,回到 Crawler 控制台,点击 New crawler 创建爬虫任务。

    验证后还要新增 Crawler

    域名验证只说明站点归属可确认,不会自动创建索引任务。验证成功后需要再新增一个 Crawler,并把它绑定到当前 Application、站点地址和目标 Index。

    推荐按下面的方式填写,字段名称以 Algolia 页面实际展示为准:

    配置项建议值说明
    Crawler namejerry-up-blog-crawler方便和博客项目对应
    Application当前 Algolia Application使用前面创建的应用
    Start URLhttps://jerry-up-blog.pages.dev/填线上博客首页
    Index namejerry-up-blog和 VitePress indexName 保持一致
    ConfigurationJavaScript config / Manual setup方便直接粘贴下面的规则
    博客文档推荐

    内容类型优先选择 Technical documentation 和 Articles;Template 优先选择 VitePress / Vitepress;Products 不勾;General web pages 只有在需要搜索首页、关于页、项目页时再勾选。

    如果页面出现 What types of content do you want to crawl?,它是在问站点里主要有哪些内容类型。这个选择通常用于生成初始抓取模板,后续仍然可以在 Crawler 配置中通过 actionspathsToMatchrecordExtractor 精细控制。

    选项作用和其他选项的区别推荐策略
    General web pages抓取普通网站页面,例如首页、关于页、项目介绍页、导航页、活动页关注整页内容,不假设页面一定有文章时间线、商品字段或文档层级按需勾选。需要让首页、关于页、推荐页、项目页进入搜索时再选;只做文档搜索可以不勾
    Products抓取商品或服务条目,例如名称、价格、分类、库存、SKU、商品描述面向电商或产品目录,重点是结构化商品属性,不适合普通博客文档不勾。博客文档没有商品、价格、库存、SKU 这类模型
    Articles抓取文章内容,例如博客、资讯、经验总结、排障记录、更新日志面向按时间发布的内容,通常更关注标题、摘要、作者、发布时间和正文推荐勾选。日常积累、排障记录、经验总结属于这一类
    Technical documentation抓取技术文档、教程、安装手册、API/配置说明、问题排查步骤面向有目录层级的文档,通常按 h1/h2/h3、正文段落、列表和代码块生成搜索记录优先勾选。安装手册、应用使用说明、模板代码、环境搭建文档属于这一类
    内容类型选择

    如果是多选,优先勾选 Technical documentation 和 Articles;General web pages 视搜索范围决定;Products 不勾。这样搜索结果会优先覆盖技术文档和排障文章,避免混入商品类模板。

    如果向导只能单选,优先选择 Technical documentation。博客文档的核心价值是目录化阅读、安装说明和问题排查,文章内容也可以在后续 Crawler 配置中继续覆盖。

    接着遇到 Template * 时,它是在问 Crawler 用哪一种站点生成器的初始抓取模板。模板会预填 startUrlssitemapspathsToMatch、正文选择器和搜索索引字段,但它只是起点,创建后仍然可以在 Editor 里替换成下方配置。

    Template 选项适合场景推荐策略
    VitePress / VitepressVitePress 站点,正文通常在 .content 容器里,标题按 h1/h2/h3 分层首选。VitePress + Teek 项目和下方配置规则最接近
    VuePressVuePress 站点,正文容器和侧边栏选择器与 VitePress 不同不选。名字接近但选择器不完全一致
    DocusaurusDocusaurus 站点,通常用 article h1/h2、Docusaurus 菜单类名不选。模板会偏向 Docusaurus DOM
    Default没有匹配框架,或只是想先创建 Crawler 再手动改配置备选。如果页面没有 VitePress,就选 Default,创建后进入 Editor 粘贴下面的配置
    其他框架模板Astro Starlight、Rspress、pkgdown 等对应框架不选,除非站点实际迁移到了对应框架
    Template 选择

    优先选择 VitePress / Vitepress。没有这个选项时选择 Default,创建后进入 Editor,把配置替换为下面这段 Crawler 配置。不要选 VuePress 或 Docusaurus,它们和博客文档的 DOM 结构不同。

    官方 VitePress 模板的核心思路是使用 .content h1.content h2.content h3.content p, .content li 抽取文档层级与正文。下方配置也沿用这个思路,并额外移除了侧边栏、导航和页脚,避免搜索结果里混入导航文字。

    如果页面提供可视化配置,也可以先创建 Crawler,再进入 ConfigurationEditor 页面改成下面的 JavaScript 配置。关键是最终要能看到一个独立的 Crawler 任务,并且它指向 jerry-up-blog 这个 Index。

    image-20260524175345404

  4. 在 Crawler 配置中写入抓取规则。VitePress 官方示例使用 .content h1.content h2.content p 等选择器,适合默认文档布局。如果站点暂时没有 sitemap.xml,先删除 sitemaps 这一行,等后续生成站点地图后再补回来。

    js
    new Crawler({
      appId: "YOUR_APP_ID",
      apiKey: "YOUR_ADMIN_API_KEY",
      rateLimit: 8,
      startUrls: ["https://your-domain.com/"],
      sitemaps: ["https://your-domain.com/sitemap.xml"],
      discoveryPatterns: ["https://your-domain.com/**"],
      exclusionPatterns: [
        "https://your-domain.com/tags/**",
        "https://your-domain.com/categories/**",
        "https://your-domain.com/archives/**",
      ],
      renderJavaScript: false,
      actions: [
        {
          indexName: "jerry-up-blog",
          pathsToMatch: ["https://your-domain.com/**"],
          recordExtractor: ({ $, helpers }) => {
            $(".VPDocAside, .VPSidebar, .VPNav, .site-footer").remove();
    
            return helpers.docsearch({
              recordProps: {
                lvl0: {
                  selectors: ".content h1",
                  defaultValue: "博客文档",
                },
                lvl1: ".content h1",
                lvl2: ".content h2",
                lvl3: ".content h3",
                lvl4: ".content h4",
                lvl5: ".content h5",
                content: ".content p, .content li",
              },
              indexHeadings: true,
            });
          },
        },
      ],
    });

    image-20260524180409209

  5. 保存配置后运行一次测试抓取。抓取完成后,进入 Index 页面确认有 records。

    text
    Records > 0

    image-20260524180412974

2.5.3. 修改 VitePress 搜索配置

打开 .vitepress/config.mts,把 Algolia 前端搜索参数收敛到公共配置常量中。appIdsearchApiKeyindexName 三个值都存在时使用 Algolia;缺少任意一个时回退到本地搜索,避免本地开发或临时部署时搜索不可用。

ts
import { defineConfig, type DefaultTheme } from "vitepress";

const getEnvValue = (key: string): string => process.env[key]?.trim() ?? "";
const algoliaPublicConfig = {
  appId: "B2S5VC6T26",
  searchApiKey: "7842783a4e6cb13827082894378549d3",
  indexName: "jerry-up-blog",
};

const algoliaAppId = getEnvValue("ALGOLIA_APP_ID") || algoliaPublicConfig.appId;
const algoliaSearchApiKey = getEnvValue("ALGOLIA_SEARCH_API_KEY") || algoliaPublicConfig.searchApiKey;
const algoliaIndexName = getEnvValue("ALGOLIA_INDEX_NAME") || algoliaPublicConfig.indexName;

const siteSearch: DefaultTheme.Config["search"] =
  algoliaAppId && algoliaSearchApiKey && algoliaIndexName
    ? {
        provider: "algolia",
        options: {
          appId: algoliaAppId,
          apiKey: algoliaSearchApiKey,
          indexName: algoliaIndexName,
        },
      }
    : {
        provider: "local",
      };

export default defineConfig({
  themeConfig: {
    search: siteSearch,
  },
});

这些参数的安全边界如下:

参数来源是否可以写前端代码
appIdAlgolia Application ID可以
searchApiKeySearch-Only API Key可以
indexNameIndex Name可以
siteVerificationMeta tag 验证码可以
Admin API KeyAlgolia Admin API Key不可以

Search-Only API Key 的设计目标就是给前端搜索读取索引用。它会出现在浏览器里,不属于服务端密钥。真正只能放在 Algolia Crawler 后台的是 Admin API Key

如果想保留中文搜索框文案,可以在 Algolia 分支的 options 中补充 locales

ts
export default defineConfig({
  themeConfig: {
    search: {
      provider: "algolia",
      options: {
        appId: "B2S5VC6T26",
        apiKey: "7842783a4e6cb13827082894378549d3",
        indexName: "jerry-up-blog",
        locales: {
          root: {
            placeholder: "搜索文档",
            translations: {
              button: {
                buttonText: "搜索",
                buttonAriaLabel: "搜索",
              },
              modal: {
                searchBox: {
                  resetButtonTitle: "清除查询",
                  resetButtonAriaLabel: "清除查询",
                  cancelButtonText: "取消",
                  cancelButtonAriaLabel: "取消",
                },
                startScreen: {
                  recentSearchesTitle: "最近搜索",
                  noRecentSearchesText: "暂无最近搜索",
                  saveRecentSearchButtonTitle: "保存到最近搜索",
                  removeRecentSearchButtonTitle: "从最近搜索中移除",
                  favoriteSearchesTitle: "收藏",
                  removeFavoriteSearchButtonTitle: "从收藏中移除",
                },
                errorScreen: {
                  titleText: "无法获取结果",
                  helpText: "请检查网络连接后重试。",
                },
                noResultsScreen: {
                  noResultsText: "没有找到相关内容",
                  suggestedQueryText: "可以换个关键词试试",
                },
                footer: {
                  selectText: "选择",
                  navigateText: "切换",
                  closeText: "关闭",
                  searchByText: "搜索由",
                },
              },
            },
          },
        },
      },
    },
  },
});
推荐做法

第一次集成先使用最小配置跑通搜索。确认能搜索到文章后,再补充中文文案、搜索策略和 Ask AI 等增强配置。

2.5.4. 部署和验证

  1. 统一确认构建命令和输出目录。

    text
    Build command: pnpm run build
    Output directory: dist

    本地手动打包时,才需要先安装依赖:

    sh
    pnpm install && pnpm run build
  2. 统一确认 Algolia 公共参数。

    优先写进配置常量

    Application IDSearch-Only API KeyIndex NameSite Verification 都会出现在前端页面或请求中,可以直接写进 .vitepress/config.mts。构建环境变量只作为覆盖手段,不是必填项。

    text
    appId=B2S5VC6T26
    searchApiKey=7842783a4e6cb13827082894378549d3
    indexName=jerry-up-blog
    siteVerification=ALGOLIA_SITE_VERIFICATION_CODE

    如果不想把公共参数写进代码,也可以在部署平台使用环境变量覆盖:

    text
    ALGOLIA_APP_ID=B2S5VC6T26
    ALGOLIA_SEARCH_API_KEY=7842783a4e6cb13827082894378549d3
    ALGOLIA_INDEX_NAME=jerry-up-blog
    ALGOLIA_SITE_VERIFICATION=ALGOLIA_SITE_VERIFICATION_CODE
    场景推荐做法
    固定个人博客配置写进 .vitepress/config.mtsalgoliaPublicConfig
    临时切换索引或验证码使用构建环境变量覆盖
    Crawler 写入索引只在 Algolia Crawler 后台使用 Admin API Key
  3. 在 Algolia Crawler 后台手动运行一次抓取。

  4. 打开线上博客,点击右上角搜索框,输入文章标题或正文关键词。

    截图槽位:搜索效果

    这里后续补充 VitePress 搜索弹窗、命中标题和命中正文的截图。

  5. 如果搜索无结果,按下面顺序排查:

    现象优先检查
    搜索弹窗打不开providerappIdapiKeyindexName 是否写对
    弹窗打开但无结果Crawler 是否运行成功,Index records 是否大于 0
    只搜到导航或页脚recordExtractor 是否移除了侧边栏、导航、页脚
    新文章搜不到是否重新部署,Crawler 是否重新抓取
    本地能打开线上不行域名、HTTPS、访问限制、CORS 或网络环境

完成后,Algolia 的集成关系就是:

text
VitePress 页面 -> 公开部署地址 -> Algolia Crawler -> Algolia Index -> VitePress 搜索框

3. 部署

3.1. Cloudflare

部署建议

Cloudflare Pages 适合托管静态站点,支持 Git 仓库自动构建,也支持手动上传构建产物。

  1. 登录 Cloudflare,进入 Workers 和 Pages 概述页,创建应用程序。

    创建应用程序

  2. 选择构建源。Cloudflare Pages 支持 GitHub、GitLab,也支持手动上传资产。

    上传限制

    手动上传资产有大小限制。如果构建产物较大,建议将代码推送到 GitHub 或 GitLab 后,通过 Git 集成完成部署。

    选择构建源

  3. 选择存储库,设置构建和部署配置。

    text
    Build command: pnpm run build
    Build output directory: dist

    Algolia 公共参数按 2.5.4. 部署和验证 的统一清单写进配置常量;只有需要覆盖时才配置环境变量。构建平台会自动安装依赖,所以这里的 Build command 不需要写 pnpm install && pnpm run build。如果是手动本地打包,再使用下面第 5 步的命令。

    部署配置

    部署成功

  4. 设置部署分支、自动部署策略和部署目录。

    部署设置

  5. 本地打包,并将 dist 文件夹下的内容推送至 GitHub 仓库。

    sh
    pnpm install && pnpm run build

    本地打包

  6. 如果未开启自动部署,需要手动点击重试部署。

    重试部署

  7. 部署完成后,打开访问地址确认站点可用。

    访问地址

3.2. Netlify

访问提示

Netlify 节点和访问链路可能受网络环境影响,国内访问稳定性需要按实际情况验证。

  1. 登录 Netlify 进行 Sites 配置。Netlify 支持 GitHub、GitLab、Bitbucket、DevOps 等平台。

    支持的开源平台

  2. 选择项目,配置部署。

    text
    Build command: pnpm run build
    Publish directory: dist

    Algolia 公共参数按 2.5.4. 部署和验证 的统一清单写进配置常量;只有需要覆盖时才配置环境变量。Netlify 会在构建流程中自动安装依赖,Build command 保持 pnpm run build 即可。

    配置部署

  3. 修改域名,根据实际需要是否开启推送自动部署。

    站点配置

3.3. Gitee Pages

Gitee Pages 状态

2024 年 5 月曾出现无法访问、无法部署的情况。
目前 Gitee Pages 支持 Jekyll、Hugo、Hexo 编译静态资源。使用 VitePress 等其他静态网站生成器时,通常需要先在本地打包,再上传静态资源到仓库。

  1. 登录 Gitee,进行实名认证。

    实名认证

  2. 新建项目,并开通 Gitee Pages。

    Gitee Pages

  3. 设置 /src/.vitepress/config.mtsbase 选项与仓库名保持一致。

    设置 base

  4. 设置部署分支以及部署目录。

    部署设置

  5. 本地打包,并将 dist 文件夹推送至仓库。

    sh
    pnpm install && pnpm run build

    本地打包

  6. 点击更新。

    更新

  7. 部署完成后,打开访问地址确认站点可用。

    访问地址

最近更新