前回の続き。Vite/CRXJS/Vueで作るときの備忘録(*´ω`*)
使ったサンプルはこちらで公開中(*´ω`*)
環境構築
プロジェクトの作成
# viteでプロジェクトを作成 $ pnpm create vite chrome-extension-sample --template vue-ts $ cd chrome-extension-sample # .npmrcを設定 $ echo "auto-install-peers=true" > .npmrc # @crxjs/vite-pluginの追加。vite3はbeta版 $ pnpm add @crxjs/vite-plugin@beta -D
manifest.jsonの設定
manifest.jsonが必要だけど、CRXJSでは.tsにも対応してる。
補完や環境変数で切り替えができるのでJSONよりも便利。
// manifest.config.ts import { defineManifest } from "@crxjs/vite-plugin"; export const manifest = defineManifest({ manifest_version: 3, name: "chrome-extension-sample", description: "chrome-extension-sample", version: "0.0.1", action: { default_popup: "index.html", }, });
viteでビルドするときにmanifest.config.tsが含まれるように、
tsconfig.node.jsonを変更しておく。
# tsconfig.node.json
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
- "include": ["vite.config.ts"]
+ "include": ["vite.config.ts", "manifest.config.ts"]
}
最後に、vite.config.tsへCRXJSプラグイン関連の設定を追加する。
// vite.config.ts + import { crx } from "@crxjs/vite-plugin"; import vue from "@vitejs/plugin-vue"; import { defineConfig } from "vite"; + import { manifest } from "./manifest.config"; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [vue()], + plugins: [vue(), crx({ manifest })], });
Chrome Extesions APIの型定義を追加
型定義が提供されているので、追加しておく。
$ pnpm add -D chrome-types
実際の拡張機能側で使うので、tsconfig.jsonに追加
# tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"resolveJsonModule": true,
- "isolatedModules": true,
+ "isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
- "noEmit": true
+ "noEmit": true,
+ "types": ["chrome-types"]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
Tailwind CSSのインストール
必要なパッケージをインストール。
$ pnpm add -D postcss tailwindcss autoprefixer
各種設定ファイルを追加。
// postcss.config.cjs module.exports = { plugins: { "postcss-import": {}, "tailwindcss/nesting": {}, tailwindcss: {}, autoprefixer: {}, }, };
// tailwind.config.cjs /** @type {import('tailwindcss').Config} */ module.exports = { prefix: "twcrx-", content: ["./pages/*.html", "./src/**/*.{vue,ts}"], };
/* src/assets/tailwind.css */ @tailwind base; @tailwind components; @tailwind utilities;
最後にtailwind.cssを読み込むように、
src/main.tsを変更。
// src/main.ts
import { createApp } from "vue";
+ import "./assets/tailwind.css";
import "./style.css";
import App from "./App.vue";
createApp(App).mount("#app");
使い方
popupの追加
デフォルトではindex.htmlが追加されているので、それを参考に。
構成としては、以下のような関連。
src/App.vueで画面を構成src/main.tsで、src/App.vueをマウントindex.htmlで、src/main.tsを呼び出しmanifest.config.tsのaction.default_popupで、index.htmlを指定
ポップアップ画面の要素を確認したい場合は、
拡張機能ボタンを右クリックして、「ポップアップを検証」を選択すると、
DevToolsが開くので、そこで確認ができる。


backgroundの追加
backgroundの処理を追加するときはこんな感じ。
まずは、実際の処理を追加。
// src/background.ts // タブがアクティブになったとき chrome.tabs.onActivated.addListener(async ({ tabId, windowId }) => { const tab = await chrome.tabs.get(tabId); console.table({ onActivated: { tabId: tabId, windowId: windowId, "tab.url": tab?.url, "tab.active": tab?.active, "tab.status": tab?.status, "location.href": location.href, }, }); }); // タブが更新されたとき chrome.tabs.onUpdated.addListener((tabId, info, tab) => { console.table({ onUpdated: { tabId: tabId, windowId: tab?.windowId, "tab.url": tab?.url, "tab.active": tab?.active, "tab.status": tab?.status, "location.href": location.href, }, }); });
次にsrc/background.tsを利用するように、
permissionsとbackgroundをmanifest.config.tsに追加。
// manifest.config.ts
import { defineManifest } from "@crxjs/vite-plugin";
export const manifest = defineManifest({
manifest_version: 3,
name: "chrome-extension-sample",
description: "chrome-extension-sample",
version: "0.0.1",
+ permissions: ["tabs"],
action: {
default_popup: "index.html",
},
+ background: {
+ service_worker: "src/background",
+ type: "module",
+ },
});
- Background | CRXJS Vite Plugin
- Chrome Extensions: Manage events with service workers - Chrome Developers
拡張機能の画面から「ビューを検証」の横にある
「Service Worker」をクリックすると、DevToolsでコンソールを確認できる。


static contentsの追加
画面を開いたら、開いている画面になにか処理を埋め込みたい場合はこれ。
まずは、追加したいcontent scriptsを用意。
// src/scripts/addOutline.ts // <a>に赤色のアウトラインを追加 document.body.querySelectorAll("a").forEach((elm) => { elm.style.outline = "dotted 1px red"; }); // <img>に青色のアウトラインを追加 document.body.querySelectorAll("img").forEach((elm) => { elm.style.outline = "dotted 1px blue"; });
次にsrc/scripts/addOutline.tsを利用するように、
content_scriptsをmanifest.config.tsに追加。
// manifest.config.ts
import { defineManifest } from "@crxjs/vite-plugin";
export const manifest = defineManifest({
// ...
permissions: ["tabs"],
// ...
background: {
// ...
},
+ content_scripts: [
+ {
+ matches: ["<all_urls>"],
+ js: ["src/scripts/addOutline"],
+ },
+ ],
});

dynamic contentsの追加
任意のタイミングで開いている画面になにか処理を埋め込みたい場合はこっち。
SPAなどの場合、ページの変更をタブで検知できないので、
ページが更新されたときに処理を入れたりできる。
background.tsから、chrome.scriptingAPIで差し込む。
// background2.ts import addOutline from "./scripts/addOutline?script"; // タブが更新されたときにcontent scriptsを実行する chrome.tabs.onUpdated.addListener((tabId, info, tab) => { // chromeの画面などの場合は、content scriptsを実行できないので、スキップ if (!tab.url || tab.url.startsWith("chrome://")) return; // 読み込みが完了していない場合は、スキップ if (info.status != "complete") return; chrome.scripting.executeScript({ target: { tabId }, files: [addOutline], }); });
そのままだと型の警告が出るので、vite-env.d.tsを修正。
/// <reference types="vite/client" /> + declare module "*?script" { + const script: any; + export default script; + }
最後に、動的に差し込めるようにmanifest.config.tsの
permissionsとhost_permissionsを変更する。
// manifest.config.ts
import { defineManifest } from "@crxjs/vite-plugin";
export const manifest = defineManifest({
// ...
- permissions: ["tabs"],
+ permissions: ["tabs", "scripting"],
// ...
background: {
- service_worker: "src/background",
+ service_worker: "src/background2",
type: "module",
},
- content_scripts: [
- {
- matches: ["<all_urls>"],
- js: ["src/scripts/addOutline"],
- },
- ],
+ host_permissions: ["<all_urls>"],
});
permissionsでchrome.scriptingAPIを使う権限を追加し、
host_permissionsで差し込めるURLを指定する感じ。

小ネタ
iconを設定する
public/配下に配置して、manifest.config.tsで設定すればOK
// manifest.config.ts import { defineManifest } from "@crxjs/vite-plugin"; export const manifest = defineManifest({ manifest_version: 3, // ... icons: { "16": "icons/icon_16.png", "32": "icons/icon_32.png", "48": "icons/icon_48.png", "128": "icons/icon_128.png", }, // ... });
必要なアイコンのサイズは以下の通り。形式はPNGがよいっぽい。
| Icon Size | Icon Use |
|---|---|
| 16x16 | 拡張機能で表示されるアイコン |
| 32x32 | Windowsで必要なサイズ |
| 48x48 | 拡張機能の管理画面で表示されるサイズ |
| 128x128 | Chrome Web Storeのインストール画面で表示されるサイズ |
画像などのアセットを読み込む
差し込んだcontent scriptsで画像などを扱う場合は、ひと手間必要。
拡張機能のURL配下に配置されるので、
chrome.runtime.getURL()を使う必要がある。
import logo from './logo.png' const url = chrome.runtime.getURL(logo) const iconUrl = chrome.runtime.getURL("/icons/icon.png")
また、拡張機能内のファイルにアクセスするには、
web_accessible_resourcesの設定が必要だけど、
importを使わない場合は、CRXJSで自動設定されないので、
手動でmanifest.config.tsに指定が必要。
// manifest.config.ts
import { defineManifest } from "@crxjs/vite-plugin";
export const manifest = defineManifest({
// ...
+ web_accessible_resources: [
+ {
+ matches: ["<all_urls>"],
+ resources: ["icons/*"],
+ use_dynamic_url: false,
+ },
+ ],
});
content scriptsで.vueを差し込む
content scriptsを使って開いている画面に.vueを追加したい場合は、
マウント先のdivを作って配置する必要がある。
ただの四角を表示する.vue。
<!-- src/components/DummyView.vue --> <template> <div class="twcrx-w-20 twcrx-h-20 twcrx-bg-red-400"></div> </template>
content scriptsはこんな感じ。
// src/scripts/addDummyView.ts import { createApp } from "vue"; import DummyView from "../components/DummyView.vue"; import "../assets/tailwind.css"; const ROOT_ELEMENT_ID = "crx-root"; // マウント先のdiv要素をbody直下に配置 const root = document.createElement("div"); root.id = ROOT_ELEMENT_ID; root.className = "twcrx-fixed twcrx-top-0 twcrx-left-0 twcrx-z-10"; document.body.append(root); // 追加したdiv要素にマウント const app = createApp(DummyView).mount(root);
.vueが型定義で警告が出るので、
vue-env.d.tsに設定を追加しておく。
/// <reference types="vite/client" /> declare module "*.vue" { import type { DefineComponent } from "vue"; const component: DefineComponent<{}, {}, any>; export default component; } declare module "*?script" { const script: any; export default script; }
開発用と本番用でviteの設定を切り替える
vite.config.tsでは、modeを受け取ることができる。
それに応じて、設定を変更すると便利。
開発時はデバッグしやすくするためにminifyしないようにしたり、
本番時はconsole.logが表示されないようにしたりできる。
import { crx } from "@crxjs/vite-plugin"; import vue from "@vitejs/plugin-vue"; import { defineConfig } from "vite"; import { manifest } from "./manifest.config"; // https://ja.vitejs.dev/guide/env-and-mode.html#modes // mode = "development" | "production" export default ({ mode }) => { const isProd = mode === "production"; return defineConfig({ plugins: [vue(), crx({ manifest })], build: { // 開発時はminifyしない minify: isProd, }, esbuild: { // 本番時はconsoleを削除する drop: isProd ? ["console"] : undefined, }, }); };
manifestでpackage.jsonの値を利用する
名前やバージョンなどはpackage.jsonから使いたいときはこんな感じ。
// package.json { "name": "chrome-extension-sample", "description": "sample for chrome extention", "version": "0.0.0", // ... }
// manifest.config.ts import { defineManifest } from "@crxjs/vite-plugin"; import pkg from "./package.json"; export const manifest = defineManifest({ manifest_version: 3, name: pkg.name, description: pkg.description, version: pkg.version, // ... });
viteでJSONをimportするので、
tsconfig.node.json側に設定を追加する。
// tsconfig.node.json
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
- "allowSyntheticDefaultImports": true
+ "allowSyntheticDefaultImports": true,
+ "resolveJsonModule": true
},
- "include": ["vite.config.ts", "manifest.config.ts"]
+ "include": ["vite.config.ts", "manifest.config.ts", "package.json"]
}
開発用と本番用でmanifestの内容を切り替える
viteの設定と同様に、manifestもmodeを使って切り替えることができる。
開発版では名前の前に「【DEV】」をつける場合はこんな感じ。
// manifest.config.ts import { defineManifest } from "@crxjs/vite-plugin"; import pkg from "./package.json"; export const manifest = defineManifest(async (env) => ({ manifest_version: 3, name: env.mode == "production" ? pkg.name : `【DEV】 ${pkg.name}`, // ... }));
以上!! これで基本的な使い方はわかった気がする(*´ω`*)