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

推荐订阅源

WordPress大学
WordPress大学
T
The Blog of Author Tim Ferriss
F
Fortinet All Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
阮一峰的网络日志
阮一峰的网络日志
The GitHub Blog
The GitHub Blog
Y
Y Combinator Blog
MyScale Blog
MyScale Blog
雷峰网
雷峰网
博客园 - 叶小钗
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 三生石上(FineUI控件)
云风的 BLOG
云风的 BLOG
V
V2EX
宝玉的分享
宝玉的分享
酷 壳 – CoolShell
酷 壳 – CoolShell
N
Netflix TechBlog - Medium
Vercel News
Vercel News
美团技术团队
人人都是产品经理
人人都是产品经理
The Cloudflare Blog

rxliuli blog

App Store Connect 发布事故回顾 Safari 扩展 Popup 中的滚动迟缓问题 Safari 云签名在 GitHub Actions 中的两个陷阱 使用 GitHub Actions 全自动发布 Safari 扩展 旅行 - 东福山岛 旅行 - 新加坡 - 越南 旅行 - 马来西亚 - 下 旅行 - 马来西亚 - 上 Browser Extension Dev - 08. 发布 Chrome Web Store Browser Extension Dev - 07. Popup UI Browser Extension Dev - 06. 按需注入脚本 Browser Extension Dev - 05. 存储和配置 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 Extra - User Script 介绍
rxliuli · 2026-01-15 · via rxliuli blog

前言

在此之前的基础教程系列中,为了演示创建了多个 Chrome 扩展程序,它们通常都比较简单。所以在本章,我将说明扩展之外另一种允许用户修改网页的标准:User Script,它的基本思路非常简单,向用户浏览的网站注入自定义的一个脚本片段。但与扩展不同,使用 User Script 通常需要安装一个脚本管理器,例如

通过这类脚本管理器,可以从网站上直接安装其他人创建的 User Script,也可以自己创建需要的脚本。

User Script 结构

首先是结构,User Script 虽然只是一个 JavaScript 文本,但同样分为 Manifest 和 Code 两部分,前者使用注释完成,后者则通常包含在闭包函数中。下面这一个非常简单的示例脚本(由 Tampermonkey 创建)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ==UserScript==
// @name New Userscript
// @namespace http://tampermonkey.net/
// @version 2026-04-09
// @description try to take over the world!
// @author You
// @match https://*/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
// ==/UserScript==

;(function () {
'use strict'

// Your code here...
})()

可以看到,开头的注释以 // ==UserScript== 开始和 // ==/UserScript== 结尾,中间每一行的注释都以 @ 开头代表脚本的元数据,例如 @name/@version/@match/@grant 之类的,最接近官方文档的地方可能是 https://www.tampermonkey.net/documentation.php。你可能注意到 icon 是一个 datauri 图标。是的,由于只有一个 JavaScript 脚本,所以无法放置 icon 文件之类的。

而下面的代码使用了一个看上去有点奇怪的写法,这是在 ES6 之前为了避免全局变量污染而遗留下来的历史包袱。例如如果在 devtools console 中输入两次下面这个代码,将会出现不同的打印结果

1
2
3
4
console.log(a) // undefined
var a = 1
console.log(a) // 1
var a = 1

如果使用闭包包裹,则两次执行的变量都是局部的,不会互相影响。

1
2
3
4
5
6
7
8
9
10
;(function () {
'use strict'
console.log(a) // undefined
var a = 1
})()
;(function () {
'use strict'
console.log(a) // undefined
var a = 1
})()

重写之前的扩展

对于之前 Browser Extension Dev - 01. 介绍基本概念 创建的那个隐藏 Google 搜索主页上 AI Mode 按钮的扩展,完全可以使用 User Script 重写它。

之前的 manifest.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"manifest_version": 3,
"name": "Hide AI Mode on Google Search",
"version": "0.0.1",
"description": "Hide the AI Mode button on Google Search pages.",
"content_scripts": [
{
"matches": ["https://www.google.com/"],
"js": ["content-scripts/content.js"],
"run_at": "document_start"
}
],
"icons": {
"128": "icon/128.png"
}
}

转换成 User Script 就变成了

1
2
3
4
5
6
7
8
9
10
11
// ==UserScript==
// @name Hide AI Mode on Google Search
// @namespace https://rxliuli.com/
// @version 0.0.1
// @description Hide the AI Mode button on Google Search pages.
// @author rxliuli
// @match https://www.google.com/
// @icon https://www.google.com/s2/favicons?sz=64&domain=google.com
// @run-at document-start
// @grant none
// ==/UserScript==

然后将之前的 content-scripts/content.js 追加到闭包函数中。

1
2
3
4
5
6
7
8
;(function () {
'use strict'

const style = document.createElement('style')
style.textContent =
'button:has([d="M15.65 11.58c.18-.5.27-1.03.31-1.58h-2c-.1 1.03-.51 1.93-1.27 2.69-.88.87-1.94 1.31-3.19 1.31C7.03 14 5 12.07 5 9.5 5 7.03 6.93 5 9.5 5c.46 0 .89.08 1.3.2l1.56-1.56C11.5 3.22 10.55 3 9.5 3 5.85 3 3 5.85 3 9.5S5.85 16 9.5 16c.56 0 2.26-.06 3.8-1.3l6.3 6.3 1.4-1.4-6.3-6.3c.4-.5.72-1.08.95-1.72z"]) { display: none; }'
document.documentElement.appendChild(style)
})()

在 Tampermonkey 中安装脚本

现在,我们可以在 Tampermonkey 脚本管理器中安装脚本了。

首先从 Chrome Web Store 中安装 Tampermonkey https://chromewebstore.google.com/detail/dhdgffkkebhmkfjojejmpbldmpobfkfo

1775697649183.jpg

安装之后还必须启用 Developer Mode 并且在 Tampermonkey 扩展的设置中允许 User Scripts。

1775697452427.jpg

然后打开 chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/options.html#nav=new-user-script+editor 创建一个新的脚本

1775697776386.jpg

最后,将上面的脚本粘贴进去,然后保存。

1775697819185.jpg

现在,可以看到 AI Mode 搜索按钮确实被隐藏起来了。

1775697832818.jpg

使用 GM API

上面我们完成了一个最简单的脚本,但完全没有涉及到 UserScript 提供的独特的 GM API。目前而言,Tampermonkey 包含了相当多的 API https://www.tampermonkey.net/documentation.php

1775694780786.jpg

最常见的可能是 GM_setValue/GM_getValue,它们是扩展向脚本提供的 KV 存储 API。这里使用 GM_registerMenuCommand 注册一个菜单项来演示如何在脚本内部启用和禁用。

首先仍然需要使用 // @grant 声明权限

1
2
3
4
5
6
7
8
9
10
11
12
13
// ==UserScript==
// @name Hide AI Mode on Google Search
// @namespace https://rxliuli.com/
// @version 0.0.1
// @description Hide the AI Mode button on Google Search pages.
// @author rxliuli
// @match https://www.google.com/
// @icon https://www.google.com/s2/favicons?sz=64&domain=google.com
// @run-at document-start
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// ==/UserScript==

然后根据设置动态注册菜单项,并且在启用时自动隐藏 AI Mode 按钮。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
;(function () {
'use strict'

let menuId
if (GM_getValue('visible')) {
menuId = GM_registerMenuCommand('Hide AI Mode', toggle)
} else {
menuId = GM_registerMenuCommand('Show AI Mode', toggle)
hide()
}

function toggle() {
if (GM_getValue('visible')) {
hide()
GM_registerMenuCommand('Show AI Mode', toggle, { id: menuId })
GM_setValue('visible', false)
} else {
show()
GM_registerMenuCommand('Hide AI Mode', toggle, { id: menuId })
GM_setValue('visible', true)
}
}

function hide() {
const style = document.createElement('style')
style.id = 'hide-ai-mode-on-google-search'
style.textContent =
'button:has([d="M15.65 11.58c.18-.5.27-1.03.31-1.58h-2c-.1 1.03-.51 1.93-1.27 2.69-.88.87-1.94 1.31-3.19 1.31C7.03 14 5 12.07 5 9.5 5 7.03 6.93 5 9.5 5c.46 0 .89.08 1.3.2l1.56-1.56C11.5 3.22 10.55 3 9.5 3 5.85 3 3 5.85 3 9.5S5.85 16 9.5 16c.56 0 2.26-.06 3.8-1.3l6.3 6.3 1.4-1.4-6.3-6.3c.4-.5.72-1.08.95-1.72z"]) { display: none; }'
document.documentElement.appendChild(style)
}
function show() {
document.getElementById('hide-ai-mode-on-google-search')?.remove()
}
})()

现在打开 Tampermonkey 的 Popup 的弹窗就可以看到注册的菜单项了。

1775697581036.jpg

总结

总体来说,如果你需要快速向网页注入一些脚本(这本质上就是用户脚本的定义),你可以优先使用 UserScript。如果你需要更强大的功能,比如操作标签页、运行后台定时任务,或者需要包含大型资源文件(例如 wasm,直接将 base64 嵌入内容脚本会很乱),那么你可以选择扩展。

UserScript 有几个显著的优势:

  1. 没有审核,这意味着你可以为 YouTube 添加视频下载功能,而谷歌禁止所有试图这样做的扩展程序,甚至本地下载工具 FDM 也收到了投诉。https://www.wilderssecurity.com/threads/free-download-manager-removes-support-for-youtube-downloads.441430/
  2. 更广泛的可用性,扩展在移动浏览器中难以使用,只有少数浏览器支持扩展,而支持用户脚本的浏览器数量要更多,例如 Firefox Android、Kiwi Browser、Safari + Userscripts app 等
  3. 没有数量限制,这可能是许多人不知道的,Chrome 网上应用店限制一个账户最多提交 20 个扩展程序,这是一个不常见的限制,但它确实存在。https://developer.chrome.com/docs/webstore/publish#:~:text=Note%3A%20You%20cannot%20have%20more%20than%2020%20extensions%20published%20on%20the%20Chrome%20Web%20Store
  4. 风险相对更低,扩展拥有的能力远超 UserScript,但这也意味着风险更高,例如加密货币领域因恶意扩展损失数百万美元的例子屡见不鲜。https://thehackernews.com/2025/12/trust-wallet-chrome-extension-bug.html

除了拥有更强大的 API 之外,扩展还有几个优势

  1. 可发现性,如果你希望被更多人看到,选择扩展可能是更好的选择。扩展有官方的 Chrome Web Store 或 App Store,而 User Script 没有什么“官方”商店,Userscript.Zone 是一个好的搜索工具,但并不负责审核。
  2. 可货币化,扩展尽管成功货币化的例子并不多(相比于 App/Web),但仍然存在。很难说服别人为一个脚本付费,尽管有点愚蠢,但脚本小子的刻板印象也存在
  3. UserScript 需求更加“下沉”和“长尾”,相对而言,User Script 的创建门槛足够低,并且缺乏审核或规范,所以你能找到各种形形色色的脚本和开发者,容易被灰黑产滥用
  4. 资源限制更高,例如通过嵌入 wasm/onnx 可以运行一些客户端 AI 模型,例如实现网页内容过滤或是图片识别等等,拓宽了 User Script 难以触及的可能性