惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Google DeepMind News
Google DeepMind News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
宝玉的分享
宝玉的分享
量子位
博客园 - 叶小钗
博客园_首页
Know Your Adversary
Know Your Adversary
S
Schneier on Security
罗磊的独立博客
C
Cyber Attacks, Cyber Crime and Cyber Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Simon Willison's Weblog
Simon Willison's Weblog
美团技术团队
WordPress大学
WordPress大学
大猫的无限游戏
大猫的无限游戏
Hacker News: Ask HN
Hacker News: Ask HN
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Security Latest
Security Latest
月光博客
月光博客
Spread Privacy
Spread Privacy
C
Cybersecurity and Infrastructure Security Agency CISA
人人都是产品经理
人人都是产品经理
J
Java Code Geeks
C
CERT Recently Published Vulnerability Notes
Last Week in AI
Last Week in AI
Attack and Defense Labs
Attack and Defense Labs
NISL@THU
NISL@THU
H
Hacker News: Front Page
N
News and Events Feed by Topic
小众软件
小众软件
T
Threatpost
V2EX - 技术
V2EX - 技术
T
Tailwind CSS Blog
阮一峰的网络日志
阮一峰的网络日志
Project Zero
Project Zero
L
LINUX DO - 热门话题
Apple Machine Learning Research
Apple Machine Learning Research
C
CXSECURITY Database RSS Feed - CXSecurity.com
TaoSecurity Blog
TaoSecurity Blog
P
Privacy International News Feed
Latest news
Latest news
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
酷 壳 – CoolShell
酷 壳 – CoolShell
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
AWS News Blog
AWS News Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 【当耐特】
Hugging Face - Blog
Hugging Face - Blog

rxliuli blog

旅行 - 东福山岛 旅行 - 新加坡 - 越南 旅行 - 马来西亚 - 下 旅行 - 马来西亚 - 上 Browser Extension Dev - 08. 发布 Chrome Web Store Browser Extension Dev - 07. Popup UI Browser Extension Dev - 05. 存储和配置 Browser Extension Dev Extra - User Script 介绍 Browser Extension Dev - 04. Background Script Browser Extension Dev Extra - 如何找到锁定滚动的元素 Browser Extension Dev - 03. 注入 UI Browser Extension Dev - 02. 使用 WXT Browser Extension Dev - 01. 介绍基本概念 Chrome => Firefox 扩展移植的那些坑 发布 Safari 扩展到 iOS 应用商店 再游新疆 -- 自驾 实践: 使用 Hono 开发全栈应用 Web 流式写入文件 在构建时而非运行时编译 Markdown 在 Web 中解压大型 ZIP 并保持目录结构
Browser Extension Dev - 06. 按需注入脚本
rxliuli · 2026-01-21 · via rxliuli blog

前言

在上一章 Browser Extension Dev - 05. Storage and Configuration 中,我们介绍了为扩展添加设置页面并使用 Storage API 保存和读取配置的功能。在这一章,我们将介绍按需注入脚本。这种方式完全不会拖慢网站运行速度,同时在 Chrome Web Store 安装时不会有任何安全警告提示。接下来我们将实现一个扩展:点击图标即可将网页主要内容复制为 Markdown。

思考

我们之前已经接触过 Content Script 注入和 Background Script 监听扩展图标点击。虽然尚未介绍,但两者可以进行消息通信。

有了上面的背景知识,你可能会想要这样做:

  1. 为所有网页注入 Content Script 并监听后台消息
  2. 点击扩展图标时在 Background Script 通知 Content Script
  3. 在 Content Script 中执行具体逻辑

这种方法有几个主要缺点:

  1. 默认为所有网页注入 Content Script 不仅会拖慢网站速度,还可能导致风险,因为注入过程对用户完全无感知,后续扩展更新可能引入漏洞
  2. 权限要求极高,安装扩展时会提示安全警告,说明这个扩展要求读取和修改所有用户访问的网站

1768995880017.jpg

而对于需要明确动作触发的场景,其实有更简单的实现方式:

  1. 点击扩展图标时在 Background Script 中向当前网页注入一段脚本
  2. 在脚本中执行具体逻辑

这样,需要的权限就从 ['<all_urls>'] 变成了 ['activeTab', 'scripting'],虽然权限数量变多了,但风险反而更低,必须由用户触发才能执行代码,所以安装扩展时不会有任何警告。例如:

1768996707257.jpg

这里涉及到的关键 API 是 scripting.executeScript,顾名思义,用于执行自定义脚本。

参考 Chrome 官方 activeTab 指南:https://developer.chrome.com/docs/extensions/develop/concepts/activeTab

实现

在扩展图标点击时注入脚本

接下来在后台脚本中实现监听和注入部分。
首先还是更新 wxt.config.ts,添加所需权限以及空的 action 字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { defineConfig } from 'wxt'

export default defineConfig({
manifestVersion: 3,
manifest: {
name: 'Copy As Markdown',
description: 'Copy page content as Markdown',
permissions: ['activeTab', 'scripting'],
action: {},
},
webExt: {
disabled: true,
},
})

然后添加一个用于测试的注入脚本。与 Content Script 不同,这类脚本在 WXT 中需要使用 defineUnlistedScript 声明。

1
2
3
4
// entrypoints/inject.ts
export default defineUnlistedScript(() => {
alert('Injected script executed!')
})

然后在 background 中监听并注入它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { PublicPath } from 'wxt/browser'

export default defineBackground(() => {
browser.action.onClicked.addListener(async (tab) => {
if (tab.id) {
await browser.scripting.executeScript({
target: { tabId: tab.id },
// 这里的 /inject.js 是指构建之后的文件,如果你使用 pnpm build,就可以在 .output/chrome-mv3 中看到 inject.js 了
// 注:一开始这里可能会报 ts 类型错误,pnpm dev/build 启动之后 wxt 会正确扫描 entrypoints 并生成类型定义
files: ['/inject.js'] as PublicPath[],
})
}
})
})

除了 files 参数,还可以通过 func/args 直接传递函数和参数,适用于简单场景。参考:https://developer.chrome.com/docs/extensions/reference/api/scripting#type-ScriptInjection

当我们打开 google.com 然后点击扩展图标时,却发现没有反应。查看扩展详情页面,可以看到一个错误。

1
Uncaught (in promise) Error: Could not load file: 'inject.js'.

和之前 Browser Extension Dev - 03. 注入 UI 时一样,需要在 manifest 中增加 web_accessible_resources 配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { defineConfig } from 'wxt'

export default defineConfig({
manifestVersion: 3,
manifest: {
name: 'Copy As Markdown',
description: 'Copy page content as Markdown',
permissions: ['activeTab', 'scripting'],
action: {},
web_accessible_resources: [
{
resources: ['inject.js'],
matches: ['<all_urls>'],
},
],
},
webExt: {
disabled: true,
},
})

然后再试一次,可以看到脚本确实被注入并正确执行了。

1768998702041.jpg

⚠️ 局限性:如果你希望注入的脚本能持久化(刷新或重新进入页面后仍自动运行),这是行不通的,仍然需要正确声明 host_permissions 权限来持久化注入 Content Script,即使使用 scripting API 仍然受到权限模型的限制。

在注入脚本中实现功能

接下来实现读取网页主要内容,转换为 Markdown,然后复制到剪贴板的功能。借助 npm 生态,实现起来非常简单。

首先安装需要的依赖

1
2
pnpm i @mozilla/readability turndown
pnpm i -D @types/turndown

然后编写少量胶水代码即可完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Readability } from '@mozilla/readability'
import TurndownService from 'turndown'

export default defineUnlistedScript(async () => {
const service = new TurndownService({
headingStyle: 'atx',
codeBlockStyle: 'fenced',
})
const reader = new Readability(document.cloneNode(true) as Document) // 深度复制避免影响到原网页
const article = reader.parse() // 解析主要内容
if (article && article.title && article.content) {
const markdown = service.turndown(article.content) // 转换 HTML 为 Markdown
await navigator.clipboard.writeText(`# ${article.title}\n\n${markdown}`) // 复制
alert('Article copied as Markdown!')
} else {
alert('Failed to parse the article.')
}
})

1768999375770.jpg
1769003419253.jpg

总结

在本章中,我们实现了一个按需注入脚本的扩展,它不会影响网页正常运行,只在用户触发时才执行代码,真正做到即插即用。在下一篇,我们将继续完善这个扩展,使用 Popup 弹窗直接预览和编辑从当前页面复制的 Markdown。

如果有任何问题,欢迎加入 Discord 群组讨论。
https://discord.gg/VxbAqE7gj2

完整代码:https://github.com/rxliuli/browser-extension-dev-examples/tree/main/packages/06-inject-script-on-demand