油猴脚本这四个字你可能已经见过很多次了——去广告、页面重排、自动展开折叠内容、给视频网站加倍速这些不起眼的小功能装上之后就再也回不去了。但我发现一个很有意思的现象很多人用了好几年油猴脚本却从没想过自己动手写一个。原因倒也不复杂大家总觉得写脚本需要完整的前端工程能力其实油猴脚本开发的入门门槛低到难以想象。你只要会一点 JavaScript甚至现学都来得及就能从“只能等现成脚本”的消费者变成“随手解决网页痛点”的开发者。这篇入门教程就是带零基础或弱基础的你从装工具开始一步一步写出一个真正属于自己的油猴脚本并且搞懂它背后的运行逻辑。1. 这脚本到底能干嘛油猴脚本能解决的问题和它的边界1.1 一句话讲清楚油猴脚本是什么油猴脚本UserScript本质上是一段运行在浏览器页面上下文里的 JavaScript由脚本管理器这个浏览器扩展负责注入和执行。你可以把它理解成“网页的浏览器插件”——但不需要过审、不需要打包、不考虑跨浏览器兼容性打包工具改一行代码刷新页面就能看到效果。它和正经浏览器扩展的区别在于扩展是独立进程能监控所有标签页、修改浏览器界面油猴脚本只能在你指定的网站页面里运行但它保留了“随改随生效”的轻量开发体验。很多场景里写一个扩展要配置 manifest、打包、上传审核写一个油猴脚本只要新建一个文件手写几行元数据保存即用。这也是为什么大量前端工程师、测试工程师、运维在遇到内部提效需求时第一反应是“写个油猴脚本吧”。我用过油猴脚本做过的事情包括自动填写内部系统的重复表单、在项目管理页面给每个任务标题后面加“复制链接”按钮、把某网站默认的小字号改成适合阅读的排版、定时轮询某个数据接口并把结果用桌面通知弹出来。这些都是几十行代码能搞定的活如果都去做成浏览器扩展时间和收益完全不成比例。1.2 能做和不能做边界要心里有数能做操作页面 DOM增删改查节点、改样式、加事件页面怎么折腾都行。拦截和增强请求可以主动发起跨域请求也可以在页面现有请求返回后做二次处理。跨页面存数据通过 GM_setValue 之类的 API脚本可以自己保存配置和状态不受页面刷新影响。加菜单项在脚本管理器的菜单里加自定义按钮用来开关功能、执行操作。发桌面通知适合做长任务完成提醒、状态变化提醒。不能做的或者说做不好的不能访问浏览器系统 API比如下载管理、书签管理等需要 extension API 的能力。不能修改浏览器底层的网络栈、证书体系、代理规则这些“系统级”配置。不能在所有页面之外的地方运行比如浏览器新标签页这种特殊页面很多都注入不进去。如果目标页面有严格的内容安全策略CSP有些动态执行代码的操作会受限。把这些边界想清楚选型的时候就不会尴尬。比如你想做一个“拦截下载链接并自动重命名文件”的工具那大概率还是得老实写一个浏览器扩展但如果你只想让某个后台管理系统的表格更好看、更好点那油猴脚本就是最合适的工具。1.3 这篇文章适合谁会一点 JavaScript 但没做过完整项目的新手照着抄、改一改就能出成果。经常被重复网页操作困扰的效率党自己动手消灭重复劳动。前端同学油猴脚本是你做日常脚本工具、验证页面交互想法的最快路径。测试、运维、运营这类经常处理内部系统的人写脚本解决内部平台“难用”的问题性价比极高。不需要你懂 Node.js、不需要会打包工具、不需要有服务器。油猴脚本开发的环境其实就是一个浏览器加一个脚本管理器。2. 开发环境准备装扩展、开控制台、找试验田2.1 安装脚本管理器Tampermonkey 是我一直在用的油猴脚本必须靠脚本管理器来运行。市面上主流的有 Tampermonkey篡改猴、Violentmonkey暴力猴、Greasemonkey油猴的原版。我推荐 Tampermonkey理由很实在兼容性最好Chrome、Edge、Firefox、Safari 都能装而且 GM API 实现得最完整。脚本编辑界面自带语法高亮对新手友好。有导入导出、脚本备份功能换电脑迁脚本很省事。更新频率高对新版浏览器的适配快。安装步骤不复杂打开 Edge 或 Chrome 的扩展商店搜索 Tampermonkey点安装即可。装完后浏览器工具栏会出现一个黑色方块的图标。要注意装好之后默认是启用的但个别浏览器会要求你手动开启“允许访问文件 URL”之类的权限这个看情况开就行日常开发用不到。2.2 找一个安全的试验田你不需要一上来就针对重要网站开发脚本。我发现最好的做法是找一两个你自己常用、结构相对简单、就算改坏了也不影响工作的普通网站比如某个资讯类站点、文档类站点在上面反复试验。为什么强调“就算是改坏了也不影响工作”因为开发过程中你会反复刷新页面、改错代码、副作用层出不穷。如果你拿一个公司核心后台当试验田万一写了个死循环把页面搞卡了尴尬的是你自己。找个内容型网站练手的同时还能提高自己的阅读体验一举两得。另外建议你在浏览器里单独开一个用户配置专门用来跑开发中的脚本。Chrome 和 Edge 都支持创建独立的用户配置文件这样开发脚本和日常使用互不干扰脚本出了问题也不会污染你平时的浏览环境。这个小习惯能让你在调试脚本时少很多心理负担。2.3 浏览器开发者工具是你最重要的调试台写油猴脚本不需要装任何 IDE。浏览器自带的开发者工具就是你最核心的开发环境。按下 F12你会看到一整套面板但实际上写油猴脚本最常用的是两个Console控制台看脚本的 console.log 输出、报错信息。这是首要调试入口。Elements元素查看页面结构确认某个按钮、某个标题对应的 DOM 节点长什么样。调试时最常见的流程就是在 Elements 里找到目标节点确认它的 class 或 id然后回到 Tampermonkey 的编辑界面里写对应的选择器。这个过程和日常前端开发一模一样没有任何额外的工具成本。还有一个很实用的小技巧在做任何页面 DOM 操作之前先按 F12 打开 Console手动输入一行document.querySelector(目标选择器)看看能不能正确选中元素。这个方法能帮你快速排除选择器写错的问题省得每次都在脚本里折腾半天才发现是选择器不匹配。2.4 装上后第一件事新建一个测试脚本打开 Tampermonkey 面板点击“添加新脚本”会看到一个默认模板。先不去管模板里那些陌生的字段把内容替换成下面这段最简单的代码然后保存// UserScript // name 测试脚本 // namespace https://your-blog.example/ // version 1.0.0 // description 测试油猴脚本是否正常运行 // author you // match https://example.com/* // grant none // /UserScript (function () { use strict; console.log(hello from userscript); })();打开 example.com 这个网站按 F12 切到 Console你会看到hello from userscript这行输出。看到这行字就代表你的环境已经完全搭好了。这只是第一小步真正的骨架逻辑我们放在下一节讲。3. 脚本的骨架看懂并写好 UserScript 头部声明3.1 一段脚本元数据决定脚本在什么时候、什么地方跑Tampermonkey 新脚本模板里有一大堆以// UserScript开头和结尾的注释这部分叫“元数据块”metadata block。很多人刚开始会忽略它觉得不就是注释吗没什么用。这是最大的误解——元数据块就是脚本的配置文件脚本的运行范围、权限、时机全靠它控制。标准的骨架长这样// UserScript // name 脚本名称 // namespace https://your-blog.example/ // version 1.0.0 // description 脚本功能描述 // author your-name // match https://example.com/* // grant GM_addStyle // grant GM_setValue // grant GM_getValue // run-at document-idle // /UserScript我来逐个解释这些字段都是开发里经常要打交道的。3.2 match、include、exclude精确控制脚本的运行范围match是控制脚本在哪些 URL 上执行。它的语法类似于浏览器的通配符匹配最常用的是*通配符和路径匹配。比如https://example.com/*匹配 example.com 下所有页面。https://example.com/post/*只匹配 /post/ 路径下的页面。https://*.example.com/*匹配 example.com 的所有子域名。http://example.com/*注意这个只匹配 http 协议带 https 的要另外写。还有一个容易忽略的?符号它只在当前层级生效比如/post?*会匹配带任意 query 参数的文章页。新手经常会在这里写错最常见的问题是match写得太窄或者太宽。写窄了脚本怎么都不生效写太宽脚本会在所有页面执行拖慢浏览速度。我的建议是能写多窄就写多窄。只针对你要处理的页面路径做匹配这样既提升脚本执行效率也避免脚本在其他页面引发莫名其妙的副作用。还有一个老牌字段include语法比match更古老也更宽松我建议新手一律用match因为它的匹配规则更明确、更可控。如果你还需要在某些页面排除脚本那就用exclude配合。3.3 run-at选择脚本的执行时机run-at控制脚本在页面加载的哪个阶段执行。三个最常用的值document-start页面文档刚开始加载就执行。适合拦截或者修改早期 DOM比如替换页面标题、阻止某些元素创建。但此时 DOM 可能还没准备好很多操作会拿不到节点。document-endDOM 解析完成但图片样式等资源可能没加载完。这个时机比较均衡。document-idle页面加载基本完毕所有资源加载完后执行。大部分脚本推荐用这个因为此时 DOM 最完整操作不容易漏。实操里的经验是如果你的脚本只是给页面加个按钮、改改样式无脑用document-idle就好。如果你需要监听页面最初的一段内容变化或者要在最早期就注入样式避免闪烁才考虑document-start。3.4 grant脚本向脚本管理器申请权限grant是另一个容易踩坑的字段。它的作用是声明脚本需要使用哪些 GM API比如GM_addStyle、GM_setValue、GM_xmlhttpRequest等。如果不声明这些函数在脚本里就是 undefined调用会直接报错。有一个特殊值grant none。它的意思是脚本不申请任何 GM API完全以页面普通脚本的方式运行。这个模式性能最好、最符合原生态但它会损失所有 GM 函数的能力。很多纯 DOM 操作的脚本用grant none就够了不需要声明一大串授权。如果拿到别人的脚本模板它默认可能是grant none你新写了一个GM_addStyle的调用却忘了在头部加grant GM_addStyle那么脚本保存后只会静默失败控制台报一个GM_addStyle is not defined。定位方法也简单用到哪个 GM API就在grant里加哪个不加就不能用。3.5 为什么脚本主体要用 IIFE 包起来元数据块下面所有的脚本代码都被包在一个 IIFE立即执行函数表达式里(function () { use strict; // 你的代码 })();这么写主要有两个原因。第一隔离作用域。油猴脚本里的变量如果不包一层会直接挂到全局对象上一旦多个脚本用了同样的通用变量名就会互相污染排查起来非常痛苦。第二use strict严格模式能帮你提前暴露一些低级的变量声明错误比如忘记用 let/const 导致变量意外全局化之类的。我见过一些新手把脚本写成一列裸代码不包函数结果在某个页面同时装两个脚本的时候动不动就“xxx is not defined”。从第一天开始习惯 IIFE 包裹可以避免掉这一整类问题。4. 核心 API 实战存储、跨域、菜单、样式、通知一个都不少4.1 GM_setValue / GM_getValue解决页面刷新后数据丢失问题很多时候脚本需要保存一些用户配置比如“开关是否打开”“默认选择的是哪个值”。如果你用的是 localStorage会发现它绑定在当前页面域名下换个页面、清理缓存可能就没了而且不同脚本之间共享同一个 localStorage 键值空间容易出现命名冲突。GM_setValue 和 GM_getValue 是脚本管理器提供的独立存储 API数据由脚本管理器统一管理和页面本身完全隔离。用法就像 Map 一样简单// 存 GM_setValue(progressBarEnabled, false); // 取第二个参数是默认值 const enabled GM_getValue(progressBarEnabled, true);这里要养成的习惯是读取配置时永远给一个默认值。因为脚本第一次运行时配置项大概率还不存在你不给默认值拿到的就是 undefined后续逻辑用默认值兜底会健壮很多。实际项目中我把“脚本开关状态”这类配置存在 GM_setValue 里切换页面也不会丢体验比 localStorage 稳得多。4.2 GM_xmlhttpRequest绕开页面跨域限制请求外部接口这是油猴脚本最有魅力的 API 之一。普通的网页 fetch 或 XHR 会受到浏览器同源策略和 CORS 限制跨域请求经常被拦截。但 GM_xmlhttpRequest 是由脚本管理器这个扩展发起的不会受到页面 CORS 策略约束。这意味着你可以直接请求一个完全不同的第三方 API把数据拿回来展示到当前页面。使用方法// UserScript // grant GM_xmlhttpRequest // /UserScript GM_xmlhttpRequest({ method: GET, url: https://api.example-weather.org/v1/current?citybeijing, onload: function (res) { console.log(res.status); console.log(res.responseText); }, onerror: function (err) { console.log(请求失败, err); } });有几个细节要提醒。第一虽然它不受 CORS 限制但目标服务器仍然可能校验 Referer、User-Agent 等请求头所以不是所有请求都一定成功。第二请求频率千万要克制。你是用自己的浏览器在访问第三方服务不是给你的服务器集群做压测一旦频率太高轻则被目标服务封 IP重则影响脚本作者的名声。第三回调是异步的别在 GM_xmlhttpRequest 调用后面直接同步使用返回值这也是新手最容易犯的错。4.3 GM_registerMenuCommand在脚本管理器菜单里加上自己的按钮一个脚本如果只有“进页面自动改”那功能就比较死板。更灵活的做法是通过菜单命令让用户手动控制脚本行为。GM_registerMenuCommand 可以在脚本管理器的菜单里注册一个自定义选项点击后执行指定函数非常适合做“开关功能”“导出数据”“手动触发某操作”。示例// UserScript // grant GM_registerMenuCommand // /UserScript let progressOn true; function toggleProgress() { progressOn !progressOn; progressBar.style.display progressOn ? block : none; } GM_registerMenuCommand(切换进度条显示, toggleProgress);这个 API 的实际使用体验等于在脚本运行期间给用户提供了一个控制面板。新手开发时不妨优先考虑把一次性的自动操作做成菜单里可手动触发的动作这样即使页面结构变化导致自动触发失效用户还能通过菜单手动救场。4.4 GM_addStyle注入样式最干净的方式要用油猴脚本改页面样式有几种方式直接操作 style 属性、创建 style 标签、用 GM_addStyle。最推荐的是 GM_addStyle因为它是脚本管理器专门准备的样式注入 API注入的样式天然带一定的隔离性并且自动支持后续的更新和管理。// UserScript // grant GM_addStyle // /UserScript GM_addStyle( #my-custom-button { position: fixed; bottom: 20px; right: 20px; z-index: 99999; } );这里有一个经常遇到的坑目标网站通常有自己的样式体系优先级不一定比你低。如果你的样式老是不生效优先检查是不是被目标网站的样式覆盖了。解决方法是提高选择器优先级或者加上!important必要时把 z-index 拉高。比如你要加一个悬浮按钮z-index 写 9999 都不够用的时候直接上 99999基本能保证在绝大多数网站上正常显示。4.5 GM_notification把提示推送出来有些脚本是后台运行的比如定时刷新页面、监控价格变化、等待某个任务完成。这时候用户不可能一直盯着页面。GM_notification 能在系统桌面弹出一个原生通知适合做“任务完成提醒”。// UserScript // grant GM_notification // /UserScript GM_notification({ title: 监控提醒, text: 目标商品价格已更新记得去看看, timeout: 5000, onclick: function () { // 用户点击通知后的回调 console.log(notification clicked); } });我用得最多的场景是让脚本在后台轮询某个接口一旦检测到状态变化就发一条桌面通知。这样你可以放心去干别的活不用一直开着页面盯着看。要注意的是部分浏览器需要站点有通知权限如果系统弹不出来先看看浏览器设置里通知权限是否被禁了。5. 完整案例做一个阅读进度条和返回顶部按钮5.1 需求分析为什么选这个例子理论说了一大堆我们来落地一个完整例子。我的建议是做一个“阅读进度条 返回顶部按钮”的脚本原因是它麻雀虽小五脏俱全能覆盖 DOM 创建、样式注入、事件监听、滚动计算这几个最核心的技术点而且代码量不大二十多分钟就能写通。更重要的是这个功能几乎所有内容型网站都适用你装完马上就能感受到用处。设想的需求很简单页面顶部出现一条 3 像素高的进度条随着页面往下滚动进度条变长到页面底部时正好占满 100%。页面右侧悬浮一个“返回顶部”按钮往下滚动超过 400 像素时显示点击后平滑滚回顶部。5.2 第一步写好元数据声明// UserScript // name 阅读进度条和返回顶部 // namespace https://your-blog.example/ // version 1.0.0 // description 在文章页顶部添加阅读进度条并在右下角显示返回顶部按钮 // author your-name // match https://example.com/* // grant GM_addStyle // run-at document-idle // /UserScript注意这里的match我用了占位域名你实际使用时替换成自己的目标文章站点。因为功能只针对阅读型页面没必要让脚本在所有页面运行所以我把匹配范围收紧到 https://example.com/* 然后又在代码里进一步判断当前 URL 是否符合文章页的特征。5.3 第二步注入样式样式用 GM_addStyle 注入。进度条是固定在页面顶部的一条横线按钮固定在浏览器的右下角。注意两个元素都要规划好 z-index避免被目标网站的某些固定元素盖住GM_addStyle( #reading-progress-bar { position: fixed; top: 0; left: 0; height: 3px; width: 0%; background: #1a73e8; z-index: 99999; transition: width .1s linear; pointer-events: none; } #back-to-top-btn { position: fixed; right: 24px; bottom: 48px; width: 40px; height: 40px; line-height: 38px; text-align: center; border-radius: 50%; background: rgba(0, 0, 0, .6); color: #fff; font-size: 22px; cursor: pointer; display: none; z-index: 99999; user-select: none; } );pointer-events: none给进度条加上鼠标事件就不会被它挡住用户选中文本时也不会受到影响。这个细节刚开始很容易忽略写完之后才发现进度条挡在页面顶部点都点不动。5.4 第三步创建 DOM 节点并绑定事件有了样式接下来用 JavaScript 创建元素并追加到页面// 创建进度条节点 const progressBar document.createElement(div); progressBar.id reading-progress-bar; document.body.appendChild(progressBar); // 创建返回顶部按钮 const backToTopBtn document.createElement(div); backToTopBtn.id back-to-top-btn; backToTopBtn.textContent ↑; document.body.appendChild(backToTopBtn); // 点击返回顶部 backToTopBtn.addEventListener(click, function () { window.scrollTo({ top: 0, behavior: smooth }); }); // 滚动时更新进度条和按钮状态 function updateProgress() { const scrollTop window.scrollY; const docHeight document.documentElement.scrollHeight - window.innerHeight; const percent docHeight 0 ? (scrollTop / docHeight) * 100 : 0; progressBar.style.width percent %; backToTopBtn.style.display scrollTop 400 ? block : none; } window.addEventListener(scroll, updateProgress, { passive: true }); // 页面刚打开先执行一次保证初始状态正确 updateProgress();这里最核心的计算是进度百分比scrollTop / (scrollHeight - innerHeight) * 100。为什么要scrollHeight - innerHeight因为滚动条能滚动的最大距离是“文档总高度减去视口高度”。如果不减页面滚到底时 scrollTop 永远达不到 scrollHeight进度条就永远到不了 100%。这个公式在很多类“阅读进度”的场景里都能复用建议直接记下来。监听滚动时加上{ passive: true }是告诉浏览器这个监听器里不会调用 preventDefault浏览器可以放心做滚动性能优化。对 WordPress 这类内容站页面有时会比较重这个参数能让滚动更顺滑。5.5 第四步合成完整代码把上面的代码组合起来就可以保存试跑了// UserScript // name 阅读进度条和返回顶部 // namespace https://your-blog.example/ // version 1.0.0 // description 在文章页顶部添加阅读进度条并在右下角显示返回顶部按钮 // author your-name // match https://example.com/* // grant GM_addStyle // run-at document-idle // /UserScript (function () { use strict; GM_addStyle( #reading-progress-bar { position: fixed; top: 0; left: 0; height: 3px; width: 0%; background: #1a73e8; z-index: 99999; transition: width .1s linear; pointer-events: none; } #back-to-top-btn { position: fixed; right: 24px; bottom: 48px; width: 40px; height: 40px; line-height: 38px; text-align: center; border-radius: 50%; background: rgba(0, 0, 0, .6); color: #fff; font-size: 22px; cursor: pointer; display: none; z-index: 99999; user-select: none; } ); const progressBar document.createElement(div); progressBar.id reading-progress-bar; document.body.appendChild(progressBar); const backToTopBtn document.createElement(div); backToTopBtn.id back-to-top-btn; backToTopBtn.textContent ↑; document.body.appendChild(backToTopBtn); backToTopBtn.addEventListener(click, function () { window.scrollTo({ top: 0, behavior: smooth }); }); function updateProgress() { const scrollTop window.scrollY; const docHeight document.documentElement.scrollHeight - window.innerHeight; const percent docHeight 0 ? (scrollTop / docHeight) * 100 : 0; progressBar.style.width percent %; backToTopBtn.style.display scrollTop 400 ? block : none; } window.addEventListener(scroll, updateProgress, { passive: true }); updateProgress(); })();保存后在目标文章页面刷新往下滚顶部进度条会跟着走往下滚超过 400 像素右下角会出现按钮点击平滑回顶。整个流程很直观。这个脚本后续还能扩展加一个“返回上次阅读位置”的标记加夜间模式切换甚至加一个阅读计时器。每加一个功能你就多练一个知识点慢慢地就从一个只会抄脚本的人变成能独立设计脚本的人了。6. 调试、发布与常见问题排查6.1 调试流程先看报错再打日志最后加断点油猴脚本开发最常见的问题就是“脚本没生效”这时候千万别急着改代码。先按 F12 打开控制台看有没有红色报错然后一步步排查确认脚本已启用且当前页面 URL 匹配上了你写的match。在脚本第一行加一个console.log(script loaded)刷新页面看控制台有没有输出。没有输出说明脚本根本没在这个页面运行问题基本在match或浏览器扩展权限上。有输出但后续逻辑没执行说明是某句代码报错中断了看控制台的具体报错信息。如果逻辑不稳定比如有时生效有时不生效考虑是不是run-at时机不对或者页面加载后又有新的 DOM 被渲染。如果只是想快速验证一个选择器对不对我常用的土办法是直接在控制台输入一行document.querySelectorAll(这里写选择器)看看返回结果。这个技巧能帮你省去无数轮“改代码-保存-刷新”的循环。6.2 发布到 GreasyFork让别人也能用上你的脚本写好的脚本除了自用也可以发布到 GreasyFork。这是目前最主流的用户脚本分享平台。发布流程不复杂注册一个 GreasyFork 账号。点击“发布新脚本”把元数据块和代码整体粘贴进去。填好描述、标签、支持的浏览器。发布后其他用户点击“安装此脚本”就能通过 Tampermonkey 直接安装。发布前有一个容易被忽略的点版本号。每次更新都要记得改元数据里的version否则用户那边可能永远收不到更新提醒。版本号我习惯用1.0.0起步小改动 0.0.1功能变化较大再升级到1.1.0。还要给脚本起个好的名字和描述。不要只写“XXX 工具”要说清楚它解决什么问题比如“为 XX 页面添加返回顶部按钮和阅读进度条”。这样别人在 GreasyFork 里搜索时更容易找到你的脚本。6.3 常见问题速查表问题常见原因解决办法脚本完全不运行控制台无输出match 不匹配当前URL脚本被禁用Tampermonkey 扩展权限异常在脚本文本里临时加 console.log确认匹配范围逐个检查 matchGM_xxx 报 undefined没有在元数据块里 grant 对应权限用到哪个 API就在 grant 上加哪个脚本在页面刷新后丢失状态使用了页面 localStorage 而不是 GM_setValue换成 GM_setValue / GM_getValue样式不生效被目标网站样式覆盖提高选择器优先级加 !important必要时提高 z-index页面很卡脚本在太多页面执行监听器没做节流收窄 match滚动监听改用 passive 并在回调里精简操作页面内容动态加载脚本处理不到脚本只在页面初载时执行了一次用 MutationObserver 监听 DOM 变化或者监听 URL 变化后重新执行与其他脚本变量冲突变量名太通用污染了全局作用域始终用 IIFE 包裹变量命名带上脚本前缀6.4 动态页面是最大的坑MutationObserver 的思路现在很多网站都是 SPA单页应用页面内容会随着路由切换和接口返回不断变化。你脚本只在document-idle时跑一次明明选中的元素当时还在过一会儿就被框架重渲染掉了按钮和样式全没了。面对这类动态页面最简单的兜底方案是 MutationObserver。它就像页面的“安保摄像头”每当 DOM 发生变化就通知你然后在回调里重新执行你需要的 DOM 操作和事件绑定。基本用法const observer new MutationObserver(function (mutations) { // 检查目标节点是否回来了回来了就补一份操作 if (!document.querySelector(#target-node)) return; // 你的初始化逻辑 init(); }); observer.observe(document.body, { childList: true, subtree: true });不过 MutationObserver 不能滥用——只要 DOM 一变就触发如果你在回调里又去改 DOM很容易触发连环回调把页面搞得卡顿。实际开发中我会在回调里加一个简单的检查条件只有目标节点确实需要处理时才执行初始化逻辑并且初始化前先做一次“是否已初始化”的判断。6.5 一些来自经验的小建议最后分享几个我踩过坑之后养成的习惯。第一开发阶段的脚本名称里加一个“-dev”后缀。这样你能一眼分辨哪个是正在调试的版本哪个是稳定版。发布到 GreasyFork 的时候再把后缀去掉更新版本号避免用户安装到测试版。第二代码里所有 ID、class 名都加上自己的前缀比如rp-progress、rp-backtop这种。页面是别人的class 名冲突不可控自己加前缀能大幅度减少样式和选择器相互干扰的概率。第三写完脚本后不要急着加功能。先用一段时间看看有没有误伤结构是否稳定。油猴脚本最大的风险不是写不出来而是写得太多、太复杂最后自己都维护不动。把一个痛点解决干净比堆砌十个功能更有价值。第四改完脚本要刷新页面才生效。这个听起来像废话但真的很多人忘了Tampermonkey 里保存的脚本不会自动重新注入当前已打开的页面。如果你在某个页面上调试改完代码后记得 CtrlShiftR 强制刷新确保拿到的是最新脚本。
阅读完成 · 觉得有帮助?