> ## Documentation Index
> Fetch the complete documentation index at: https://extension.js.org/llms.txt
> Use this file to discover all available pages before exploring further.

# 逐步建立你的第一個瀏覽器擴充功能

> 用 Extension.js 從頭到尾打造你的第一個瀏覽器擴充功能。建立 GitHub 搜尋的 Omnibox 捷徑，並掌握核心開發循環。

你將建立一個 Omnibox（網址列）捷徑。在網址列輸入 `gh`、再輸入查詢字串，就會直接前往 GitHub 搜尋結果。過程中你會串接一份 `manifest.json`，並在背景 service worker 中處理輸入。同時也會熟悉每個專案都會遵循的開發循環（`create` → `dev` → `build`）。

## 你會做出什麼

| 能力            | 你會獲得的成果                   |
| ------------- | ------------------------- |
| Omnibox 關鍵字流程 | 在瀏覽器網址列輸入 `gh` 觸發擴充功能行為   |
| 背景事件處理        | 透過 service worker 處理使用者輸入 |
| 本地開發循環        | 快速執行、載入並驗證擴充功能行為          |
| 漸進式強化         | 在基本流程運作後加入即時的 GitHub 搜尋建議 |

## 計畫

讓 GitHub 搜尋速度像瀏覽器原生捷徑一樣快。擴充功能會保留關鍵字 `gh`；當你輸入 `gh` 加上查詢字串後，會打開 GitHub 的搜尋結果。

## 步驟 1：建立擴充功能

使用 Extension.js 的 `create` 指令來建立一個名為 `github-search` 的最小擴充功能。

<CodeGroup>
  ```bash npm theme={null}
  npx extension@latest create github-search
  ```

  ```bash pnpm theme={null}
  pnpx extension@latest create github-search
  ```

  ```bash yarn theme={null}
  yarn dlx extension@latest create github-search
  ```

  ```bash bun theme={null}
  bunx extension@latest create github-search
  ```

  ```bash bun theme={null}
  bunx extension@latest create github-search
  ```
</CodeGroup>

## 步驟 2：建立 manifest 檔案

<Frame>
  <iframe className="w-full aspect-video rounded-xl" src="https://www.loom.com/embed/1193dc69f7b74a56a5f5d9e0324c255d?sid=99132929-4c05-40e7-b804-3f242daf95ea" title="Create the manifest file" loading="lazy" referrerPolicy="no-referrer-when-downgrade" allow="clipboard-write; encrypted-media; fullscreen" allowFullScreen />
</Frame>

每個擴充功能都從 manifest 檔案開始。它定義中繼資料、權限與執行階段檔案。根據[上述計畫](#計畫)，設定 `gh` 捷徑並加入處理使用者事件的 service worker。

```json theme={null}
{
  "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`：

```js theme={null}
// 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` 檔案現在看起來像這樣：

```json theme={null}
{
  "scripts": {
    "dev": "extension dev",
    "start": "extension start",
    "build": "extension build"
  },
  "devDependencies": {
    "extension": "latest"
  }
}
```

這些 script 是 Extension.js 預設的指令。第一次執行擴充功能：

<CodeGroup>
  ```bash npm theme={null}
  npm run dev
  ```

  ```bash pnpm theme={null}
  pnpm run dev
  ```

  ```bash yarn theme={null}
  yarn run dev
  ```

  ```bash bun theme={null}
  bun run dev
  ```

  ```bash bun theme={null}
  bun run dev
  ```
</CodeGroup>

如果設定正確，Extension.js 會啟動使用全新設定檔的 Chrome、以未封裝擴充功能的方式載入 `github-search`，並在終端機印出就緒訊息。Chrome 的網址列現在已認得 `gh` 為關鍵字。

輸入 `gh` 接著按一下空白鍵，輸入 `extension.js`，按下 Enter。會開啟新分頁前往 `https://github.com/search?q=extension.js&type=issues`。

<Frame>
  <iframe className="w-full aspect-video rounded-xl" src="https://www.loom.com/embed/777544977a32444ba6de4ff23bdaccbc?sid=360eb1b1-af3a-480b-9e71-41a7fb01ca6e" title="Load and run your first extension" loading="lazy" referrerPolicy="no-referrer-when-downgrade" allow="clipboard-write; encrypted-media; fullscreen" allowFullScreen />
</Frame>

你現在有一個可以搜尋 GitHub 的可運作擴充功能了。

## 步驟 5：讓它變得更好

透過 Omnibox 輸入監聽器，在網址列直接顯示建議，藉此改善搜尋體驗。

更新 `service_worker.js`，在輸入時抓取 GitHub 建議並顯示出來。

```js title="service_worker.js" theme={null}
// 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 搜尋擴充功能。繼續迭代，調整成符合自己工作流程的版本。

## 後續步驟

* 用[範本](/docs/getting-started/templates)再建立一個擴充功能。
* 用 [Playwright E2E](/docs/workflows/playwright-e2e) 加入自動化檢查。
* 隨著擴充功能成長，瀏覽[疑難排解](/docs/workflows/troubleshooting)、[安全檢查清單](/docs/workflows/security-checklist)與[效能手冊](/docs/workflows/performance-playbook)。
