Skip to Content
LaunchExt | Chrome 扩展开发平台 (Next.js + Plasmo) 🚀 Read more → 
博客Plasmo Content Script 独立配置机制详解

Plasmo Content Script 独立配置机制详解

最近在开发 Chrome 扩展时发现,Plasmo 框架有个特别实用的特性:contents 目录下的每个文件都可以配置自己独立的 PlasmoCSConfig,而且它们之间完全不会互相干扰。

为什么需要独立配置?

在实际项目中,我们经常需要为不同的页面或功能编写专门的内容脚本。比如:

  • 有些脚本只需要在特定域名下运行
  • 有些需要在页面加载的不同时机执行
  • 有些需要注入 UI 组件,有些只需要处理数据

如果所有脚本都共用一套配置,很容易出现配置冲突。Plasmo 的设计就很好地解决了这个问题。

独立配置的实际应用

不同页面的专属脚本

// contents/vulcan.ts - 只在特定域名运行 export const config: PlasmoCSConfig = { matches: ["https://vulcan.plasmo.com/*", "https://www.nowarpls.org/*"] } // contents/nested/index.tsx - 在另一个域名运行 export const config: PlasmoCSConfig = { matches: ["https://itero.plasmo.com/*"] }

不同执行时机的脚本

// contents/content-isolated.ts - 页面空闲时运行 export const config: PlasmoCSConfig = { matches: ["https://www.plasmo.com/*"], run_at: "document_idle" } // contents/relay.ts - 文档开始时立即运行 export const config: PlasmoCSConfig = { matches: ["https://www.plasmo.com/*"], run_at: "document_start" }

特殊执行环境的脚本

// contents/plasmo-main.ts - 在主世界运行 export const config: PlasmoCSConfig = { matches: ["https://www.plasmo.com/*"], world: "MAIN", run_at: "document_start" }

UI 组件注入脚本

// contents/plasmo-overlay.tsx - 带样式文件的 Overlay 组件 export const config: PlasmoCSConfig = { matches: ["https://www.plasmo.com/*"], css: ["font.css"] } // contents/plasmo-inline.tsx - 简单的 Inline 组件 export const config: PlasmoCSConfig = { matches: ["https://www.plasmo.com/*"] }

技术实现原理

PlasmoCSConfig 类型定义

PlasmoCSConfig 实际上是对 ManifestContentScript 类型的封装,但排除了 js 字段,因为 JavaScript 文件路径由 Plasmo 自动处理:

export type PlasmoCSConfig = Omit<Partial<ManifestContentScript>, "js">

配置隔离机制

Plasmo 通过 manifest 工厂系统实现配置隔离。每个 content script 都通过 toggleContentScript 方法独立处理:

  1. 提取配置元数据const metadata = await extractContentScriptConfig(path)
  2. 应用独立配置:每个脚本的配置被单独处理
  3. 存储到 Mapthis.contentScriptMap.set(path, contentScript)

关键点在于配置通过文件路径作为键存储在 Map 中,确保了每个脚本配置的完全独立。

最终合并过程

在生成 manifest 时,所有独立的配置会被合并到 content_scripts 数组中:

base.content_scripts = [ ...Array.from(this.contentScriptMap.values()).filter( (s) => s.world !== "MAIN" ), ...(overrideContentScripts! || []) ]

实践建议

根据我的使用经验,这种独立配置设计有几个明显的好处:

1. 代码组织更清晰

每个功能模块都有自己的配置,查找和修改都很方便。

2. 避免配置冲突

不同脚本可以有不同的 matchesrun_at 等配置,互不影响。

3. 便于团队协作

多人开发时,各自负责的脚本可以独立配置,减少合并冲突。

4. 灵活的部署策略

可以根据需要启用或禁用特定的内容脚本。

常见问题

Q: 如果多个脚本配置了相同的 matches 会怎样?

A: 完全没问题!每个脚本都会在匹配的页面上独立运行,互不干扰。

Q: 配置中的 css 字段如何处理?

A: Plasmo 会自动解析相对路径,将 CSS 文件正确关联到对应的内容脚本。

Q: 主世界脚本有什么特殊之处?

A: 主世界脚本(world: "MAIN")目前需要特殊处理,在最终合并时会暂时过滤掉,等待 Chrome 原生支持。

总结

Plasmo 的这种模块化设计真的很实用,让内容脚本的管理变得简单明了。每个脚本都可以专注于自己的功能,配置清晰独立,大大提高了开发效率和代码可维护性。

如果你也在用 Plasmo 开发 Chrome 扩展,强烈建议充分利用这个特性来组织你的内容脚本。有什么问题欢迎交流讨论!

最后更新于