迁移到 Plasmo Framework
Plasmo 浏览器扩展框架通过其声明式开发方法,极大地简化了浏览器扩展的构建过程。它消除了繁琐的样板代码,提供了基于文件的配置系统,让开发变得直观、高效,并且支持灵活退出。
在本指南中,我们将引导您完成从现有扩展项目到 Plasmo 的完整迁移过程,并重点介绍需要关注的关键变更点。
安装 Plasmo CLI
Plasmo 框架的核心驱动是 Plasmo CLI。这是一个专为浏览器扩展开发定制的 Node.js 包,集成了编译器、打包器、开发服务器和打包工具:
pnpm install plasmo启动开发服务器:plasmo dev
构建扩展:plasmo build
打包扩展:plasmo package
manifest.json 配置
Plasmo 将 manifest.json 的功能整合到 package.json 中,并对核心属性进行了抽象化处理:
| Manifest 字段 | Plasmo 抽象方式 |
|---|---|
icons | 自动从 assets 目录中的 icon.png 生成 |
action, browser_actions | 通过 popup.tsx 文件配置 |
options_ui | 通过 options.tsx 文件配置 |
content_scripts | 通过 contents/*.{ts,tsx}, content.ts, content.tsx 配置 |
background | 通过 background.ts 文件配置 |
sandbox | 通过 sandbox.tsx 和 沙盒页面 配置 |
manifest_version | 通过 --target 构建标志设置,默认为 3 |
version | 使用 package.json 中的 version 字段设置 |
name | 使用 package.json 中的 displayName 字段设置 |
description | 使用 package.json 中的 description 字段设置 |
author | 使用 package.json 中的 author 字段设置 |
homepage_url | 使用 package.json 中的 homepage 字段设置 |
Plasmo 集中管理 package.json 和 manifest.json 之间的通用元数据,并自动解析所有静态文件引用(如操作、后台、内容脚本等)。
这使您能够专注于重要的元数据配置,例如名称、描述、OAuth、declarative_net_request 等。
此外,这种设计让框架能够提供更强大的功能,包括:
弹出窗口、选项页和新标签页
Plasmo 消除了手动挂载 React/Svelte/Vue 组件的需求。
在传统的非 Plasmo 扩展项目中,挂载 React 组件需要创建 HTML 模板和相关的 JavaScript 代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Popup</title>
</head>
<body>
<div id="root"></div>
<script src="popup.js"></script>
</body>
</html>import { createRoot } from "react"
import Popup from "./core/popup"
const root = document.getElementById("root")
createRoot(root).render(<Popup />)如果需要添加 TypeScript、LESS 或 SCSS 支持,还需要配置 Webpack、Parcel 或 ESBuild,以及额外的插件和加载器。
使用 Plasmo 框架,整个过程变得极其简单。只需在源代码目录中添加 popup.tsx 或 options.tsx 文件,并导出一个默认的 React 组件:
import Popup from "./core/popup"
export default PopupPlasmo 会自动处理剩余的所有工作。
框架内置支持 CSS、SCSS、LESS、CSS 模块和 PostCSS 插件。例如,要使用 SCSS,只需在 React 组件中导入 SCSS 文件:
import Popup from "./core/popup"
import "./popup.scss"
export default Popup您可以使用相同的方式为选项页、新标签页、沙盒页面和任何其他自定义页面挂载 React 组件。
自定义页面
在传统扩展项目中,创建自定义页面需要额外的 HTML 模板和 JavaScript 代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Custom Page</title>
</head>
<body>
<div id="root"></div>
<script src="./custom.js"></script>
</body>
</html>import { createRoot } from "react"
import CustomPage from "./core/custom-page"
const root = document.getElementById("root")
createRoot(CustomPage).render(<PopupApp />)使用 Plasmo,您可以通过内置的标签页功能轻松创建自定义页面。在源代码目录中添加 tabs 文件夹,并在其中使用 .tsx 文件创建自定义页面:
import Hello from "./core/hello"
export default Hello该页面将在 chrome-extension://<extension-id>/tabs/custom.html 地址下可用。
内容脚本
在传统扩展项目中,您需要在 manifest.json 文件中指定内容脚本,并用 JavaScript 编写它们:
{
"content_scripts": [
{
"matches": ["https://*/*", "http://*/*"],
"all_frames": true,
"css": ["content.css"],
"run_at": "document_start"
}
]
}console.log("Hello from content script!")使用 Plasmo,您可以在名为 content.ts 的文件中指定内容脚本及其配置,或者在 contents 目录中组织多个脚本。
content.ts 文件的内容如下:
import type { PlasmoCSConfig } from "plasmo"
export const config: PlasmoCSConfig = {
matches: ["https://*/*", "http://*/*"],
all_frames: true,
css: ["~content.css"]
}
console.log("Hello from content script!")不再需要在 manifest.json 和 content.js 文件之间来回切换。额外的优势是配置是完全类型化的,可以从您的 IDE 获得有价值的自动完成功能。
要添加多个内容脚本,请创建名为 contents 的目录并重复上述步骤。您可以在此目录中自由命名脚本文件,它们都将作为独立的内容脚本添加,每个脚本都可以有自己的配置。
例如,假设您想添加一个将页面背景颜色改为绿色的内容脚本。您可以在 contents 目录中创建名为 emerald-splash.ts 的文件:
document.body.style.backgroundColor = "green"由于内容脚本文件没有导出配置,Plasmo 将使用默认配置:
{
"matches": ["<all_urls>"]
}内容脚本 UI
如果您需要将 React 组件注入到网页中,传统扩展项目通常包含大量样板代码,涉及创建 Shadow DOM、查找正确的挂载元素、使用 MutationObservers 等。
Plasmo 抽象了所有这些复杂性,让您可以专注于构建组件本身。
例如,要将一个简单的按钮 React 组件注入到网页中,只需在 contents 目录中创建名为 press-me.tsx 的文件:
export default function PressMeCSUI() {
return <button>Press me</button>
}Plasmo 会自动将此组件注入到页面中,并将其挂载到页面的根元素。
如果需要将组件注入到特定元素中,可以通过从文件中导出 getInlineAnchor 函数来实现:
import type { PlasmoGetInlineAnchor } from "plasmo"
export const getInlineAnchor: PlasmoGetInlineAnchor = async () =>
document.querySelector("#pricing")通过返回一个 Element,Plasmo 会将组件挂载到该元素旁边。您可以完全自定义挂载行为、组件的渲染方式等。
此功能称为内容脚本 UI。要深入了解其 API 和工作原理,请查看关于其生命周期的技术文档。
您还可以指定组件相对于目标元素的插入位置:
import type { PlasmoGetInlineAnchor } from "plasmo"
export const getInlineAnchor: PlasmoGetInlineAnchor = async () => ({
element: document.querySelector("#pricing"),
insertPosition: "afterend"
})后台服务工作者
在传统项目中,创建后台服务工作者需要在 manifest.json 文件中指定 background 属性,并用 JavaScript 编写服务工作者代码:
{
"background": {
"service_worker": "background.js"
}
}console.log("Hello from BGSW!")在 Plasmo 中,您通过创建 background.ts 文件来指定后台服务工作者:
console.log("Hello from BGSW!")您可以将任何针对标准服务工作者运行时的模块导入到 background.ts 中。例如,添加 bip39 模块:
import { generateMnemonic } from "bip39"
console.log(
"把握当下,让此刻成为最珍贵的时光。此刻永不再来。"
)
console.log(generateMnemonic())Plasmo 会自动打包 background.ts 文件,并将其作为后台服务工作者添加到 manifest.json 中。
环境变量
如果您当前正在使用环境变量,可以在 Plasmo 中继续使用相同的模式。
Plasmo 框架开箱即用地支持环境变量,无需额外配置。
退出策略
Plasmo 框架的设计理念是移除最常见的配置和样板代码,让开发人员能够在更高的抽象层工作 - 专注于他们选择的 UI 库/框架,如 React、Vue 和 Svelte。这是创建更强大、更美观扩展的有效途径。
退出 Plasmo 就像从 package.json 文件中删除 plasmo 依赖项并将您的组件迁移到自定义设置中一样简单。这是因为 Plasmo 生成的所有胶水代码都在框架编译器和打包器层注入,使您的功能代码极具可移植性。
您也可以根据需要灵活使用 Plasmo。所有 chrome API 都可用,您可以直接在功能代码中使用它们。例如,您可以直接使用 chrome.runtime.messaging API,而不是使用消息传递 API。内容脚本和扩展页面也是如此。