Hook Vue3 app

通过劫持Proxy方法,逆向还原Vue3 app元素到DOM

Αυτός ο κώδικας δεν πρέπει να εγκατασταθεί άμεσα. Είναι μια βιβλιοθήκη για άλλους κώδικες που περιλαμβάνεται μέσω της οδηγίας meta // @require https://update.greasyfork.org/scripts/449444/1081400/Hook%20Vue3%20app.js

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name         Hook Vue3 app
// @version      1.0.3
// @description  通过劫持Proxy方法,逆向还原Vue3 app元素到DOM
// @author       DreamNya
// @license      MIT
// @namespace https://greasyfork.org/users/809466
// ==/UserScript==

const $window = window.unsafeWindow || document.defaultView || window
const realLog = $window.console.log; //反劫持console.log(大部分网站都会劫持console.log)
const realProxy = $window.Proxy; //劫持Proxy

var vueUnhooked = new WeakSet() //以WeakSet存储已获取到但未未劫持的app对象,作为debug用变量,正常情况WeakSet应为空
var vueHooked = new WeakMap() //以WeakMap存储已劫持的app对象,DOM元素为key,app对象为value

$window.Proxy = function () {
    let app = arguments[0]._
    if (app?.uid >= 0) { //判断app
        let el = app.vnode.el
        if (el) {
            recordVue(el, app) //记录到WeakMap
            recordDOM(el, app) //挂载到DOM
            watch_isUnmounted(app) //观察销毁
        } else {
            //realLog(app,el)
            vueUnhooked.add(app) //记录未劫持的app
            //realLog(vueUnhooked,app)
            watchEl(app.vnode) //不存在el则观察el
        }
    }
    return new realProxy(...arguments)
}

function watchEl(vnode) { //观察el 变动时还原到DOM
    let value = vnode.el
    let hooked = false
    Object.defineProperty(vnode, "el", {
        get() {
            return value
        },
        set(newValue) {
            value = newValue
            if (!hooked && this.el) {
                hooked = true
                recordVue(this.el, this.component)
                recordDOM(this.el, this.component)
                watch_isUnmounted(this.component)
                //realLog(this.component,"已还原")
            }
        }
    })
}

function watch_isUnmounted(app) { //观察isUnmounted 变动时销毁引用
    let value = app.isUnmounted
    let unhooked = false
    Object.defineProperty(app, "isUnmounted", {
        get() {
            return value
        },
        set(newValue) {
            value = newValue
            if (!unhooked && this.isUnmounted) {
                unhooked = true
                //realLog(this,"已删除")
                let el = this.vnode.el
                if (el) {
                    let DOMvalue = el.__vue__ //删除DOMelement.__vue__挂载
                    if (DOMvalue) {
                        if (Array.isArray(DOMvalue)) {
                            let index = DOMvalue.findIndex(i => i == this)
                            index > -1 && DOMvalue.splice(index, 1)
                            el.__vue__ = DOMvalue.length > 1 ? DOMvalue : DOMvalue[0]
                        } else {
                            if (DOMvalue == this) {
                                el.__vue__ = void 0
                            }
                        }
                    }
                    let WMvalue = vueHooked.get(el) //删除WeakMap存储
                    if (WMvalue) {
                        if (Array.isArray(WMvalue)) {
                            let index = WMvalue.findIndex(i => i == this)
                            index > -1 && WMvalue.splice(index, 1)
                            vueHooked.set(el, WMvalue.length > 1 ? WMvalue : WMvalue[0])
                        } else {
                            if (WMvalue == this) {
                                vueHooked.delete(el)
                            }
                        }
                    }
                }
            }
        }
    })
}

function recordVue(el, app) { //将app记录到WeakMap中
    vueUnhooked.delete(app)
    if (vueHooked.has(el)) {
        let value = vueHooked.get(el)
        if (Array.isArray(value)) {
            if (value.findIndex(i => i == app) == -1) {
                vueHooked.set(el, vueHooked.get(el).push(app))
            }
        } else {
            if (value != app) {
                vueHooked.set(el, [value, app])
            }
        }
    } else {
        vueHooked.set(el, app)
    }
}

function recordDOM(el, app) { //将app挂载到DOMelement.__vue__
    if (el.__vue__) {
        let value = el.__vue__
        if (Array.isArray(value)) {
            if (value.findIndex(i => i == app) == -1) {
                el.__vue__ = value.push(app)
            }
        } else {
            if (value != app) {
                el.__vue__ = [value, app]
            }
        }
    } else {
        el.__vue__ = app
    }
}