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

Hook Vue3 app v1.0.3 [Vue3 app劫持 油猴库]

[复制链接]
  • TA的每日心情
    慵懒
    1 小时前
  • 签到天数: 97 天

    [LV.6]常住居民II

    9

    主题

    220

    帖子

    537

    积分

    荣誉开发者

    Rank: 10Rank: 10Rank: 10

    积分
    537

    新人进步奖荣誉开发者喜迎中秋

    发表于 2022-8-12 23:26:46 | 显示全部楼层 | 阅读模式

    如何使用? 库问题反馈 给库评分 查看代码

    本帖最后由 steven026 于 2022-8-14 23:10 编辑

    【前言警告】

    本脚本库作者完全没有接触过Vue2及Vue3正向开发,也不清楚其具体正向原理(脚本原理是倒推的)
    本脚本库是作者根据自身JS经验及油猴中文网脚本开发指南加上断点逆向无意中发现的通用劫持方法而写,
    局限于作者水平,可能存在诸多不合理之处,尽请谅解,另外欢迎交流、探讨。


    【脚本原理】

    根据Vue数据绑定机制,Vue为了保证数据同步,会对每个实例均创建一个app对象用于数据存储、监听、同步;
    一旦该app对象中存储的变量发生变化,Vue便会监听到该变化并将其作用到相应的DOM元素中,以实现前端交互。
    该app对象存在于闭包中,Vue2将其挂载在DOM元素中的__vue__属性中,可令油猴脚本直接获取;
    但Vue3并未提供该方法,这导致通常油猴脚本无法直接获取app对象,加大针对Vue3框架的油猴脚本的开发难度。

    本脚本库根据Vue3的数据绑定原理,通过劫持Proxy方法以暴露Vue3 app对象,并将其还原挂载到DOM元素中,
    进而使油猴脚本可直接访问、操作该app对象,以实现油猴脚本与Vue3的快速交互。


    【使用方法】

    全局变量

    vueHooked
    [object WeakMap] 以WeakMap存储已劫持的app对象,DOM元素为key,app对象为value
    (同一个元素可能有多个app对象,以数组形式存储,下同)

    vueUnhooked
    [object WeakSet] 以WeakSet存储已获取到但未未劫持的app对象,作为debug用变量,正常情况WeakSet应为空

    DOM元素属性

    DOMelement.__vue__
    [object Object] 已挂载到对应DOM元素属性的Vue app对象
    (类似于Vue2的DOMelement.__vue__)


    【使用示例】

    必要元信息(脚本猫,注意版本号,示例可能会没有更新)
    // @run-at       document-start
    // @require      https://scriptcat.org/lib/567/1.0.3/Hook%20Vue3%20app.js
    必要元信息(greasyfork)
    // @run-at       document-start
    // @require      https://greasyfork.org/scripts/449444-hook-vue3-app/code/Hook%20Vue3%20app.js
    脚本示例(以Bilibili为例 下同)
    // ==UserScript==
    // @name         Bilibile VUE3
    // @run-at       document-start
    // @match        https://www.bilibili.com/*
    // @require      https://scriptcat.org/lib/567/1.0.3/Hook%20Vue3%20app.js
    // @grant        none
    // ==/UserScript==
    
    //暴露变量到全局
    window._vueUnhooked_ = vueUnhooked;
    window._vueHooked_ = vueHooked;
    
    //等待元素加载
    let timer = setInterval(() => {
        let app = document.querySelector(".bili-video-card")
        if (app?.__vue__) {
            clearInterval(timer)
            console.log("已加载元素", app)
        }
    })
    实际应用
    遍历app

    遍历app.png

    获取元信息

    获取元信息.png

    数据替换(仅演示可行性)

    数据替换.png

    进阶应用:通过vue获取函数、内置方法 (自行研究,各页面均不相同)

    【参考文献】

    @李恒道 哥哥的指导与Vue2指南:

    [油猴脚本开发指南]VUE数据绑定的响应原理
    [油猴脚本开发指南]Vue初探vue
    [油猴脚本开发指南]通过vue获取数据

    @cxxjackie 哥哥的闭包劫持方法指导

    油猴劫持闭包方法

  • TA的每日心情
    开心
    昨天 13:55
  • 签到天数: 100 天

    [LV.6]常住居民II

    171

    主题

    2253

    帖子

    2299

    积分

    管理员

    Rank: 10Rank: 10Rank: 10

    积分
    2299

    荣誉开发者喜迎中秋热心会员活跃会员突出贡献三好学生管理员家财万贯

    发表于 2022-8-13 10:54:39 | 显示全部楼层
    上不慕古,下不肖俗。为疏为懒,不敢为狂。为拙为愚,不敢为恶。/ 微信公众号:一之哥哥
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    1 小时前
  • 签到天数: 97 天

    [LV.6]常住居民II

    9

    主题

    220

    帖子

    537

    积分

    荣誉开发者

    Rank: 10Rank: 10Rank: 10

    积分
    537

    新人进步奖荣誉开发者喜迎中秋

    发表于 2022-8-13 11:41:19 | 显示全部楼层
    本帖最后由 steven026 于 2022-8-13 11:47 编辑
    王一之 发表于 2022-8-13 10:54
    哥哥nb!

    找了几个vue3的demo测试,好像有的可以 有的不行

    h5.png
    第二个页面我试了下可以的,但是脚本自动还原的比较少,可能有点bug,控制台试了下能手动还原,我再研究一下
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    1 小时前
  • 签到天数: 97 天

    [LV.6]常住居民II

    9

    主题

    220

    帖子

    537

    积分

    荣誉开发者

    Rank: 10Rank: 10Rank: 10

    积分
    537

    新人进步奖荣誉开发者喜迎中秋

    发表于 2022-8-13 12:12:33 | 显示全部楼层
    王一之 发表于 2022-8-13 10:54
    哥哥nb!

    找了几个vue3的demo测试,好像有的可以 有的不行

    h5.png

    又更新了一下,哥哥看看,之前写的代码有点BUG,现在基本都能还原了
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2022-3-8 11:41
  • 签到天数: 2 天

    [LV.1]初来乍到

    15

    主题

    479

    帖子

    836

    积分

    荣誉开发者

    Rank: 10Rank: 10Rank: 10

    积分
    836

    卓越贡献活跃会员热心会员突出贡献三好学生荣誉开发者喜迎中秋

    发表于 2022-8-13 12:33:22 | 显示全部楼层
    我感觉vueHooked这个对象没有意义,只要遍历一下vueApp,谁没有el谁就没被还原吧,没必要专门弄一个对象。而且Vue中是存在同一个元素对应多个app的情况的,这样就会出现你以为他还原了,但又被另一个覆盖的情况,这样到底算不算“还原”呢?我觉得可以放弃在元素上附加额外属性的做法,改用一个WeakMap来记录,就是 元素->app数组 这样的对应关系,查询起来更方便,还可以应用到Vue2(不过Vue2是Object.defineProperty绑定的,需要改一下劫持方式)。
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    1 小时前
  • 签到天数: 97 天

    [LV.6]常住居民II

    9

    主题

    220

    帖子

    537

    积分

    荣誉开发者

    Rank: 10Rank: 10Rank: 10

    积分
    537

    新人进步奖荣誉开发者喜迎中秋

    发表于 2022-8-13 13:34:19 | 显示全部楼层
    cxxjackie 发表于 2022-8-13 12:33
    我感觉vueHooked这个对象没有意义,只要遍历一下vueApp,谁没有el谁就没被还原吧,没必要专门弄一个对象。 ...

    vueHooked其实算是一个debug用的对象

    同一个元素多个app的情况,我原本是想做成和recordVue()一样,如果el已存在__vue__则把这个__vue__变成数组形式,但感觉还是有点问题,所以暂时没处理
    我觉得element.__vue__操作起来比较方便,比较符合vue2的习惯
    如果改成WeakMap形式,WeakMap.get(element),总觉得有点别扭

    不知道vue2同一元素多个app的__vue__是怎么个处理情况,我似乎记得就是像我现在一样摆烂……

    总结一下,现在有4种解决办法
    1.摆烂,__vue__只赋值,不管覆不覆盖
    2.保留__vue__,如果存在覆盖则转为数组
    3.放弃__vue__,改为WeakMap
    4.既保留__vue__数组,也新增一个WeakMap,让用户自由选择(这块内存应该占用不大吧 引用的实际上是同一个已存在的对象)
    不知道哥哥怎么看?
    回复

    使用道具 举报

  • TA的每日心情
    开心
    前天 23:59
  • 签到天数: 88 天

    [LV.6]常住居民II

    386

    主题

    3405

    帖子

    3388

    积分

    管理员

    非物质文化遗产社会摇传承人

    Rank: 10Rank: 10Rank: 10

    积分
    3388

    喜迎中秋国庆纪念章荣誉开发者家财万贯管理员

    发表于 2022-8-13 14:48:57 | 显示全部楼层
    哥哥牛逼!
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2022-3-8 11:41
  • 签到天数: 2 天

    [LV.1]初来乍到

    15

    主题

    479

    帖子

    836

    积分

    荣誉开发者

    Rank: 10Rank: 10Rank: 10

    积分
    836

    卓越贡献活跃会员热心会员突出贡献三好学生荣誉开发者喜迎中秋

    发表于 2022-8-13 21:29:47 | 显示全部楼层
    steven026 发表于 2022-8-13 13:34
    vueHooked其实算是一个debug用的对象

    同一个元素多个app的情况,我原本是想做成和recordVue()一样,如果 ...

    这个问题主要是有性能方面的隐患,由于Vue组件存在指向元素的引用,即使元素已从页面删除,只要引用存在,元素就会一直留在内存中。在元素上绑属性的做法有一个问题,就是元素属性引用自身,即循环引用,这容易造成内存泄露,所以Vue2在销毁组件时会将__vue__置空。用对象来保存组件同样存在问题,永远有指向元素的引用,在增删改动频繁的页面中可能引发性能问题。WeakMap的做法我想了一下也不妥当,虽然键名是弱引用,但键值还是强引用,你可能不得不去监听Vue的销毁流程(Vue3我记得是unmount),把相关引用断开才行。
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    1 小时前
  • 签到天数: 97 天

    [LV.6]常住居民II

    9

    主题

    220

    帖子

    537

    积分

    荣誉开发者

    Rank: 10Rank: 10Rank: 10

    积分
    537

    新人进步奖荣誉开发者喜迎中秋

    发表于 2022-8-13 21:48:17 | 显示全部楼层
    cxxjackie 发表于 2022-8-13 21:29
    这个问题主要是有性能方面的隐患,由于Vue组件存在指向元素的引用,即使元素已从页面删除,只要引用存在 ...

    Vue3的app有个属性是isUnmounted: false
    我用Object.defineProperty去监听这个变化吗?如果变为true就把__vue__和vueApp、vueHooked置空

    但是Object.defineProperty这个监听过程本身又无法销毁(百度查了下似乎Proxy的监听可销毁?但我随便试了一下并未成功),是否陷入了死循环?或者算是节约一部分内存?
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2022-3-8 11:41
  • 签到天数: 2 天

    [LV.1]初来乍到

    15

    主题

    479

    帖子

    836

    积分

    荣誉开发者

    Rank: 10Rank: 10Rank: 10

    积分
    836

    卓越贡献活跃会员热心会员突出贡献三好学生荣誉开发者喜迎中秋

    发表于 2022-8-13 22:39:24 | 显示全部楼层
    steven026 发表于 2022-8-13 21:48
    Vue3的app有个属性是isUnmounted: false
    我用Object.defineProperty去监听这个变化吗?如果变为true就把_ ...

    我测试了一下,好像浏览器有对这个问题进行优化,循环引用的资源是能被正确释放的(主要是IE里有问题),那__vue__和WeakMap的做法应该都没问题,但用对象保存肯定是不靠谱的。Vue应该有专门的方法可以劫持,好像是beforeUnmount和unmounted,直接监听属性也行,Object.defineProperty不会影响引用计数的,但我还是不推荐用对象,WeakMap虽说不可枚举,但实际在chrome控制台中是可以展开的,也能起到查看所有组件的效果。
    回复

    使用道具 举报

    发表回复

    本版积分规则

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