温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何打印Proxy对象和ref对象的包

发布时间:2022-11-18 10:01:00 来源:亿速云 阅读:127 作者:iii 栏目:开发技术

本文小编为大家详细介绍“如何打印Proxy对象和ref对象的包”,内容详细,步骤清晰,细节处理妥当,希望这篇“如何打印Proxy对象和ref对象的包”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

目标

我希望新的console.log可以像现在的console.log一模一样,只是当打印Proxyref对象时可以直接输出它的源对象或ref.value。并且,还保留记录当前文件和行数的功能,可以让我看到到底是哪个文件哪个步骤执行的打印。

结果

如何打印Proxy对象和ref对象的包

但退而求其次,我用console.traceError.stack两种方式十分简陋的完成了这个目标。

如何打印Proxy对象和ref对象的包

实现(直接看源码的同学可以略过)

判断一个对象是否是Proxy

这个不好判断,Vue3添加了isProxy 方法,但如果不是Vue环境的话,那这个方法就失效了。 而且就这么一个简单的小功能,实在没必要依赖其他的包。 最终是选择在用户new Proxy之前,把Proxy对象改造。

// 记录用户new Proxy操作的所有对象
// WeakSet,WeakMap,都是弱引用,不干预其他模块的垃圾回收机制
export const proxyMap = new WeakMap()
let OriginalProxy = null
export function listenProxy() {
    if (OriginalProxy) { // 防止用户多次调用监听
        return
    }
    OriginalProxy = window.Proxy
    window.Proxy = new Proxy(Proxy, {
        construct(target, args) {
            const newProxy = new OriginalProxy(...args)
            proxyInstances.set(newProxy, target)
            return newProxy
        },
        get(obj, prop) {
            // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/hasInstance
            if (prop === Symbol.hasInstance) { // 监控 `instanceof` 关键字
                return instance => proxyMap.has(instance)
            }
            // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/get
            return Reflect.get(...arguments)
        }
    })
}
export function unListenProxy() {
    window.Proxy = OriginalProxy || window.Proxy
}

输出用户log的源对象

按说我们上一步已经监控了用户动作,可以获取源对象,等用户log的时候,我们直接输出源对象就可以了。但这也有个问题,Proxy毕竟不是普通的对象,通过Proxy获取的结果,很可能跟源对象没有一毛钱关系。所以只能通过深克隆返回源对象的值,但这也有个问题,就是对于某些不能遍历的对象或属性,就打印不了了……

问题貌似锁死了,但,我们实际运用中,只是为了简简单单输出一个不用展开的源对象而已,甚至运用场景都特别单一:Vue3! 用户如果觉得打印的不准确,换一个api不完了吗,比如我们监控的是console.log,那用户就用console.info一样能输出相同的结果。 把选择权交给用户就好了。在引用包的时候,再写多一个配置项,让用户自己选平时的使用场景哪个正确结果比较多,就选哪个。想要完全正确,就换一个其他的api。

我简直是个天才,哈哈哈

export function getOrg(obj) {
    return proxyMap.get(obj)
}
// 深克隆
export function clone(obj, _refs = new WeakSet()) {
    if (obj === null || obj === undefined) return null
    if (typeof obj !== 'object') return obj
    if (obj.constructor === Date) return new Date(obj)
    if (obj.constructor === RegExp) return new RegExp(obj)
    const newObj = new obj.constructor() //保持继承的原型
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            const val = obj[key]
            if (typeof val === 'object' && !_refs.has(val)) {
                newObj[key] = clone(val)
            } else {
                newObj[key] = val
            }
        }
    }
    return newObj
}

最后暴露出去给用户调用

import { listenProxy, unListenProxy, clone, getOrg } from "./until";
let config = {
    key: 'log', // any String
    type: 'trace', // 'trace' | 'error' | 'any String'
    cloneProxy: getOrg
}
let Vue = {}
export default function (obj = {}, vue) {
    Vue = vue || {}
    config = { ...config, ...obj }
    if (obj.copy === 'clone') {
        config.cloneProxy = clone
    }
    listenLog(config)
}
// ----------------------------------------
const { groupCollapsed, groupEnd, trace, log } = console
// const type = 'trace' | 'error' | ''
function listenLog() {
    const isRef = Vue.isRef || (obj => {
        return typeof obj === 'object' && !!obj.constructor && obj.constructor.name === 'RefImpl'
    })
    const unref = Vue.unref || (obj => obj.value)
    const { key, type, cloneProxy } = config
    if (!key) {
        console.error('Missing required parameter: key')
    }
    listenProxy() // 为 new Proxy 对象添加 `instanceof` 支持
    console[key] = function (...arr) {
        const newArr = arr.map(i => {
            if (isRef(i)) {
                return unref(i)
            } else if (i instanceof Proxy) {
                return cloneProxy(i)
            } else {
                return i
            }
        })
        groupCollapsed(...newArr)
        // 以 trace
        if (type === 'trace') {
            // trace(...newArr)
            console.log('第二行即为调用者所在的文件位置')
            trace('The second line is the file location of the caller')
            groupEnd()
            return
        }
        let stack = new Error().stack || ''
        // stack = stack.replace('Error', 'Log')
        if (type === 'error') {
            log('%c这不是一个错误,请点击第二行的"at",跳转到对应的文件', 'color: #008000')
            log('%cThis is not an error. Please click "at" in the second line to jump to the corresponding file', 'color: #008000')
            log(stack)
            groupEnd()
            return;
        }
        // 简单输入模式,控制台看起来是简单了,却失去了点击链接直接跳转到对应文件的功能
        const stackArr = stack.match(/at.*\s/g) || []
        log(stackArr[1])
        groupEnd()
    }
}

再加上一点ts的解释文件,那这个库就能运行在所有平台了

读到这里,这篇“如何打印Proxy对象和ref对象的包”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI