跳转到主要内容
你将构建一个 Omnibox(地址栏)快捷方式。在地址栏输入 gh,再输入一个查询词,就会跳到 GitHub 搜索结果页。一路上你会接好一份 manifest.json,并在后台 service worker 中处理输入。你还会练习每个项目都会遵循的开发循环(createdevbuild)。

你将构建什么

能力你会得到什么
Omnibox 关键字流程在浏览器地址栏中输入 gh 就能触发扩展行为
后台事件处理通过 service worker 处理用户输入
本地开发循环快速运行、加载并验证扩展行为
渐进增强基础流程跑通后,再加入实时 GitHub 建议

计划

让 GitHub 搜索像浏览器原生快捷方式一样快。扩展会保留关键字 gh;当你输入 gh 加一段查询后,它会打开 GitHub 搜索结果页。

第 1 步:创建扩展

使用 Extension.js 的 create 命令脚手架生成一个名为 github-search 的最小扩展。
npx extension@latest create github-search

第 2 步:创建 manifest 文件

每个扩展都从一份 manifest 文件开始。它定义元数据、权限以及运行时文件。基于 上面的计划,设置 gh 快捷方式,并为用户事件添加一个 service worker。
{
  "manifest_version": 3,
  "name": "GitHub Search",
  "version": "1.0",
  "omnibox": { "keyword": "gh" },
  "background": {
    "service_worker": "service_worker.js"
  }
}
  • omnibox.keyword:当你输入 gh 时,浏览器会触发一个事件。
  • background.service_worker:监听你触发的事件。

第 3 步:创建后台 service worker

在浏览器扩展中,后台 service worker(一个独立于任何可见页面运行的脚本)负责处理浏览器事件。 在这个示例中,添加一个脚本,监听 Omnibox 输入,并把查询路由到 GitHub 搜索。 创建 service_worker.js
// When the user has accepted what is typed into the omnibox.
chrome.omnibox.onInputEntered.addListener((text) => {
  // Convert any special character (spaces, &, ?, etc)
  // into a valid character for the URL format.
  const encodedSearchText = encodeURIComponent(text);
  const url = `https://github.com/search?q=${encodedSearchText}&type=issues`;

  chrome.tabs.create({ url });
});
每当你在地址栏中输入 “gh” 后跟其他内容时,上面的脚本会打开一个新标签页,显示 GitHub 搜索结果。

第 4 步:加载扩展

你的 package.json 文件现在看起来是这样的:
{
  "scripts": {
    "dev": "extension dev",
    "start": "extension start",
    "build": "extension build"
  },
  "devDependencies": {
    "extension": "latest"
  }
}
这些脚本就是 Extension.js 的默认命令。首次运行扩展:
npm run dev
如果设置正确,Extension.js 会使用全新的配置文件启动 Chrome,把 github-search 作为未打包扩展加载,并在终端中打印一条就绪横幅。Chrome 地址栏现在会把 gh 识别为关键字。 输入 gh 后空一格,再输入 extension.js,按下回车。新标签页将打开 https://github.com/search?q=extension.js&type=issues
你现在已经有了一个可以在 GitHub 上搜索的可用浏览器扩展。

第 5 步:让它更好用

通过为 Omnibox 添加输入监听器,让搜索体验更上一层楼——直接在地址栏中显示建议。 更新 service_worker.js,在用户输入时拉取 GitHub 建议并展示:
service_worker.js
// Create a debounce function to avoid excessive
// calls to the GitHub API while the user is still
// typing the search query.
function debounce(fn, delay) {
  let timeoutID;
  return function (...args) {
    if (timeoutID) clearTimeout(timeoutID);
    timeoutID = setTimeout(() => fn(...args), delay);
  };
}

// When the user has changed what is typed into the omnibox.
chrome.omnibox.onInputChanged.addListener(
  debounce(async (text, suggest) => {
    const response = await fetch(
      `https://api.github.com/search/issues?q=${text}`,
    );
    const data = await response.json();
    const suggestions = data.items.map((issue) => ({
      content: issue.html_url,
      description: issue.title,
    }));

    suggest(suggestions);
  }, 250),
);

// When the user has accepted what is typed into the omnibox.
chrome.omnibox.onInputEntered.addListener((text) => {
  // Convert any special character (spaces, &, ?, etc)
  // into a valid character for the URL format.
  const encodedSearchText = encodeURIComponent(text);
  const url = `https://github.com/search?q=${encodedSearchText}&type=issues`;

  chrome.tabs.create({ url });
});
这段代码在地址栏中直接加入了实时的 GitHub 建议。 你现在已经拥有了一个可用的 GitHub 搜索扩展。继续在上面迭代,根据你自己的工作流改造它。

下一步