☰
深入解析 chrome-extension-boilerplate-react-vite 的 Content Runtime Script:运行时按需注入脚本与 UI 的完整指南
2026/10/2 13:34:01 网站建设 项目流程
  • 前端
  • 示例工程

【免费下载链接】chrome-extension-boilerplate-react-vite

Chrome Extension Boilerplate with React + Vite + Typescript

项目地址:https://gitcode.com/GitHub_Trending/ch/chrome-extension-boilerplate-react-vite
点击查看免费下载

Content Runtime Script 是 chrome-extension-boilerplate-react-vite 模板中用于在运行时(runtime)向任意页面按需注入脚本与 UI的能力模块。它通过 pages/content-runtime/README.md 定义使用流程,配合chrome.scriptingAPI 在运行时动态执行,区别于 manifest 中静态声明的 content scripts。读完本文,你将掌握如何新增一个 runtime 注入脚本、理解其目录约定与构建产物命名规则、看懂底层 Shadow DOM 隔离机制,并能立即在自己的项目里落地一个带 React UI 的按需注入方案。

Content Runtime Script 是什么:与静态 Content Script 的对比

在 Chrome 扩展(Manifest V3)开发中,向页面注入代码有两种主流路径:

  1. 静态声明(manifest content_scripts):在 manifest 中预先声明matches与 JS/CSS 文件,浏览器在页面加载时自动注入。当前仓库的 chrome-extension/manifest.ts 中就声明了content/all.iife.js、content-ui/all.iife.js等静态注入项。
  2. 运行时注入(chrome.scripting.executeScript):在任意时刻通过chrome.scripting.executeScriptAPI 动态注入脚本,无需提前在 manifest 中声明。这正是 Content Runtime Script 模块的用途。

根据 pages/content-runtime/README.md 的定义,该模块“allows users to inject scripts (Console and UI) during runtime into all pages specified by you”,即:既可以注入纯逻辑脚本(Console),也可以注入带界面的 UI,并且注入目标页面的集合完全由你指定。

从源码结构看,pages/content-runtime/src/matches 下预置了all与example两个注入脚本入口,每个入口都包含 React 组件(App.tsx)、样式(index.css)与挂载逻辑(index.tsx),说明该模块默认面向React UI 的运行时注入场景。

快速上手:新增一个运行时注入脚本

第一步:复制matches/example并改造

原文档给出的第一步是:复制matches/example文件夹,以其他名称命名并编辑其内容。对应的源码目录为 pages/content-runtime/src/matches/example,包含三个文件:

  • App.tsx:React 根组件。默认实现仅打印日志并渲染一行文本:
import { useEffect } from 'react'; export default function App() { useEffect(() => { console.log('[CEB] Example runtime content view loaded'); }, []); return <div className="ceb-example-runtime-content-view-text">Example runtime content view</div>; }
  • index.css:组件样式,通过@import '@extension/ui/global.css'引入全局样式,并定义局部类:
@import '@extension/ui/global.css'; .ceb-example-runtime-content-view-text { font-size: 20px; }
  • index.tsx:注入入口,负责将组件渲染进页面的 Shadow DOM(详见下文“底层原理”小节):
import inlineCss from '../../../dist/example/index.css?inline'; import { initAppWithShadow } from '@extension/shared'; import App from '@src/matches/example/App'; initAppWithShadow({ id: 'CEB-extension-runtime-example', app: <App />, inlineCss });

例如,若要新增一个在 GitHub 页面上注入的“快速导航”脚本,只需将example目录复制为github-nav,然后:

  • 修改App.tsx中的组件内容与useEffect日志;
  • 修改index.css中的样式类名(建议保持ceb-前缀以避免与宿主页面冲突);
  • 修改index.tsx中initAppWithShadow的id(如CEB-extension-runtime-github-nav)与样式导入路径../../../dist/github-nav/index.css?inline。

第二步:通过chrome.scripting.executeScript触发注入

新目录就绪后,在你希望触发注入的任意位置(README 默认建议放在popup弹窗中)调用chrome.scripting.executeScript,注意注入文件的命名规则:

await chrome.scripting.executeScript({ ..., files: ['/content-runtime/{matches_folder_name}.iife.js'], });

其中{matches_folder_name}就是你新建的文件夹名。沿用上面的例子,应为:

await chrome.scripting.executeScript({ target: { tabId }, files: ['/content-runtime/github-nav.iife.js'], });

第三步:确保scripting权限

运行时注入依赖chrome.scriptingAPI。当前仓库的 chrome-extension/manifest.ts 已声明permissions: ['storage', 'scripting', 'tabs', 'notifications', 'sidePanel'],其中scripting正是运行时脚本注入所需权限;同时host_permissions: ['<all_urls>']允许脚本注入到所有匹配的 URL。如果你是从零新建扩展,请务必在 manifest 的permissions中加入"scripting",否则executeScript会因缺少权限而失败。

目录约定与构建产物命名:{folder}.iife.js从何而来

README 中files: ['/content-runtime/{matches_folder_name}.iife.js']的产物命名并非凭空约定,而是由仓库中的构建配置决定的。

从源码结构看,Content Runtime 模块的每个matches子目录被当作一个独立的 Rollup/Vite 入口。其入口收集逻辑位于 packages/vite-config/lib/get-content-script-entires.ts:

  • 该函数扫描matchesDir(即matches目录)下的每个子文件夹;
  • 若某文件夹是目录,则检查其中是否存在index.ts或index.tsx;
  • 若两者都不存在,则抛出异常:{folder} in \matches` doesn't have index.ts or index.tsx file`;
  • 否则以文件夹名作为入口 key,指向其中的index.ts或index.tsx文件。

因此,新建的注入脚本文件夹必须包含index.ts或index.tsx作为入口文件(example与all使用的都是index.tsx)。构建时,Vite 会将每个入口打包成<folder>.iife.js格式的 IIFE(立即执行函数)产物,再经由content-runtime/前缀部署到扩展根目录,最终与 README 中的'/content-runtime/{matches_folder_name}.iife.js'路径一一对应。.iife.js后缀意味着产物是自包含的、以立即执行形式运行的脚本,这正是executeScript直接加载文件所需的形式。

底层原理:initAppWithShadow与 Shadow DOM 隔离

example/index.tsx与all/index.tsx都调用了来自@extension/shared的initAppWithShadow。其实现位于 packages/shared/lib/utils/init-app-with-shadow.ts,核心流程如下:

  1. 创建一个div元素(root.id = id)并追加到document.body;
  2. 在该div上通过attachShadow({ mode: 'open' })建立 Shadow DOM;
  3. 将内联 CSS 注入 Shadow DOM:
    • Firefox 环境:由于 Mozilla 已知 Bug #1770592 不支持adoptedStyleSheets,改用<style>元素注入样式(实现中注明这可能导致与宿主页面样式冲突);
    • 其他浏览器:使用CSSStyleSheet+adoptedStyleSheets注入,实现彻底的样式隔离;
  4. 最后通过createRoot(rootIntoShadow).render(app)将 React 组件渲染进 Shadow DOM。

样式文件通过import inlineCss from '../../../dist/example/index.css?inline'以内联字符串形式引入(Vite 的?inline查询参数),从而避免运行时再次发起网络请求、也无需在web_accessible_resources中额外注册 CSS 资源。该机制使注入的 UI不会受宿主页面样式污染,也不会反向污染宿主页面,这是运行时注入 UI 相比直接document.body.appendChild的关键优势。

两个预置示例的对照参考

仓库提供了两个开箱即用的参考实现:

  • pages/content-runtime/src/matches/example:面向特定站点(如https://example.com/*,对应 manifest 中静态 content scripts 的匹配范围)的示例,Shadow DOM 根节点 id 为CEB-extension-runtime-example;
  • pages/content-runtime/src/matches/all:面向所有页面的示例,根节点 id 为CEB-extension-runtime-all,App.tsx中日志为[CEB] All runtime content view loaded。

对照两者可以发现,除目录名、id、样式类名与组件文案不同外,其index.tsx的挂载模式完全一致,这正是“复制文件夹即可新增脚本”这一设计的体现。由于matches目录中的每个文件夹都会由 get-content-script-entires.ts 自动收集为独立入口,新增脚本不需要修改任何配置文件——复制、改名、编辑三步即可。

与静态 Content Scripts 的协同与选型建议

回到 chrome-extension/manifest.ts,可以看到静态声明的 content scripts 会根据matches自动注入。运行时注入与静态注入的协同场景大致如下:

  • 页面加载时就必须存在的注入,优先使用 manifest 静态声明(如content/all.iife.js),浏览器保证注入时机与顺序;
  • 需要按需触发(点击弹窗按钮、收到消息、用户操作后才注入)、或希望精确控制注入目标的 UI,使用 Content Runtime Script +chrome.scripting.executeScript,它不受 manifest 匹配规则的束缚,完全由业务代码决定注入时机与目标标签页。

从 README 的默认用法看,popup(弹窗)是最典型的触发入口:用户点击扩展图标后,根据当前标签页 URL 决定是否注入、注入哪个脚本,这与模板中 pages/popup 模块的定位相符。在实际项目中,你完全可以将触发逻辑迁移到 background service worker(chrome-extension/src/background/index.ts)中,结合消息传递实现更复杂的注入决策。

总结

Content Runtime Script 模块以极低的接入成本提供了“运行时按需注入 React UI”的能力:一个文件夹即一个注入入口,构建自动产出content-runtime/{folder}.iife.js,配合chrome.scripting.executeScript即可在任意目标页面动态挂载 UI;底层initAppWithShadow的 Shadow DOM 方案则保证了注入内容与宿主页面之间的样式隔离。结合本仓库的 pages/content-runtime/README.md、pages/content-runtime/src/matches/example/index.tsx 与 packages/shared/lib/utils/init-app-with-shadow.ts,你可以快速搭建属于自己的运行时注入方案。

关键参考路径:

  • Content Runtime 使用文档
  • 预置示例入口(example)
  • 预置示例入口(all)
  • 入口自动收集逻辑
  • Shadow DOM 注入实现
  • 扩展 manifest 权限与静态注入声明
  • 前端
  • 示例工程

【免费下载链接】chrome-extension-boilerplate-react-vite

Chrome Extension Boilerplate with React + Vite + Typescript

项目地址:https://gitcode.com/GitHub_Trending/ch/chrome-extension-boilerplate-react-vite
点击查看免费下载
上一篇:OpenVINO属性配置指南:设备参数与性能调优详解
下一篇:Go语言设计模式持续集成:从零构建可靠的测试验证体系

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询