本帖最后由 溯水流光 于 2025-2-25 22:22 编辑
本帖最后由 溯水流光 于 2025-2-24 15:27 编辑
elmGetter 水果玉米 魔改版 文档
原版的地址: https://bbs.tampermonkey.net.cn/thread-2726-1-1.html
感谢 cxxjackie 大佬的开源, cxxjackie 大佬的代码未明确开源协议,我实在是太欣赏这段代码,没忍住先 fork 了,在此诚挚地向 cxxjackie 大佬道个歉。如果涉及任何侵权问题,我一定第一时间下架,绝不含糊。
如果我魔改后的库能合并到主分支,将会是最棒的。
修改源码的契机
elmGetter原有的部分代码设计用起来不太顺手,所以我决定对它进行魔改,添加自己需要的功能。
我希望给 elmGetter 添加三个功能:一是增加超时错误处理回调参数,在未找到元素时打印日志,便于调试;二是通过 bool 参数控制超时时,让 promise 保持 pending 或者 resolve (null);三是支持自定义超时时 resolve 的值,比如 resolve 一个无法删除的 div。
我不仅把这些参数保留为函数参数,还将其设为 elmGetter 成员变量,便于统一设置。为传参方便,我重构了几乎所有函数的传参方式,支持对象传参,无需记参数顺序。
require:
// @require https://scriptcat.org/lib/2847/3.0.0/ElementGetter%20%E6%B0%B4%E6%9E%9C%E7%8E%89%E7%B1%B3%20%E9%AD%94%E6%94%B9%E7%89%88.js
魔改版 vs 原版
和原版相比,elmGetter水果玉米魔改版主要在优化开发体验上做了提升和改善。
-
✅ 新增超时回调,方便日志输出,便于调试代码;
-
✅ 新增统一设置超时时间功能,方便在脚本发布时去掉超时时间;
-
✅ 还新增了超时后的行为控制,能通过参数或成员变量设置超时回调、超时后promise的状态,以及超时后resolve降级元素。
-
✅ 我会持续维护这个库,一旦原版有更新,将及时进行合并。
-
✅ 接口参数进行了重构,使用option方式传参,增加代码的可读性,和方便了接口调用
-
❎ 由于进行了接口重构,这个库将不兼容原版库
简单的代码示例
原版的代码:
// ==UserScript==
// @name New Userscript
// @namespace http://tampermonkey.net/
// @version 2025-02-21
// @description try to take over the world!
// @author You
// @match http://127.0.0.1:5500/playground.html
// @icon https://www.google.com/s2/favicons?sz=64&domain=0.1
// @grant unsafeWindow
// @require https://scriptcat.org/lib/513/2.0.1/ElementGetter.js#sha256=V0EUYIfbOrr63nT8+W7BP1xEmWcumTLWu2PXFJHh5dg=
// ==/UserScript==
(async function () {
'use strict';
// TODO: 发布脚本时, 请去掉延时, 延时会导致 elmGetter 启动 setTimeout, 降低脚本性能, 这里设置延时, 仅仅是方便日志打印和调试bug
const timeout = 300;
const childSelector = ".child";
let el1 = await elmGetter.get(childSelector, timeout);
if (el1 === null) {
console.warn(`[elmGetter] [get失败] ${childSelector} 没有找到`);
}
const errorSelector = ".error";
// TODO: 发布脚本时, 请去掉延时, 延时会导致 elmGetter 启动 setTimeout, 降低脚本性能, 这里设置延时, 仅仅是方便日志打印和调试bug
let el2 = await elmGetter.get(errorSelector, timeout);
if (el2 === null) {
console.warn(`[elmGetter] [get失败] ${errorSelector} 没有找到`);
}
})();
魔改版:
// ==UserScript==
// @name New Userscript
// @namespace http://tampermonkey.net/
// @version 2025-02-21
// @description try to take over the world!
// @author You
// @match http://127.0.0.1:5500/playground.html
// @icon https://www.google.com/s2/favicons?sz=64&domain=0.1
// @grant unsafeWindow
// @require [库地址]
// ==/UserScript==
// 没有中间层
initGlobalVar();
(async function () {
'use strict';
// TODO: 发布脚本时, 请去掉延时, 延时会导致 elmGetter 启动 setTimeout, 降低脚本性能, 这里设置延时, 仅仅是方便日志打印和调试bug
elmGetter.timeout = 300;
let el1 = await elmGetter.get(".comments");
let el2 = await elmGetter.get(".error");
})();
接口文档
使用说明: 基本上和原版一致, 看cxxjackie的原文就好了, https://bbs.tampermonkey.net.cn/thread-2726-1-1.html, 这里主要的是接口参数有变化, 变成了options 传参
get
/**
* 异步的 querySelector
* @param selector
* @param options 一个对象
* - parent 父元素, 默认值是 document
* - timeout 设置 get 的超时时间, 默认值是 elmGetter.timeout, 其值默认为 0
* - 如果该值为 0, 表示永不超时, 如果 selector 有误, 返回的 Promise 将永远 pending
* - 如果该值不为 0, 表示等待多少毫秒, 和 setTimeout 单位一致
* - onError 超时后的失败回调, 参数为 selector, 默认值为 elmGetter.onError, 其默认行为是 console.warn 打印 selector
* - isPending 超时后 Promise 是否仍然保持 pending, 默认值为 elmGetter.isPending, 其值默认为 true
* - errEl 超时后 Promise 返回的值, 需要 isPending 为 false 才能有效, 默认值为 elmGetter.errorEl, 其值默认为一个 class 为一个 class 为 no-found 的元素
* @returns {Promise<Awaited<unknown>[]>|Promise<unknown>}
*/
get(selector, options = {}) {
let {
parent = doc,
timeout = this.timeout,
onError = this.onError,
isPending = this.isPending,
errEl = this.errEl,
} = options;
each
/**
* 为父节点设置监听,所有符合选择器的元素(包括页面已有的和新插入的)都将被传给回调函数处理,
* each方法适用于各种滚动加载的列表(如评论区),或者发生非刷新跳转的页面等
* @param selector
* @param callback 回调函数, 只在每个元素上触发一次。 回调函数接收2个参数,第一个是符合选择器的元素,第二个表明该元素是否为新插入的(已有为false,插入为true)
* @param options 一个对象
* - parent 父元素, 默认值是 document
*/
each(selector, callback, options = {}) {
let {
parent = doc,
} = options;
create
/**
* 将html字符串解析为元素
* @param domString
* @param options 一个对象
* - returnList 布尔值,是否返回以 id 作为索引的元素列表, 默认值为 false
* - parent 父节点,将创建的元素添加到父节点末尾处, 如果不指定, 解析后的元素将
* @returns {Element|{}|null} 元素或对象,取决于returnList参数
*/
create(domString, options = {}) {
let {
returnList = false,
parent = null
} = options;
成员变量
let errEl = document.createElement('div');
errEl.classList.add('no-found');
errEl.remove = () => {};
return {
timeout: 0,
onError: (selector) => {console.warn(`[elmGetter] [get失败] selector为: ${selector} 的查询超时`)},
isPending: true,
errEl,
配套的完整指南
我为elmGetter打造了一整套原版源码分析指南,旨在帮大家掌握源码修改技巧,让elmGetter成为你的如意兵器。
指南内容涵盖MutationObserve快速上手、源码分析思路与知识点补充,还将经典的WaitForKeyElememt异步库设计作为附录。
目前已近万字,我仍在精简润色,后续会拆分成多篇文章,整理目录后发布于脚本猫论坛,敬请期待!
(◦˙▽˙◦)
文档链接: https://bbs.tampermonkey.net.cn/thread-8196-1-1.html
未整理过的碎碎念
原版 vs 魔改版
原版存在的问题: 当一个脚本有40,50个异步查询的时候,开发的过程中不小心填错了某个选择器,或某个选择器失效了,由于elmGetter的默认策略就是无限等待pending,也不会控制台输出,所以排查问题变得比较棘手
唯一的解决方案是设置timeout参数,elmGetter超时后,判断其resolve的值是否为null
这里存在三个问题,
- 第一是要写大量的 if 判断
- 二是我就想超时后pending,原版是无法做到的
- 第三,elmGetter使用timeout参数是极其耗费性能的操作,因为底层会起很多的setTimeout,等到生产环境后,还得一个个去掉timeout, 得统一方便地设置延时
使用了魔改库后,解决了这三个问题
摘要
摘要: elmGetter 魔改版的主要改进,增加了错误处理回调,方便找不到元素的时候打印日志,排查错误
在elmGetter 魔改版找不到元素的时候,默认错误处理是console.warn
而且支持全局统一设置超时时间,等发布脚本的时候直接把那行注释掉就可以了
明细
- ✅ 添加了 elmGetter.timeout 成员变量, 方便设置全局 timeout, 来排查代码错误,
等实际脚本发布的时候, 把设置 timeout 代码去掉就可以了, 避免elmGetter 创建过多的 setTimeout 来浪费脚本性能
- ✅ 添加了elmGetter.get错误处理参数, 在超时后, 调用你的失败回调, 方便超时进行日志输出, 定位脚本bug
- ✅ elmGetter.onError 支持设置全局错误处理, 来统一继续超时后的错误处理, 默认的全局错误处理是() => console.warn("[elmGetter]: 找不到")
- ✅ elmGetter.get 找不到目标后有两种处理:
- 如果没有设置timeout, 会一直pedding, 这个可以用于发布环境, 但如果在开发环境使用, 因为没有日志打印, 容易找不到bug
- 如果设置了timeout, 原版会resolve(null), 我们这里支持通过isPedding参数和elmGetter.isPedding设置超时后, 调用完失败回调后, 仍然pedding, 也支持设置errorEl和elmGetter.errorEl来设置resolve(errorEl)
❎代码参数部分进行了全面重构, 不向后兼容以前版本的elmGetter
后记
感谢 cxxjackie 大佬的开源,正是有了他的贡献,我才能站在巨人的肩膀上;感谢脚本猫脚本站的站长和管理员,正是有了这个平台,脚本分享和交流才变得如此便捷。最后,衷心感谢每一位用户的支持!祝你们用得开心!
✿✿ヽ(°▽°)ノ✿完结撒花!