内容脚本开发指南
内容脚本在网页的上下文中运行于一个隔离的执行环境中。这种设计使得来自不同扩展的多个内容脚本可以共存而不会相互冲突,同时与网页原生的 JavaScript 环境保持完全隔离。
文件类型区分
.ts文件:纯 TypeScript 脚本,不会捆绑前端运行时(React/Vue/Svelte),也不被视为 UI 组件.tsx、.vue、.svelte文件:会被视为 UI 组件并进行相应的构建处理
主要应用场景
- 数据采集:从当前网页中提取和收集数据
- DOM 操作:选择、查找和样式化网页元素
- UI 注入:将自定义 UI 元素注入到网页中
- 页面原生环境交互:将代码注入到页面原生执行环境
创建单个内容脚本
模块化要求: 由于 Plasmo 默认将所有源文件视为 ES 模块,如果文件中没有导入或导出语句,必须在文件开头添加 export {} 来明确标识为模块。
创建一个 content.ts 文件,导出一个空对象,然后开始编写您的逻辑:
export {}
console.log(
"实践是检验真理的唯一标准。理论再好,也需要实际验证。"
)重新加载扩展后,打开任意网页并查看开发者工具的控制台:

完整的功能示例请参考 with-content-script 。
创建多个内容脚本
如需创建多个内容脚本,请在项目根目录下建立 contents 文件夹,并将各个内容脚本文件放置其中。建议使用描述性的文件名来清晰表达每个脚本的功能:
contents/
├── data-extractor.ts # 数据提取脚本
├── dom-manipulator.ts # DOM 操作脚本
└── ui-injector.tsx # UI 注入脚本具体实现示例请参考 with-many-content-scripts 。
配置内容脚本行为
通过导出一个配置对象,可以精确控制内容脚本的执行范围和方式:
import type { PlasmoCSConfig } from "plasmo"
export const config: PlasmoCSConfig = {
matches: ["https://*.example.com/*"], // 匹配的网址模式
all_frames: true, // 在所有iframe中运行
run_at: "document_end" // 执行时机
}得益于 TypeScript 的类型支持,配置过程具有完整的智能提示和类型检查功能。
有关所有可用配置选项的详细信息,请参阅 Chrome 官方内容脚本文档 。
注入到页面原生环境
如需修改网页的 window 对象或访问全局变量,必须将代码注入到页面原生的执行环境。
自动注入方式(Plasmo v0.65.0+)
从 Plasmo v0.65.0 开始,可以直接在配置中指定执行环境:
import type { PlasmoCSConfig } from "plasmo"
export const config: PlasmoCSConfig = {
matches: ["<all_urls>"],
world: "MAIN" // 指定在页面原生环境中执行
}手动注入方式
如需手动控制注入过程,首先需要在清单中申请 scripting 权限:
{
"manifest": {
"permissions": ["scripting"]
}
}然后在后台服务工作线程中执行注入:
// 获取当前活跃标签页
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true })
chrome.scripting.executeScript(
{
target: { tabId: tab.id },
world: "MAIN", // 在页面原生环境中执行
func: windowChanger // 要注入的函数
},
() => {
console.log("脚本注入完成")
}
)作用域限制: 通过 func 参数注入的函数具有严格封装的作用域,无法访问外部声明的变量或导入的模块。只能使用通过 args 属性传递的 JSON 可序列化值。
完整示例请参考 页面原生环境注入示例 。
处理外部 API 和 CORS 限制
由于内容脚本在网页上下文中运行,会受到同源策略的限制。要解决 CORS 问题,可以通过 Plasmo 消息传递 API 使用后台服务工作线程作为请求代理。
资源导入方式
使用 URL 方案导入
import resourceUrl from "url:./path/to/resource.js"url: 方案会自动将资源文件添加到构建包的 web_accessible_resources 声明中,并返回资源的完整 URL:
console.log(resourceUrl)
// 输出: chrome-extension://<扩展ID>/resource.hash.js?timestamp
// 提取文件名
const filename = resourceUrl.split("/").pop().split("?")[0]内联资源导入
对于较小的资源文件,可以使用内联方案直接嵌入到代码中:
data-base64::将文件转换为 base64 编码字符串data-text::将文本文件内容直接内联
有关波浪号 (~) 导入路径的详细说明,请参阅导入解析文档
最佳实践建议
性能优化: 尽量减少内容脚本的初始执行时间,避免阻塞页面加载。对于复杂的操作,可以考虑使用异步方式或延迟执行。
兼容性考虑: 确保内容脚本在不同浏览器和网页环境下都能正常工作,特别注意处理动态加载的内容和单页应用的场景。
调试技巧: 利用浏览器的开发者工具进行调试,可以使用 debugger 语句或设置断点来检查内容脚本的执行过程。