美文网首页
响应式框架原理1(数据劫持与代理)

响应式框架原理1(数据劫持与代理)

作者: nomooo | 来源:发表于2020-03-02 11:02 被阅读0次
    • 感知数据变化的方法很直接,就是进行数据劫持或数据代理。往往通过 Object.defineProperty 实现。这个方法可以定义数据的 getter 和 setter
                let data = {
                    stage: '状态',
                    course: {
                        title: '标题',
                        author: '作者',
                        publishTime: '出版时间'
                    }
                }
                Object.keys(data).forEach(key => {
                    let currentValue = data[key]
                    // Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。
                    Object.defineProperty(data, key, {
                        // 当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false
                        enumerable: true,
                        // 当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
                        configurable: false,
                        get() {
                            console.log(`getting ${key} value now, getting value is:`, currentValue)
                            return currentValue
                        },
                        set(newValue) {
                            currentValue = newValue
                            console.log(`setting ${key} value now, setting value is`, currentValue)
                        }
                    })
                })
    

    这段代码对 data 数据的 getter 和 setter 进行定义拦截,当读取或者改变 data 的值时:

                data.course
    
                data.course = 'Test'!
    
    

    但是这种实现会有问题,例如:

    data.course.title = 'newTitle'
    

    出现这个问题的原因是因为实现代码只进行了一层 Object.defineProperty,或者说只对 data 的第一层属性进行了 Object.defineProperty,对于嵌套的引用类型数据结构:data.course,同样应该进行拦截。

    为了达到深层拦截的目的,将 Object.defineProperty 的逻辑抽象为 observe 函数,并改用递归实现:

                let data = {
                    stage: '状态',
                    course: {
                        title: '标题',
                        author: '作者',
                        publishTime: '出版时间'
                    }
                }
    
                const observe = data => {
                    if (!data || typeof data !== 'object') {
                        return
                    }
                    Object.keys(data).forEach(key => {
                        let currentValue = data[key]
    
                        observe(currentValue)
                        Object.defineProperty(data, key, {
                            enumerable: true,
                            configurable: false,
                            get() {
                                console.log(`getting ${key} value now, getting value is: `, currentValue)
                                return currentValue
                            },
                            set(newValue) {
                                currentValue = newValue;
                                console.log(`setting ${key} value now, setting value is: `, currentValue)
                            }
                        })
                    })
                }
                observe(data)
    

    这样就实现了深层数据拦截:

    data.course.title = 'newTitle'
    

    在 set 代理中,并没有对 newValue 再次递归进行 observe(newValue)。也就是说,如果赋值是一个引用类型:

                data.course.title = {
                    title: 'newTitle2'
                }
    

    无法实现对 data.course.title 数据的观察。

    在尝试对 data.course.title 赋值时,首先会读取 data.course,因此输出:getting course value now, getting value is: {// ...},赋值后,触发 data.course.title 的 setter,输出:setting title value now, setting value is newTitle。

    相关文章

      网友评论

          本文标题:响应式框架原理1(数据劫持与代理)

          本文链接:https://www.haomeiwen.com/subject/jbfpkhtx.html