用搜索数据 API 做一个关键词联想组件(防抖 + 缓存 + 可复用)

搜索框的联想/自动补全,大部分实现用的是「内置词库」或「热门词表」。但用户真正在搜什么,答案在谷歌的相关搜索里------related_searches 和 people_also_ask 就是现成的真实联想词。这篇记录我做一个基于搜索数据 API 的联想组件:防抖、缓存、可复用,前端向,代码可直接跑。

需求

用户在搜索框打字时,出现基于真实搜索数据的联想词。要求:

  1. 输入停顿后请求(防抖),不每键都发
  2. 相同输入不重复请求(前端缓存)
  3. 做成可复用组件,不绑定业务

数据源

联想词来自搜索数据 API 的返回。我用的是 SerpBase(serpbase.dev)的 /google/search 端点,返回里带 related_searches(相关搜索)和 people_also_ask(人们还问),POST JSON + X-API-Key 认证。

关键安全点:API key 不能放浏览器。联想请求必须走后端代理,前端只调自己的接口。

第一步:后端代理接口

一个极薄的代理,把联想词捞出来(Node 版):

js 复制代码
// server.mjs
import express from "express";
const app = express();

const SERPBASE = "https://api.serpbase.dev";
const API_KEY = process.env.SERPBASE_API_KEY;   // key 只在服务端

app.get("/api/suggest", async (req, res) => {
  const q = (req.query.q || "").trim();
  if (!q) return res.json([]);

  const resp = await fetch(`${SERPBASE}/google/search`, {
    method: "POST",
    headers: { "Content-Type": "application/json", "X-API-Key": API_KEY },
    body: JSON.stringify({ q, hl: "zh-cn", gl: "cn" }),
  });
  const data = await resp.json();

  // 把相关搜索 + PAA 问题合并成联想词列表
  const related = data.related_searches ?? [];
  const paas = (data.people_also_ask ?? []).map((p) => p.question);
  res.json([...new Set([...related, ...paas])].slice(0, 8));
});

app.use(express.static("public"));
app.listen(3000, () => console.log("http://localhost:3000"));

第二步:前端联想组件

做成一个可复用的 Web Component,不依赖框架:

html 复制代码
<!-- public/index.html -->
<script>
  class KeywordSuggestion extends HTMLElement {
    connectedCallback() {
      this._cache = new Map();          // 请求缓存:相同输入不重复发
      this._timer = null;
      this.innerHTML = `
        <input placeholder="输入关键词,看真实联想词..." />
        <ul hidden></ul>`;
      const input = this.querySelector("input");
      const list = this.querySelector("ul");

      input.addEventListener("input", (e) => {
        clearTimeout(this._timer);
        const q = e.target.value.trim();
        if (!q) { list.hidden = true; return; }
        this._timer = setTimeout(() => this._suggest(q, list), 300);  // 防抖 300ms
      });
    }

    async _suggest(q, list) {
      if (this._cache.has(q)) {
        this._render(this._cache.get(q), list);   // 命中缓存,不发请求
        return;
      }
      const words = await fetch(`/api/suggest?q=${encodeURIComponent(q)}`).then(r => r.json());
      this._cache.set(q, words);
      this._render(words, list);
    }

    _render(words, list) {
      list.innerHTML = words.map(w => `<li data-word="${w}">${w}</li>`).join("");
      list.hidden = words.length === 0;
      list.querySelectorAll("li").forEach(li =>
        li.addEventListener("click", () => {
          this.querySelector("input").value = li.dataset.word;
          list.hidden = true;
        })
      );
    }
  }
  customElements.define("keyword-suggestion", KeywordSuggestion);
</script>

<keyword-suggestion></keyword-suggestion>

要点:

  • 防抖 300ms:停止输入 300ms 后才请求,避免每键一请求
  • 前端缓存 Map:相同输入直接命中,不重复调接口、不浪费 credits
  • Web Component :<keyword-suggestion> 一处定义,多处复用

第三步:效果与体验优化

几个值得加的点:

空态提示。 如果联想词为空,提示「无联想词」而不是空白,体验更好。

点击联想后继续联想。 点击一个联想词,可以把它当作新输入再触发联想------形成「词 → 相关词」的探索链:

js 复制代码
// 点击后把词填入输入框,再自动触发一次联想
li.addEventListener("click", () => {
  input.value = li.dataset.word;
  setTimeout(() => this._suggest(input.value, list), 100);
});

选中高亮。 键盘上下键导航是加分项,这里先不展开。

成本和注意点

  • /google/search 每次 1 credits;前端缓存保证了「同一用户同一次输入只请求一次」
  • 注册送 100 次免费搜索,开发测试完全够
  • 标准包 10买2万次(10 买 2 万次( 10买2万次(0.50/千次),永不过期------这种交互型调用量不大
  • related_searches / people_also_ask 是可选字段,服务端用 ?? [] 兜底

收尾

这个组件的价值,是把「谷歌的真实搜索数据」变成产品里的交互能力------搜索框联想、选题推荐、站内搜索补全都能用。核心技术点就三个:防抖、缓存、后端代理(保 key 安全)。

接口参数细节在 SerpBase 官方文档。从「复制组件 + 一个代理接口」开始,你就能在任意页面拥有基于真实搜索数据的联想功能。有更好的联想交互,评论区聊聊。

相关推荐
Patrick在香港1 分钟前
MCP 的 initialize 握手真的没了?67 行标准库实测 2026-07-28 规范
python·agent·claude·mcp·json-rpc
vx_Biye_Design4 分钟前
springboot角色扮演服务平台65161-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·python·spring·课程设计
追烽少年x7 分钟前
从零构建一个3D点云预览器:Python + PySide6 + pyqtgraph 实战
python·3d
weixin_4404016920 分钟前
质朴的爬虫+数据处理
爬虫·python·数据分析·pandas
估值探索者21 分钟前
【Python量化系统工程实战 #01】数据存储选型 CSVSQLiteMySQL 对比与 SQLite 实战建库
开发语言·jvm·python·sqlite·api接口·数据api接口·股票数据api接口
计算机毕业编程指导师25 分钟前
计算机毕设答辩技巧:基于Hadoop+Django的公共交通运营数据分析与可视化系统怎么做 源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·hadoop·python·spark·毕业设计·课程设计·交通运行
砚底藏山河27 分钟前
量化实战:行情数据 Schema 演进与向后兼容
java·python·金融·maven
苏离~Hack28 分钟前
InfoScraper:面向授权目标的一站式资产信息收集工具
python
计算机毕业编程指导师31 分钟前
【计算机毕设选题推荐】基于Hadoop+Django高频电力消耗大数据分析系统从0到1 源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
hadoop·python·数据分析·spark·毕业设计·课程设计·电力
FYKJ_201034 分钟前
springboot手工蜀绣在线销售系统60947-计算机课程设计、毕业设计
java·spring boot·后端·python·mysql·spark·课程设计