Astro 博客接入 Algolia DocSearch JS 实战

最近在优化博客的阅读体验时,我希望像主流技术文档网站一样,在顶部导航栏里加入一个搜索框,支持快捷键唤起、弹窗搜索和结果跳转。最终选择的是 Algolia 官方提供的 DocSearch JS

这篇文章记录一下完整接入过程,项目技术栈是:

  • Astro
  • Vue 组件
  • pnpm
  • Algolia DocSearch JS v4

为什么选择 DocSearch

对技术博客来说,搜索体验非常影响阅读效率。相比自己手写一个搜索弹窗,DocSearch 已经提供了比较完整的能力:

  • 默认生成可访问的搜索按钮和弹窗
  • 支持 Ctrl / Command + K 快捷键
  • 支持搜索结果高亮
  • 支持键盘导航
  • 和 Algolia 索引直接对接

官方接入方式也比较简单,核心就是准备一个容器,然后调用 docsearch() 初始化。

准备 Algolia 配置信息

接入前需要先准备三项信息:

  • appId:Algolia Application ID
  • apiKey:Search API Key
  • indexName:DocSearch 使用的索引名称

本项目使用的信息如下:

const appId = 'PEKHQFSYYO';
const apiKey = 'ffa9cd55f1c2d8f18c5c7742ba487764';
const indexName = 'momoc_robot';

需要注意的是,浏览器端只能放 Search API Key,不要把 Admin API Key 写到前端代码里。

安装依赖

项目里先安装 DocSearch JS:

pnpm add @docsearch/js@4

DocSearch 的官方示例中还会引入样式:

import '@docsearch/css';

所以也需要安装对应的 CSS 包:

pnpm add @docsearch/css@4

安装完成后,package.json 中会出现:

{
  "dependencies": {
    "@docsearch/css": "^4.6.3",
    "@docsearch/js": "^4.6.3"
  }
}

封装搜索组件

src/components 下新增 DocSearchBox.vue,用于初始化 DocSearch。

核心代码如下:

<script setup lang="ts">
import docsearch from '@docsearch/js';
import '@docsearch/css';
import { onBeforeUnmount, onMounted, ref } from 'vue';

const searchContainer = ref<HTMLElement | null>(null);
let searchInstance: ReturnType<typeof docsearch> | undefined;

onMounted(() => {
  if (!searchContainer.value) return;

  searchInstance = docsearch({
    container: searchContainer.value,
    appId: 'PEKHQFSYYO',
    indexName: 'momoc_robot',
    apiKey: 'ffa9cd55f1c2d8f18c5c7742ba487764',
  });
});

onBeforeUnmount(() => {
  searchInstance?.destroy();
});
</script>

<template>
  <div ref="searchContainer" class="docsearch-root" />
</template>

这里有几个细节:

  • container 使用真实 DOM 元素,而不是手动写一个 input。
  • onMounted 后再初始化,避免服务端渲染阶段访问浏览器 DOM。
  • onBeforeUnmount 中调用 destroy(),避免组件卸载后留下事件监听。

适配博客现有样式

DocSearch 自带默认样式,但直接使用会和博客当前风格有一点割裂,所以可以通过覆盖 CSS 变量和按钮样式来适配。

例如:

.docsearch-root :global(.DocSearch-Button) {
  width: 100%;
  height: 36px;
  margin: 0;
  padding: 0 10px 0 12px;
  border: 1px solid var(--c-hairline);
  border-radius: 8px;
  background: var(--c-surface-soft);
  color: var(--c-muted);
  box-shadow: none;
}

:global(.DocSearch) {
  --docsearch-primary-color: var(--c-primary);
  --docsearch-highlight-color: var(--c-primary);
  --docsearch-text-color: var(--c-ink);
  --docsearch-muted-color: var(--c-muted);
  --docsearch-modal-background: var(--c-canvas);
}

如果站点支持暗色模式,也可以继续覆盖暗色主题下的变量:

:global([data-theme="dark"] .DocSearch) {
  --docsearch-container-background: rgba(0, 0, 0, 0.58);
  --docsearch-modal-background: var(--c-surface-dark-elevated);
  --docsearch-footer-background: var(--c-surface-dark-soft);
  --docsearch-hit-background: var(--c-surface-dark-soft);
}

这样既保留 DocSearch 的完整交互,又能让视觉风格和站点保持统一。

接入顶部导航栏

本项目顶部导航是 Vue 组件 NavBar.vue,因此只需要引入刚刚封装好的搜索组件:

<script setup lang="ts">
import DocSearchBox from './DocSearchBox.vue';
</script>

然后把它放到 logo 旁边:

<div class="brand-search">
  <a href="/" class="logo" aria-label="Momoc's Blog — 首页">
    <span class="logo-text">Momoc's Blog</span>
  </a>

  <DocSearchBox />
</div>

再补充一段布局样式:

.brand-search {
  display: flex;
  align-items: center;
  gap: 18px;
  min-width: 0;
}

移动端空间比较紧张,可以隐藏搜索按钮里的占位文字和快捷键,只保留图标按钮。

增加 Algolia 预连接

为了让首次打开搜索弹窗时请求更快,可以在全局布局 Layout.astro<head> 中添加 Algolia 的 preconnect:

<link rel="preconnect" href="https://PEKHQFSYYO-dsn.algolia.net" crossorigin>

这一步不是必须的,但属于比较低成本的体验优化。

验证配置是否可用

前端能打开搜索弹窗,不代表索引一定配置正确。最直接的方式是用 Algolia 查询接口验证 indexName

例如:

curl -X POST \
  'https://PEKHQFSYYO-dsn.algolia.net/1/indexes/momoc_robot/query' \
  -H 'X-Algolia-Application-Id: PEKHQFSYYO' \
  -H 'X-Algolia-API-Key: ffa9cd55f1c2d8f18c5c7742ba487764' \
  -H 'Content-Type: application/json' \
  --data '{"query":"javascript","hitsPerPage":1}'

如果返回 200,并且能看到 nbHits,说明索引名和 Search API Key 是可用的。

本项目验证结果中,momoc_robot 返回了搜索结果,说明配置正确。

本地构建验证

最后执行构建:

pnpm build

构建通过后,再本地预览:

pnpm preview

打开页面后检查:

  • 顶部导航栏是否出现搜索按钮
  • 点击按钮是否能打开 DocSearch 弹窗
  • 输入关键词后是否有搜索结果
  • Ctrl / Command + K 是否能唤起搜索
  • 暗色模式下弹窗颜色是否正常
  • 移动端下按钮是否不会挤压导航

常见问题

搜索弹窗能打开,但没有结果

优先检查 indexNameappIdapiKey 正确时,如果索引名写错,查询通常会返回 404

前端代码里能不能放 apiKey

可以放 Search API Key,因为它本来就是用于浏览器查询的公开 key。但不要放 Admin API Key。

构建时出现 chunk size warning

DocSearch 自身包含弹窗、搜索、快捷键和 Algolia 客户端逻辑,体积不算特别小。Vite 可能会提示部分 chunk 超过 500 kB,这不是构建错误。

如果后续很在意首屏体积,可以考虑把搜索组件改成按需加载,在用户点击搜索按钮时再动态加载 DocSearch。

小结

这次接入的改动主要集中在三个地方:

  • 新增 DocSearchBox.vue 封装官方 docsearch() 初始化逻辑。
  • NavBar.vue 中把搜索框放到 logo 旁边。
  • Layout.astro 中增加 Algolia preconnect。

整体改动不算大,但体验提升很明显。对于自定义 Astro 博客来说,DocSearch 是一个很适合用来补齐站内搜索体验的方案。