上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
返回列表 发新帖

一种很新的脚本开发方式——Selector HMR ?!

[复制链接]

该用户从未签到

3

主题

15

回帖

88

积分

初级工程师

积分
88
发表于 2023-12-23 01:02:38 | 显示全部楼层 | 阅读模式

本帖最后由 LinLin00 于 2023-12-23 01:23 编辑

此前,我们已经有了 LinLin00000000/usbuild: 一个基于 esbuild 的油猴脚本(UserScript)构建工具 (github.com) 这样一个开发工具,它拥有开发模式实时热重载的功能。

这个实时热重载就是监听你脚本源文件的变化,一有变化就会通知浏览器,然后浏览器进行页面刷新,免去了手动刷新的麻烦。

但是,即使有了 autoReloadDelay 这样的控制自动刷新间隔的参数,在元素繁多的页面进行频繁刷新也会产生一点不好的体验,因为刷新慢加载慢,再加上一般需要开着浏览器调试工具,会感觉到明显的卡。

我去找了另一个比较有名的构建工具 vite-plugin-monkey,它在搭配一些前端框架如 Vue、React 使用时可以对里面的前端页面实现 HMR,但一般我开发脚本都是使用 document.querySelector 获取页面上的元素进行一些修改,而不是直接塞一个前端框架在旁边。当我在它的 dev 模式中使用 document.querySelector 对页面元素做修改时,无法享受到 vite 的 hmr 功能,也是只能把整个网页重新加载。

于是我给我的工具加了一个实验性功能(尚未发布),可以在监听到文件变化时不直接刷新浏览器,而是进行动态地重新安装脚本,再加上一点小小的魔法(后面会讲到),就可以实现 Selector 级别的 HMR,下面直接看效果

演示动画.gif
(竟然只支持 1MB 以下的图片,只能压缩得非常糊)

原理其实很简单,虽然每次都会重新安装脚本,但是只要在 unsafeWindow 上存储一个 cache 就行了,每次运行的时候先找有没有 cache,有就先恢复元素最初的样子,然后再执行用户自定义的 callback。其实就是简单封装一下 dynamicQuery

有一些局限性,需要做好缓存和恢复的工作,还要处理好副作用,可以考虑抽象出一个通用的插件 {save(), load(), update()} 就可以适用更多场合。

const selectorHMR = (selector, callback, options) => {
    const CACHE_NAME = '_usbuild_selectorHMR'
    if (!unsafeWindow[CACHE_NAME]) {
        unsafeWindow[CACHE_NAME] = new Map()
    }
    const cacheMap = unsafeWindow[CACHE_NAME]
    const uuid = selector

    let cache

    if (cacheMap.has(uuid)) {
        console.log(`cache | ${uuid} | load`)
        cache = cacheMap.get(uuid)
        cache.remove()
        dynamicQuery(selector, e => (e.outerHTML = cache.originHTML), options)()
        cache.remove = dynamicQuery(selector, callback, options)
    } else {
        console.log(`cache | ${uuid} | save`)
        cache = {}
        cacheMap.set(uuid, cache)
        cache.remove = dynamicQuery(
            selector,
            e => {
                cache.originHTML = e.outerHTML
                callback?.(e)
            },
            options
        )
    }

    return cache.remove
}
已有1人评分好评 油猫币 理由
王一之 + 1 + 4 赞一个!

查看全部评分 总评分:好评 +1  油猫币 +4 

  • TA的每日心情
    开心
    2024-3-13 10:14
  • 签到天数: 211 天

    [LV.7]常住居民III

    294

    主题

    3906

    回帖

    3826

    积分

    管理员

    积分
    3826

    管理员荣誉开发者油中2周年生态建设者喜迎中秋油中3周年挑战者 lv2

    发表于 2023-12-23 09:56:39 | 显示全部楼层
    ggnb!

    这样的话,这样需要手动控制?感觉全局热重载够了,生产环境的时候不会用到这个

    或者还有些什么场景呢?不过代码值得学习!
    上不慕古,下不肖俗。为疏为懒,不敢为狂。为拙为愚,不敢为恶。/ 微信公众号:一之哥哥
    回复

    使用道具 举报

    该用户从未签到

    3

    主题

    15

    回帖

    88

    积分

    初级工程师

    积分
    88
    发表于 2023-12-23 12:01:50 | 显示全部楼层
    王一之 发表于 2023-12-23 09:56
    ggnb!

    这样的话,这样需要手动控制?感觉全局热重载够了,生产环境的时候不会用到这个

    不太明白你说的手动控制是什么意思?我感觉开发流程已经非常自动化了。

    当然是开发才用,生产模式的话直接打包部署了。

    全局热重载频繁刷新有时候体验并不好,或者就是有些页面你刷新一次会带来比较严重的副作用,就可以采用这种不刷新页面的方法。

    这确实可以解决一些我开发脚本的痛点,不过这应该也就是我个人用用了,封装成库需要做的事情很多,比直接刷新页面复杂多了。
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2024-3-13 10:14
  • 签到天数: 211 天

    [LV.7]常住居民III

    294

    主题

    3906

    回帖

    3826

    积分

    管理员

    积分
    3826

    管理员荣誉开发者油中2周年生态建设者喜迎中秋油中3周年挑战者 lv2

    发表于 2023-12-23 13:59:48 | 显示全部楼层
    LinLin00 发表于 2023-12-23 12:01
    不太明白你说的手动控制是什么意思?我感觉开发流程已经非常自动化了。

    当然是开发才用,生产模式的话直 ...

    我的手动控制的意思是:我要用selectorHMR这个方法指定局部,才能使用这个局部热重载功能

    临时debug应该很好用
    上不慕古,下不肖俗。为疏为懒,不敢为狂。为拙为愚,不敢为恶。/ 微信公众号:一之哥哥
    回复

    使用道具 举报

    该用户从未签到

    3

    主题

    15

    回帖

    88

    积分

    初级工程师

    积分
    88
    发表于 2023-12-23 15:33:04 | 显示全部楼层
    本帖最后由 LinLin00 于 2023-12-23 15:37 编辑
    王一之 发表于 2023-12-23 13:59
    我的手动控制的意思是:我要用selectorHMR这个方法指定局部,才能使用这个局部热重载功能

    临时debug应该 ...

    确实是这样,得自己主动控制让哪一部分带上 HMR 功能,但是指定之后可以给开发调试提供很大的便利,磨刀不误砍柴工吧😋

    毕竟没有网页源码,不能自动全局热重载,只能以这样 hack 的方式实现局部热重载。

    或者有没有这样一个说法,开发的时候直接把目标网页 clone 到本地,使其变成静态页面,然后脚本源文件一有变动直接刷新页面,如果是本地开发的话体验就很好了,随便全局刷新
    回复

    使用道具 举报

    该用户从未签到

    0

    主题

    5

    回帖

    4

    积分

    助理工程师

    积分
    4
    发表于 2023-12-23 21:39:53 | 显示全部楼层
    LikD__0812
    想合作
    回复

    使用道具 举报

    该用户从未签到

    3

    主题

    15

    回帖

    88

    积分

    初级工程师

    积分
    88
    发表于 2023-12-23 21:46:16 | 显示全部楼层

    合作什么呢?
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2024-1-27 11:40
  • 签到天数: 1 天

    [LV.1]初来乍到

    5

    主题

    14

    回帖

    37

    积分

    助理工程师

    积分
    37
    发表于 2024-1-29 13:31:01 | 显示全部楼层

    请教一下,如果我有多个脚本需要开发,那么能否通过配置实现,还是说只能创建不同的monkey项目,比如:QQ图片20240129132814.png

    main和main2是不同的脚本。我可以通过配置实现吗

    回复

    使用道具 举报

    该用户从未签到

    1

    主题

    4

    回帖

    93

    积分

    初级工程师

    积分
    93
    发表于 2024-2-19 00:05:04 | 显示全部楼层
    LinLin00 发表于 2023-12-23 15:33
    确实是这样,得自己主动控制让哪一部分带上 HMR 功能,但是指定之后可以给开发调试提供很大的便利,磨刀不 ...

    我的项目就是本地开发的,https://bbs.tampermonkey.net.cn/thread-2840-1-1.html
    模板:https://github.com/Eished/tampermonkey-template
    完整项目:https://github.com/Eished/jkforum_helper
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2024-3-16 12:24
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    0

    主题

    9

    回帖

    11

    积分

    助理工程师

    积分
    11

    新人报道油中2周年油中3周年喜迎中秋挑战者 lv1

    发表于 2024-3-6 16:03:20 | 显示全部楼层
    ggnb!!!
    回复

    使用道具 举报

    发表回复

    本版积分规则

    快速回复 返回顶部 返回列表