LinLin00 发表于 2023-12-23 01:02:38

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

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

此前,我们已经有了 (https://github.com/LinLin00000000/usbuild) 这样一个开发工具,它拥有开发模式实时热重载的功能。

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

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

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

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

![演示动画.gif](data/attachment/forum/202312/23/005410od2fqui2ddcuuniz.gif)
(竟然只支持 1MB 以下的图片,只能压缩得非常糊)

原理其实很简单,虽然每次都会重新安装脚本,但是只要在 unsafeWindow 上存储一个 cache 就行了,每次运行的时候先找有没有 cache,有就先恢复元素最初的样子,然后再执行用户自定义的 callback。其实就是简单封装一下 (https://bbs.tampermonkey.net.cn/thread-5561-1-1.html)

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

```JavaScript
const selectorHMR = (selector, callback, options) => {
    const CACHE_NAME = '_usbuild_selectorHMR'
    if (!unsafeWindow) {
      unsafeWindow = new Map()
    }
    const cacheMap = unsafeWindow
    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
}
```

王一之 发表于 2023-12-23 09:56:39

ggnb!

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

或者还有些什么场景呢?不过代码值得学习!

LinLin00 发表于 2023-12-23 12:01:50

王一之 发表于 2023-12-23 09:56
ggnb!

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


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

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

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

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

王一之 发表于 2023-12-23 13:59:48

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

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

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

临时debug应该很好用

LinLin00 发表于 2023-12-23 15:33:04

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

王一之 发表于 2023-12-23 13:59
我的手动控制的意思是:我要用selectorHMR这个方法指定局部,才能使用这个局部热重载功能

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

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

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

LiuYS 发表于 2023-12-23 21:39:53

LikD__0812
想合作

LinLin00 发表于 2023-12-23 21:46:16

LiuYS 发表于 2023-12-23 21:39
LikD__0812
想合作

合作什么呢?

uuorz 发表于 2024-1-29 13:31:01

请教一下,如果我有多个脚本需要开发,那么能否通过配置实现,还是说只能创建不同的monkey项目,比如:!(data/attachment/forum/202401/29/133009tu0771sflwwqq7fo.png)

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

Kished 发表于 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

paul_ 发表于 2024-3-6 16:03:20

ggnb!!!
页: [1]
查看完整版本: 一种很新的脚本开发方式——Selector HMR ?!