はじめに
本業エンジニアをしながら、Chrome拡張機能「SaveSmart - AI Reading List」を個人開発しました。
この記事では、Chrome拡張機能をゼロから作る方法を、実際のコードと一緒に解説します。
この記事で学べること:
- Chrome拡張機能の基本構造
- Manifest V3の書き方
- popup・background・content scriptの役割
- 実際に動くコードの全体像
Chrome拡張機能の基本構造
最小構成はこの4ファイルです:my-extension/
├── manifest.json # 拡張機能の設定ファイル
├── popup.html # ポップアップのUI
├── popup.js # ポップアップの動作
└── icons/ # アイコン画像
manifest.jsonの書き方
manifest.jsonは拡張機能の「設定ファイル」です。
{
"manifest_version": 3,
"name": "My Extension",
"version": "1.0",
"description": "Chrome拡張機能のサンプル",
"permissions": [
"storage",
"activeTab",
"scripting"
],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"background": {
"service_worker": "background.js"
}
}
Manifest V3の重要ポイント:
-
manifest_versionは必ず3にする - backgroundは
service_workerを使う(V2のscriptsは廃止) -
activeTab権限でアクティブなタブの情報を取得できる
popup.htmlの書き方
ツールバーアイコンをクリックしたときに表示されるUIです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<style>
body {
width: 300px;
padding: 16px;
background: #1a1a2e;
color: #e2e8f0;
font-family: sans-serif;
}
button {
width: 100%;
padding: 10px;
background: #4f46e5;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
}
#result {
margin-top: 12px;
font-size: 13px;
color: #9ca3af;
line-height: 1.6;
}
</style>
</head>
<body>
<h2 style="margin:0 0 12px;font-size:16px;">My Extension</h2>
<button id="save-btn">このページを保存</button>
<div id="result"></div>
<script src="popup.js"></script>
</body>
</html>
popup.jsの書き方
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('save-btn').addEventListener('click', handleSave);
});
async function handleSave() {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
const results = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: () => document.body.innerText.slice(0, 1000)
});
const pageText = results[0].result;
const { items = [] } = await chrome.storage.local.get(['items']);
const newItem = {
id: Date.now(),
title: tab.title,
url: tab.url,
text: pageText,
savedAt: new Date().toISOString()
};
await chrome.storage.local.set({ items: [newItem, ...items] });
document.getElementById('result').textContent = '保存しました!';
}
background.jsの書き方
background.jsはバックグラウンドで動くService Workerです。
chrome.runtime.onInstalled.addListener(() => {
console.log('拡張機能がインストールされました');
});
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'GET_DATA') {
sendResponse({ data: 'レスポンスデータ' });
}
return true;
});
Manifest V3のハマりポイント:
Service WorkerはページをまたいでDOMにアクセスできません。DOMの操作はcontent scriptで行う必要があります。
content.jsの書き方
まずmanifest.jsonに追加:
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
]
content.js:
chrome.runtime.sendMessage({
type: 'PAGE_TITLE',
title: document.title
});
chrome.runtime.onMessage.addListener((message) => {
if (message.type === 'HIGHLIGHT') {
document.body.style.border = '3px solid #4f46e5';
}
});
Chromeへのインストール方法
開発中の拡張機能をChromeにインストールする方法:
-
chrome://extensions/を開く - 右上の「デベロッパーモード」をオンにする
- 「パッケージ化されていない拡張機能を読み込む」をクリック
- 拡張機能のフォルダを選択
Chrome Web Storeへの申請方法
- Chrome Web Store Developer Dashboard にアクセス
- 開発者登録(5ドルの一回払い)
- 拡張機能をzip圧縮してアップロード
- スクリーンショット・説明文を入力して申請
審査は通常1〜3営業日かかります。
よくあるエラーと解決策
① Content Security Policy エラー
Refused to execute inline script because it violates the following Content Security Policy directive
解決:HTMLにonclick等のインラインイベントを書かない。全部JSファイルに書く。
② CORSエラー
Access to fetch at 'https://...' has been blocked by CORS policy
解決:バックエンド側にCORSヘッダーを追加する。
③ Service Worker が動かない
Manifest V3のService Workerは非常駐です。タイムアウトするので、長時間の処理は避けてメッセージパッシングで対応します。
実際に作ったもの
この記事の知識を使って「SaveSmart - AI Reading List」を作りました。
- ワンクリックでWebページを保存
- Claude APIでAI自動要約
- Cloudflare WorkersでバックエンドAPI
- Stripeで月額500円のサブスク決済
技術スタックの詳細はこちら:
https://zenn.dev/zmgt362/articles/ed9b17a1c99303
サービスはこちら:
https://savesmart-api.zmgt362.workers.dev
まとめ
Chrome拡張機能の基本構造:
-
manifest.json:設定ファイル -
popup.html/js:UIと動作 -
background.js:バックグラウンド処理 -
content.js:ページ上での処理
Manifest V3のポイント:
- インラインスクリプト禁止
- Service Worker必須
- CORSに注意
ぜひ自分の拡張機能を作ってみてください!