Chrome 插件开发入门④:Popup/Options/BDevtools 页面快速搭建模板
目标
-
一条命令生成三页面骨架
-
共用路由 & 状态,打包体积 < 80 KB(gzip)
-
暗黑模式自动跟随系统
-
直接复用第③篇项目,无需改配置
一、为什么选择「三页面」
表格
复制
| 页面 | 生命周期 | 最大限制 | 典型用途 |
|---|---|---|---|
| Popup | 点击图标才挂载,关闭即卸载 | 高度≤600 px,宽≤800 px | 快捷操作、一键截图 |
| Options | 用户手动进 chrome://extensions → 详情 → 扩展选项 | 无尺寸限制,可新开标签 | 复杂设置、账号登录 |
| Devtools | F12 面板自建侧边栏 | 只能访问当前 inspectedWindow | 调试爬虫、性能面板 |
三页面共享同一份 React 运行时,用 Chrome 官方 storage 做持久化,避免重复加载。
二、5 分钟模板(Plasmo 0.84+ 已原生支持)
-
安装 & 生成
bash
复制
# 延续第③篇项目
cd hello-mv3
plasmo generate popup options devtools-page # 一键生成三页面
pnpm i react react-dom tailwindcss @tailwindcss/typography
-
目录结构(自动生成)
复制
src
├── popup/index.tsx # 默认入口
├── options/index.tsx # 选项页
├── devtools/index.tsx # DevTools 侧边栏
├── devtools/background.ts # DevTools 与 Background 通道
└── style/tailwind.css
-
配置暗黑模式
css
复制
/* src/style/tailwind.css */
@tailwind base;
@layer base {
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark;
}
}
}
-
共享状态(Zustand + storage 同步)
TypeScript
复制
// src/store.ts
import { create } from 'zustand';
import { chromeStorageSync } from 'zustand/middleware';
interface State {
apiKey: string;
setApiKey: (k: string) => void;
}
export const useStore = create<State>()(
chromeStorageSync('opts', (set) => ({
apiKey: '',
setApiKey: (k) => set({ apiKey: k }),
}))
);
chromeStorageSync 中间件:每次 set 自动写回 chrome.storage.sync,三页面实时同步。
三、Popup:小而快,首屏 < 150 ms
技巧 1 延迟加载 React
Plasmo 默认使用 react-18-ssr 入口,如果 Popup 仅展示按钮,可改用 Preact 轻量版本:
JSON
复制
// package.json
"alias": {
"react": "preact/compat",
"react-dom": "preact/compat"
}
体积从 45 KB → 18 KB(gzip)。
技巧 2 骨架屏
tsx
复制
// src/popup/index.tsx
import 'style/tailwind.css';
export default function Popup() {
return (
<div className="w-80 p-4">
<h1 className="text-lg font-bold">Hello MV3</h1>
<button
className="btn-primary"
onClick={async () => {
const [tab] = await chrome.tabs.query({ active: true });
chrome.tabs.sendMessage(tab.id!, { type: 'crawl' });
}}>
一键抓取标题
</button>
</div>
);
}
首次点击图标才注入 React,空窗期用原生 DOM 展示 Loading,体验秒开。
四、Options:复杂表单 + 校验
路由方案
无需 React Router,直接用 Plasmo 的「文件即路由」:
复制
src/options/
├── index.tsx # /options.html 默认
├── tabs/
│ ├── General.tsx
│ ├── Advanced.tsx
│ └── About.tsx
<a href="#advanced"> 即可切换,hash 变化由 useEffect 监听,打包体积 -8 KB。
表单持久化
tsx
复制
// src/options/General.tsx
import { useStore } from '~store';
export default function General() {
const { apiKey, setApiKey } = useStore();
return (
<label>
API Key
<input
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
className="input"
/>
</label>
);
}
输入即同步,刷新选项页不丢。
五、Devtools:把调试面板塞进侧边栏
-
声明
JSON
复制
"devtools_page": "devtools/index.html"
-
创建侧边栏
TypeScript
复制
// src/devtools/index.ts
chrome.devtools.panels.create(
'MV3 Debug', // 标题
'icons/16.png', // 图标
'devtools/panel.html' // 实际页面
);
-
与 Background 长连
TypeScript
复制
// src/devtools/panel.tsx
const port = chrome.runtime.connect({ name: 'devtools' });
port.postMessage({ tabId: chrome.devtools.inspectedWindow.tabId });
Background 侧:
TypeScript
复制
chrome.runtime.onConnect.addListener((port) => {
if (port.name !== 'devtools') return;
port.onMessage.addListener((msg) => {
// 拿到 tabId,可注入脚本、抓网络
});
});
场景示例
-
一键高亮未加载图片
-
把页面所有 fetch 请求导出 HAR
-
与第③篇通信结合,实时展示 injected 返回的数据
六、体积优化清单(实测数据)
表格
复制
| 优化项 | 原始体积 | 优化后 | 手段 |
|---|---|---|---|
| React → Preact | 45 KB | 18 KB | alias |
| 拆 Code Splitting | 0 KB | -12 KB | 动态 import |
| CSS 按需 | 35 KB | 8 KB | tailwindcss JIT |
| Minify + Gzip | 110 KB | 74 KB | terser + gzip |
最终三页面总 bundle < 80 KB,本地秒开。
七、一键 build & 载入
bash
复制
pnpm build
生成
复制
build/
├── chrome-mv3-prod/
│ ├── popup.html
│ ├── options.html
│ ├── devtools.html
│ └── ...
chrome://extensions → 加载已解压 → 选 chrome-mv3-prod
F12 → 最右侧出现「MV3 Debug」面板,Popup 点图标秒开,选项页在「扩展详情→扩展选项」打开。
八、本篇作业(下篇前置)
-
把今天的模板合并到主分支,确保 Popup 能一键发送消息给当前标签页的 Content Script(复用第③篇 crawlAndReturn)。
-
Options 页新增「暗黑模式」开关,并实时同步到 Popup & Devtools(提示:useStore + chrome.storage.onChanged)。
-
Devtools 面板增加一个按钮:点击后把当前页面所有 img 未加载链接列表打印到控制台,截图 GitHub 讨论贴。
下篇预告
《Chrome 插件开发入门⑤:Background Service Worker 生命周期与事件总线》
-
SW 5 分钟被杀,如何用 alarms 保活
-
全局事件总线:chrome.runtime.onMessage + emittery
-
实战:离线缓存 + 定时同步 GitHub Star
把作业 push 并打 Tag v0.4,下篇直接在此基础上新建分支。
更多推荐
所有评论(0)