一个面向 M-Team / mTorrent / NexusPHP 种子列表页的 Tampermonkey 用户脚本。它针对大量高清海报同时下载、解码和绘制导致的滚动卡顿进行限流,并内置完整、不裁切的高清悬浮预览。
非 M-Team 官方项目。脚本仅在浏览器本地运行,不发送统计数据,也不需要访问令牌。
- 安装 Tampermonkey 或兼容的用户脚本管理器。
- 点击 安装最新版脚本。
- 在用户脚本管理器中确认安装,然后硬刷新 M-Team 列表页。
如果以前安装过单独的“馒头种子列表页图片预览”脚本,建议将其停用。优化器已经包含高清预览功能,并会隐藏旧脚本创建的 #img-preview,避免一大一小两张预览同时出现。
实测的100行列表页包含100张海报。很多列表缩略图实际指向约 2000px 的 *pl.jpg 原图:它们虽然只显示为约 52×70px,但单张解码后的 RGBA 位图可能占用十余 MiB。浏览器原生 loading="lazy" 的预取范围较宽,快速滚动时仍可能同时下载和解码大量海报。
这个脚本不把所有卡顿都归因于“大 DOM”或 <table>。它只对确认过的列表、行和海报选择器采取优化,并把传统表格行 containment 保留为默认关闭的实验选项。
- 精确观察
img.torrent-list__thumbnail,不会清空全站所有图片。 - 使用
IntersectionObserver,只让视口上下约 900px 内的海报保留真实src。 - 较远的海报临时替换成 1×1 透明图,减少同时存在的高清解码位图。
- 离开范围后有 800ms 缓冲,避免在边界附近轻微滚动时反复切换。
- 恢复的 URL 不附加随机参数,浏览器仍可使用内存缓存、磁盘缓存或条件缓存。
需要注意:如果图片尚未下载完成就快速滚远,未完成请求可能被取消;再次返回时浏览器可能重新发起请求。已经缓存的图片通常不会重新完整下载,但仍可能重新解码。
- 默认最多同时加载、解码2张海报,避免几十张大图一起争用网络和解码资源。
- 每次放行前重新计算优先级:当前屏幕内的图片优先,屏内再按距离屏幕中心排序。
- 滚动期间暂停发放新任务,滚动停止后再按当前位置调度。
- 失败图片会立即释放队列槽位,不会阻塞后续图片5秒。
2.5.0 修复了快速往返滚动的竞态:旧版可能已经从数组队列丢弃一张图片,却忘记清除它的 restoreQueued 标记,导致图片滚回来后永远无法再次入队。新版使用可撤销的 Set 队列,并同步清理 DOM 状态;加载途中滚出又滚回也会在旧任务结束后重新调度。
- 列表图片使用
decoding="async"。 - 非核心大图使用原生
loading="lazy",并在适用时降低下载优先级。 - 可选把 DMM/FANZA 的
pl.jpg改为ps.jpg小图;此功能默认关闭,因为部分作品没有对应小图,会先产生404再回退原图。 - 不强制猜测图片宽高,避免破坏站点原有布局。
- 鼠标停留约 180ms 后才加载,快速划过不会连续请求大图。
- 始终读取脚本保存的原始高清 URL,不受列表透明占位图或可选小图影响。
- 使用独立预览层和
object-fit: contain; object-position: center,横向双封面与纵向海报都不会被裁掉一半。 - 最大受
92vw、1400px 和88vh限制。 - 长边不足720px的小图默认等比放大,最多2倍,同时不超过视口。
- 滚动、移开鼠标、切换窗口或按 Escape 会关闭预览并撤销未完成请求。
- 高清图失败时尝试当前可用缩略图。
- 捕获
scroll、wheel和touchmove,覆盖站点的嵌套滚动容器。 - 只阻止种子行预览触发元素的 hover,不使用
body * { pointer-events:none }。 - 已出现的 tooltip/hoverbox 只在滚动期间暂时隐藏,不影响页面点击和菜单操作。
- 使用一个按截止时间续期的静默计时器,避免每个滚动事件反复创建定时器。
- 自动识别真正的种子列表根和数据行。
- 只给非
<tr>的现代块级列表行默认启用content-visibility:auto。 - 使用
contain-intrinsic-block-size:auto 64px,浏览器可记忆真实行高。 - 传统
<tr>containment 默认关闭,避免列宽、行高或粘性表头异常。 - MutationObserver 合并新增 DOM 根,并在空闲时间扫描 SPA 更新,不在滚动热路径遍历整页。
打开 Tampermonkey 的当前页脚本菜单即可切换:
| 设置 | 默认值 | 作用 |
|---|---|---|
| 启用优化器 | 开 | 总开关 |
| 滚动时屏蔽悬浮预览 | 开 | 避免滚动划过时创建预览 |
| 列表图片延迟加载 | 开 | 为普通列表图片补充原生懒加载 |
| 海报仅在视口附近加载 | 开 | 远处海报使用透明占位 |
| 海报限流加载解码(当前屏优先) | 开 | 最多2张并发并优先当前屏幕 |
| 列表优先使用 DMM 小海报 | 关 | 尝试 pl.jpg → ps.jpg |
| 鼠标悬浮显示高清完整海报 | 开 | 内置高清预览 |
| 悬浮预览适度放大小图 | 开 | 小图最多放大2倍 |
| 现代列表屏外渲染优化 | 开 | 对块级列表行启用 content-visibility |
| 实验:传统表格行屏外渲染 | 关 | 仅用于手动对照测试 |
| 调试日志 | 关 | 输出调度信息 |
“输出本页优化统计”会在开发者工具 Console 中显示识别的列表、行、图片、队列和预览统计。
根据需求可以组合两个海报选项:
| 视口附近加载 | 限流加载 | 行为 |
|---|---|---|
| 开 | 开 | 默认;内存占用最低,滚回来可能从缓存恢复并重新解码 |
| 关 | 开 | 每张图片第一次访问时限流加载,之后保留真实 src |
| 关 | 关 | 最接近原始站点,只依赖浏览器原生 lazy loading |
若希望确认图片是否重新走网络,在 Chrome/Edge DevTools 的 Network → Img 中关闭 Disable cache,观察 Transferred:(memory cache)、(disk cache) 或 0 B 都不是重新完整下载;304 只进行了缓存验证。
- 这是站点外部用户脚本,不会篡改 React 数据源,也不实现伪虚拟列表。
- M-Team 当前100行页面还存在站点自身的 Redux/Immer 批量状态更新问题。性能录制中,
torrent/search和queryHistory的响应处理各产生约5秒同步长任务;图片脚本无法安全修复站点内部的 O(n²) 状态更新。 - 图片来自多个第三方域名,实际速度仍受 CDN、缓存命中、图片压缩体积和像素尺寸影响。
- 站点若更改
torrent-list__thumbnail或列表 DOM 结构,可能需要更新选择器。 - 访问过的高清预览仍可能占用浏览器缓存;浏览器会根据内存压力自行回收解码位图。
升级到 2.5.0 或更高版本,硬刷新页面,并确认“海报仅在视口附近加载”和“海报限流加载解码(当前屏优先)”均为开启状态。
停用以前单独安装的海报预览脚本。新版虽然会隐藏旧 #img-preview,但停用旧脚本还能去除它的事件监听和网络包装。
关闭“海报仅在视口附近加载”,保留限流加载。图片首次显示后将继续保留真实 src,代价是访问的图片越多,内存占用越高。
先关闭“实验:传统表格行屏外渲染”。它默认应为关闭状态。
在 Microsoft Edge 150 和保存的100行列表页上完成的离线回归包括:
- 识别100/100个数据行和100/100张列表海报。
- 顶部首屏只保留约6张真实海报,远处海报使用占位图。
- 快速执行“底部 → 60ms 后顶部 → 队列清理 → 再回到底部”,最终当前视口9/9张缩略图全部恢复。
- 模拟 React 重写屏外图片
src后能够重新接管。 - 2184×1468高清图以
contain完整显示,无横向裁切。 - 260×360小图等比放大为456×632。
- Escape 可立即关闭预览并移除高清图请求。
- JavaScript 通过
node --check语法验证。
更完整的页面审查与测试记录见 使用说明与审查结论.md。
项目没有构建步骤,核心文件就是:
mteam-scroll-optimizer.user.js
修改后至少执行:
node --check mteam-scroll-optimizer.user.js版本发布时同时更新 userscript 头部的 @version 和 CHANGELOG.md。
- Bug 与兼容性问题请提交 GitHub Issue。
- 报告性能问题时,建议附上浏览器版本、列表每页数量、相关菜单设置,以及去除隐私信息后的 Performance Trace。
- Pull Request 请保持无构建依赖,并说明验证过的页面结构和浏览器。