油猴脚本开发入门:用JavaScript给任意网页加『黑科技』功能(从写第一个脚本开始)
你是否曾浏览某个网站时,心里冒出这样的念头:“要是这里能自动填表就好了”、“这个按钮要是能一键操作就完美了”、“页面广告太多,真想一键净化”……对于大多数普通用户来说,这些想法往往止步于想象。但对于了解一点前端知识,或者愿意动手尝试的你来说,这些“黑科技”功能完全可以亲手实现。今天,我们就来聊聊如何通过编写用户脚本(UserScript),借助 Tampermonkey(俗称油猴)这个强大的工具,将你的浏览器变成一个高度定制化的“超级浏览器”。
简单来说,用户脚本就是一段运行在特定网页上的JavaScript代码。它像是一个隐形的助手,在网页加载时悄悄介入,按照你设定的规则去修改页面内容、添加功能或自动化操作。而Tampermonkey,就是管理、运行这些脚本的“指挥官”。它本身不提供具体功能,但为你搭建了一个安全、便捷的脚本运行环境。这意味着,你无需等待网站官方更新,也不必依赖功能固定的浏览器扩展,就能为自己打造独一无二的浏览体验。
无论你是刚接触JavaScript的前端新手,还是希望将想法快速落地的开发者,用户脚本开发都是一个绝佳的实践场。它门槛相对较低,能让你立刻看到代码带来的改变,获得强烈的正反馈。接下来,我们将从零开始,一步步构建你的第一个脚本,并深入探讨如何让它变得更强大、更智能。
1. 环境搭建与脚本管理器选择
在开始编写代码之前,我们需要一个“舞台”来运行我们的脚本。这个舞台就是用户脚本管理器。市面上主流的脚本管理器有好几款,它们各有特色,但核心功能相似:安装、管理、启用/禁用用户脚本。
1.1 主流脚本管理器对比
目前,最广为人知且功能最完善的当属 Tampermonkey(篡改猴),这也是本文主要使用的工具。它支持 Chrome、Edge、Firefox、Safari、Opera 等几乎所有主流浏览器,拥有超过千万的用户,生态成熟,文档齐全。除了Tampermonkey,还有 Violentmonkey(暴力猴) 和 Greasemonkey(油猴,Firefox专属) 等选择。
为了帮助你快速做出选择,这里有一个简单的功能对比表格:
| 特性 | Tampermonkey (篡改猴) | Violentmonkey (暴力猴) | Greasemonkey (油猴) |
|---|---|---|---|
| 主要支持浏览器 | Chrome, Edge, Firefox, Safari, Opera 等 | Chrome, Edge, Firefox, Opera 等 | 仅 Firefox |
| 开源情况 | 部分开源(核心开源) | 完全开源 | 完全开源 |
| 同步功能 | 支持通过浏览器同步、Google Drive、Dropbox等同步脚本 | 支持通过浏览器同步、Gist等同步脚本 | 支持通过浏览器同步 |
| 内置编辑器 | 功能强大,支持语法高亮、ESLint检查 | 基础编辑器 | 基础编辑器 |
| 高级API支持 | 非常全面,包括GM_*系列API、GM_info等 | 支持大部分常用API | 支持大部分常用API |
| 社区与生态 | 最庞大,脚本资源极其丰富 | 活跃,资源较多 | 历史悠久,但主要限于Firefox |
| 推荐人群 | 绝大多数用户和开发者,追求稳定和全面功能 | 偏好开源软件、轻量简洁的用户 | 坚定的Firefox用户 |
提示:对于初学者和大多数场景,强烈建议从Tampermonkey开始。其庞大的用户基数意味着你在遇到问题时更容易找到解决方案,丰富的功能也能满足你未来更复杂的开发需求。
1.2 安装Tampermonkey
安装过程非常简单,以Chrome浏览器为例:
- 打开Chrome网上应用店。
- 在搜索框中输入“Tampermonkey”。
- 找到由“Jan Biniok”开发的“Tampermonkey”扩展,点击“添加到Chrome”。
- 在弹出的确认对话框中点击“添加扩展程序”。
安装成功后,你会在浏览器工具栏的扩展程序区域看到一个猴头图标。点击它,如果看到类似“仪表盘”、“添加新脚本”的菜单,就说明安装成功了。
// 这是一个最简单的Tampermonkey脚本元数据块示例,我们稍后会详细解释
// ==UserScript==
// @name My First Script
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://www.example.com/*
// @grant none
// ==/UserScript==
对于无法访问Chrome商店的用户,也可以从Tampermonkey官网或一些可信的第三方扩展商店下载.crx文件进行离线安装。安装完成后,别忘了点击猴头图标,进入“仪表盘”熟悉一下界面。你会看到“已安装脚本”、“实用工具”、“设置”等选项卡,这里就是你未来管理所有“黑科技”的大本营。
2. 编写你的第一个脚本:从“Hello World”到实战
理论知识铺垫完毕,现在让我们动手创建第一个脚本。我们将从一个经典的“Hello World”开始,然后迅速升级到一个有实际用途的案例:为任意网页添加一个自定义按钮。
2.1 创建新脚本与理解元数据
在Tampermonkey面板中,点击“添加新脚本”。你会看到一个预置了模板的编辑器窗口。最上方被// ==UserScript==和// ==/UserScript==包裹的部分称为元数据块(Metadata Block)。它定义了脚本的基本信息和行为规则,是脚本的“身份证”和“说明书”。
让我们逐行解读一个基础元数据块:
// ==UserScript==
// @name 页面美化助手
// @namespace https://your-site.com
// @version 1.0.0
// @description 自动移除页面广告,并添加夜间模式切换按钮
// @author YourName
// @match https://*.zhihu.com/*
// @match https://www.bilibili.com/video/*
// @exclude https://www.bilibili.com/read/*
// @grant GM_addStyle
// @grant GM_notification
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @run-at document-end
// ==/UserScript==
@name: 脚本的名称,会显示在管理列表中。@namespace: 一个唯一的标识符,通常使用你的域名,用于避免与其他作者的脚本名称冲突。@version: 版本号,便于更新和管理。@description: 脚本功能的简要描述。@author: 作者信息。@match: 最重要的指令之一。它使用URL模式来指定脚本在哪些网页上运行。例如https://*.zhihu.com/*表示在所有知乎子域名下的所有页面运行。你可以添加多个@match规则。@exclude: 排除规则,即使在@match范围内,符合@exclude规则的URL也不会运行脚本。@grant: 声明脚本需要使用的特殊Tampermonkey API(GM_* API)。例如GM_addStyle用于添加CSS样式,GM_notification用于发送桌面通知。如果不需要特殊API,可以写@grant none。@require: 在脚本运行前,预先加载外部JavaScript库,如jQuery、Lodash等。@run-at: 指定脚本注入的时机。document-end(默认)表示在DOM加载完成后、图片等资源加载前运行;document-start表示尽可能早地运行;document-idle表示在页面完全加载后运行。
注意:
@match规则编写需要格外小心。过于宽泛(如*://*/*)可能导致脚本在不需要的网站上运行,影响性能甚至引发错误。过于狭窄又可能无法覆盖目标网站的所有页面。建议先使用具体URL测试,再逐步放宽规则。
2.2 实战案例:为页面添加一个功能按钮
假设我们想为某个技术博客网站(例如 example-tech-blog.com)的每篇文章页面添加一个“一键复制文章标题和链接”的按钮。
首先,我们创建脚本并填写元数据:
// ==UserScript==
// @name 技术博客文章助手
// @namespace https://my-scripts.com
// @version 0.2
// @description 为技术博客文章页面添加一键复制标题和链接的功能按钮
// @author DevLearner
// @match https://example-tech-blog.com/article/*
// @grant GM_setClipboard
// @grant GM_notification
// ==/UserScript==
(function() {
'use strict';
// 你的代码将写在这里
})();
接下来,我们编写核心功能。思路是:等待页面主体内容加载完成,找到文章标题的DOM元素,然后创建一个按钮,并为其绑定点击事件。
(function() {
'use strict';
// 使用MutationObserver监听DOM变化,确保目标元素已加载
const observer = new MutationObserver(function(mutations, obs) {
// 尝试查找文章标题元素,这里假设标题在一个<h1>标签内,且类名为`.article-title`
const titleElement = document.querySelector('h1.article-title');
if (titleElement) {
// 找到标题后,停止观察
obs.disconnect();
// 执行添加按钮的函数
addCopyButton(titleElement);
}
});
// 开始观察整个文档<body>的子节点变化
observer.observe(document.body, { childList: true, subtree: true });
// 如果页面加载很快,可能在我们开始观察前标题就已经存在了,所以立即检查一次
const initialTitle = document.querySelector('h1.article-title');
if (initialTitle) {
observer.disconnect();
addCopyButton(initialTitle);
}
function addCopyButton(titleElement) {
// 获取文章标题文本
const articleTitle = titleElement.textContent.trim();
// 获取当前页面URL
const articleUrl = window.location.href;
// 创建按钮元素
const copyButton = document.createElement('button');
copyButton.textContent = '📋 复制标题与链接';
copyButton.style.cssText = `
position: fixed;
top: 80px;
right: 20px;
z-index: 9999;
padding: 8px 16px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
transition: all 0.15s ease;
`;
copyButton.onmouseover = () => { copyButton.style.opacity = '0.9'; };
copyButton.onmouseout = () => { copyButton.style.opacity = '1'; };
// 为按钮添加点击事件
copyButton.addEventListener('click', function() {
// 组合要复制的文本,例如:“文章标题 - 文章链接”
const textToCopy = `${articleTitle} - ${articleUrl}`;
// 使用Tampermonkey的API将文本写入剪贴板
GM_setClipboard(textToCopy, 'text').then(() => {
// 复制成功后,发送一个桌面通知(需要@grant GM_notification)
GM_notification({
text: `“${articleTitle}”的标题和链接已复制到剪贴板!`,
title: '复制成功',
timeout: 3000
});
// 也可以简单改变按钮文字提示
const originalText = copyButton.textContent;
copyButton.textContent = '✅ 已复制!';
copyButton.style.background = '#10b981';
setTimeout(() => {
copyButton.textContent = originalText;
copyButton.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
}, 2000);
}, (err) => {
// 复制失败的处理
console.error('复制失败:', err);
GM_notification({
text: '复制失败,请尝试手动复制。',
title: '操作失败',
timeout: 3000
});
});
});
// 将按钮添加到页面中
document.body.appendChild(copyButton);
}
})();
这个脚本做了以下几件事:
- 智能等待:使用
MutationObserver确保在文章标题元素加载完成后再执行操作,避免因页面加载速度问题导致脚本失效。 - 创建UI:动态创建了一个风格现代的固定定位按钮。
- 实现核心功能:点击按钮后,使用
GM_setClipboardAPI将格式化后的标题和链接复制到剪贴板。 - 提供反馈:复制成功后,通过
GM_notification发送桌面通知,并临时改变按钮样式和文字,给予用户清晰的操作反馈。
保存脚本(Ctrl+S)后,刷新目标博客的文章页面,你应该能看到一个紫色的按钮悬浮在页面右上角。点击它,如果一切顺利,你会收到复制成功的通知,并且粘贴板里已经有了整理好的信息。
3. 掌握核心技能:DOM操作与GM_* API详解
上一个案例我们接触了DOM操作和两个GM_* API。要编写更强大的脚本,必须深入理解这两块内容。
3.1 精准的DOM查询与操作
用户脚本的本质是操作网页的DOM(文档对象模型)。document.querySelector和document.querySelectorAll是你最常用的武器。
document.querySelector(selector):返回匹配指定CSS选择器的第一个元素。document.querySelectorAll(selector):返回一个包含所有匹配元素的NodeList(类似数组)。
选择器策略:网站结构可能变化,过于依赖具体的类名或ID可能导致脚本失效。更健壮的策略是:
- 使用相对路径和语义化标签:例如
article h1比.post-header .title .text更稳定。 - 组合使用属性选择器:
a[href*="download"]选择所有链接中包含“download”的<a>标签。 - 备用方案:如果首选选择器找不到元素,尝试备用选择器。
// 更健壮的元素查找示例
function findArticleContent() {
// 首选方案:通过文章容器的常见类名或ID查找
let content = document.querySelector('article .content, .post-content, #articleBody');
if (content) return content;
// 备用方案:通过语义化标签和结构查找
content = document.querySelector('main article, main .post');
if (content) return content;
// 最终备用:尝试查找页面中最大的文本容器
const allDivs = Array.from(document.querySelectorAll('div'));
const contentDiv = allDivs.reduce((a, b) => a.textContent.length > b.textContent.length ? a : b);
return contentDiv.textContent.length > 500 ? contentDiv : null; // 假设文章内容至少500字符
}
操作DOM:找到元素后,你可以修改其内容、样式、属性,或者创建、插入、删除元素。
// 修改元素
const title = document.querySelector('h1');
title.textContent = '新标题'; // 修改文本
title.style.color = 'red'; // 修改样式
title.classList.add('highlight'); // 添加CSS类
// 创建并插入新元素
const newDiv = document.createElement('div');
newDiv.innerHTML = '<p>这是动态添加的内容</p>';
document.body.appendChild(newDiv); // 插入到body末尾
title.parentNode.insertBefore(newDiv, title.nextSibling); // 插入到标题后面
// 监听事件
const button = document.querySelector('#myButton');
button.addEventListener('click', (event) => {
event.preventDefault(); // 阻止默认行为(如表单提交、链接跳转)
console.log('按钮被点击了!');
});
3.2 强大的GM_* API工具箱
Tampermonkey提供了一系列以GM_开头的特殊API,让脚本能安全地执行一些普通JavaScript在网页上下文中无法完成的操作。使用前必须在元数据中用@grant声明。
以下是一些最常用和强大的API:
| API | 用途 | 示例 |
|---|---|---|
GM_setValue / GM_getValue | 在浏览器存储中保存和读取脚本的持久化数据。 | GM_setValue('theme', 'dark'); let theme = GM_getValue('theme', 'light'); |
GM_setClipboard | 安全地将文本写入系统剪贴板。 | GM_setClipboard('要复制的文本', 'text/plain') |
GM_xmlhttpRequest | 发起跨域HTTP请求,这是脚本与外部API交互的关键。 | 可用于获取其他网站的数据,实现数据聚合。 |
GM_addStyle | 向页面注入CSS样式。比直接修改style标签更优雅。 | GM_addStyle('body { background-color: #f0f0f0; }') |
GM_notification | 创建桌面通知。 | GM_notification({text: '任务完成', title: '提示'}) |
GM_openInTab | 在新标签页中打开URL。 | GM_openInTab('https://example.com', {active: false}); |
GM_registerMenuCommand | 在Tampermonkey菜单中注册自定义命令。 | 为脚本添加一个快捷操作入口。 |
让我们看一个结合了数据存储和网络请求的复杂例子:一个简单的网页阅读进度保存器。
// ==UserScript==
// @name 阅读进度保存器
// @namespace http://tampermonkey.net/
// @version 0.3
// @description 自动保存和恢复你在长文章中的阅读位置
// @match https://*.long-article-site.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// ==/UserScript==
(function() {
'use strict';
// 为进度条添加样式
GM_addStyle(`
#reading-progress-bar {
position: fixed;
top: 0;
left: 0;
height: 4px;
background: linear-gradient(90deg, #ff6b6b, #4ecdc4);
z-index: 10000;
width: 0%;
transition: width 0.2s ease;
}
#reading-progress-resume {
position: fixed;
bottom: 20px;
right: 20px;
padding: 10px 20px;
background-color: #4ecdc4;
color: white;
border: none;
border-radius: 25px;
cursor: pointer;
display: none;
z-index: 10000;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
`);
// 生成当前页面的唯一标识(使用URL和文章标题)
const pageKey = `reading_progress_${window.location.pathname}_${document.title}`;
// 创建进度条和恢复按钮
const progressBar = document.createElement('div');
progressBar.id = 'reading-progress-bar';
document.body.appendChild(progressBar);
const resumeButton = document.createElement('button');
resumeButton.id = 'reading-progress-resume';
resumeButton.textContent = '⏩ 跳转到上次阅读位置';
document.body.appendChild(resumeButton);
// 尝试加载上次保存的滚动位置
const savedScrollY = GM_getValue(pageKey, 0);
if (savedScrollY > 100) { // 如果保存的位置超过100像素,显示恢复按钮
resumeButton.style.display = 'block';
resumeButton.addEventListener('click', () => {
window.scrollTo({ top: savedScrollY, behavior: 'smooth' });
resumeButton.style.display = 'none';
});
// 10秒后自动隐藏恢复按钮
setTimeout(() => { resumeButton.style.display = 'none'; }, 10000);
}
// 监听滚动事件,更新进度条并保存位置
let saveTimeout;
window.addEventListener('scroll', () => {
// 计算阅读进度百分比
const scrollTop = window.scrollY;
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
const scrollPercent = docHeight > 0 ? (scrollTop / docHeight) * 100 : 0;
progressBar.style.width = `${scrollPercent}%`;
// 防抖处理:停止滚动500ms后才保存位置,避免频繁写入存储
clearTimeout(saveTimeout);
saveTimeout = setTimeout(() => {
GM_setValue(pageKey, scrollTop);
console.log(`阅读位置已保存: ${scrollTop}px`);
}, 500);
});
// 页面卸载前强制保存一次
window.addEventListener('beforeunload', () => {
GM_setValue(pageKey, window.scrollY);
});
})();
这个脚本展示了如何将多个GM_* API与DOM操作结合,创建一个有状态、有交互的实用功能。它自动记录你的滚动位置,并在你下次访问同一文章时提供一键跳转的便利。
4. 调试、优化与脚本发布
脚本写好了,但它可能不完美,或者需要在不同环境下测试。掌握调试和优化技巧至关重要。
4.1 调试你的脚本
Tampermonkey脚本的调试与普通网页JavaScript调试几乎相同。
- 使用浏览器开发者工具:在运行脚本的页面上,按F12打开开发者工具。
- 切换到“源代码(Sources)”面板:在这里,你可以找到你的脚本。它通常位于
(油猴脚本) -> [你的脚本名称]目录下。你可以在这里设置断点、单步调试、查看变量。 - 控制台(Console)输出:在脚本中使用
console.log()、console.warn()、console.error()输出信息,这些信息会显示在开发者工具的Console面板中,是排查问题最直接的方式。 - Tampermonkey仪表盘:在仪表盘的“已安装脚本”列表中,可以方便地启用/禁用脚本、检查错误日志、编辑脚本源代码。
注意:有时脚本因为
@run-at时机或@match规则问题没有执行。首先检查Tampermonkey图标是否在目标页面显示为彩色(通常有数字),这表示有脚本在该页面运行。如果图标是灰色的,说明@match规则可能不匹配当前URL。
4.2 提升脚本的健壮性与性能
- 错误处理:使用
try...catch包裹可能出错的代码块,避免脚本因一个错误而完全崩溃。try { const sensitiveData = document.querySelector('.user-balance').innerText; // 处理数据... } catch (error) { console.warn('无法获取用户余额,可能页面结构已更改:', error); // 提供降级方案或友好提示 } - 防抖(Debounce)与节流(Throttle):对于
scroll、resize、input等频繁触发的事件,使用防抖或节流技术来限制处理函数的执行频率,避免性能问题。上一个进度保存器的例子就使用了防抖。 - 避免全局变量污染:始终将你的代码包裹在立即执行函数表达式(IIFE)中,并使用
'use strict';模式。(function() { 'use strict'; // 你的所有代码在这里 const myPrivateVar = 'safe'; // 这是局部变量 })(); // console.log(myPrivateVar); // 错误!无法访问 - 尊重网站与用户:不要过度请求数据,避免影响原网站性能。如果脚本会修改用户界面,确保改动是直观、非侵入式的,并提供关闭或配置选项。
4.3 分享与发布你的脚本
当你创作了一个有用的脚本,可能会想分享给他人。Greasy Fork 是目前最主流、最安全的用户脚本分享平台。
发布前请确保:
- 代码清晰:添加必要的注释,说明脚本的功能和配置方法。
- 元数据完整:准确填写
@name、@description、@match等。 - 遵守规则:阅读并遵守Greasy Fork的发布规则,不发布恶意、侵权或违反道德的脚本。
- 考虑开源:将脚本代码托管在GitHub等平台,便于他人贡献和改进。
发布流程通常是:在Greasy Fork点击“发布新脚本”,将你的脚本代码粘贴进去,填写描述、标签等信息,然后提交审核。通过后,其他用户就可以通过Tampermonkey的“获取新脚本”功能找到并安装你的作品了。
从“Hello World”到能够解决实际问题的自动化工具,用户脚本开发为你打开了一扇通往浏览器无限可能的大门。它不仅是学习JavaScript的绝佳沙盒,更是将想法快速转化为生产力的利器。当你下次再对某个网页产生“要是有这个功能就好了”的想法时,不妨打开Tampermonkey编辑器,尝试用代码将它实现。
9万+



被折叠的 条评论
为什么被折叠?



