通过劫持Proxy方法,逆向还原Vue3 app元素到DOM
Version vom
Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greasyfork.org/scripts/449444/1081399/Hook%20Vue3%20app.js
// ==UserScript==
// @name Hook Vue3 app
// @version 1.0.2
// @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
const realProxy = $window.Proxy; //劫持Proxy
var vueApp = {} //存储所有app
var vueHooked = {} //存储已还原app
$window.Proxy = function () {
let app = arguments[0]._
if (app && app.uid >= 0) { //判断app
let el = app.vnode.el
if (el) {
if (!el.__vue__) {
el.__vue__ = app //存在el则还原__vue__
if (el.__vue__) {
recordVue(vueHooked, app)
}
}
} else {
//realLog(app,el)
watchEl(app.vnode) //不存在el则进行观察
}
recordVue(vueApp, app)
}
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) {
this.el.__vue__ = this.component
if (this.el.__vue__) {
hooked = true
recordVue(vueHooked, this.component)
//realLog(this.component,"已还原")
}
}
}
})
}
function recordVue(obj, app) { //以uid为key存储app
if (!obj[app.uid]) { //uid不存在则直接存储
obj[app.uid] = app
} else if (Array.isArray(obj[app.uid])) {
obj[app.uid].push(app)
} else {
obj[app.uid] = [obj[app.uid], app] //多个root相同uid时以数组形式存储 可能存在优化空间
}
}