mini-vue实现之Observer类实现

351 阅读2分钟

mini-vue代码已放到github上。

Observer功能

  • 负责把data选项中的属性转化成响应式数据
  • data中的某个属性如果是对象,把该属性转换成响应式数据
  • 数据变化发送通知

同样我们这节实现前两个功能。

下面通过代码来实现Observer类

Observer实现

class Observer {
    constructor(data) {
        this.walk(data);
    }
    
    walk(data) {
        // 判断data是否为空,且是否是对象
        if(!data || typeof data !== 'object') {
            return
        }
        Object.keys(data).forEach(key => {
            // 为data对象中的属性转化成响应式数据
            this.defineReaction(data, key, data[key]);
        })
    }
    defineReaction(obj, key, value) {
        Object.defineProperty(obj, key, {
            enumerable: true,
            configurable: true,
            get() {
                return value
            },
            
            set() {
                if (newValue === value) {
                    return;
                }
                value = newValue;
            }
        })
    }
}
//vue.js
class Vue {
    constructor() {
        ...
        new observer(this.$data)
    }
}

我们在上述代码中的 getset 直接使用了value,是为什么呢。我们可以将value换成data[key],可以看下控制台输出。

WechatIMG380.png

可以看到报错栈溢出,原因就是在获取元素属性的时候,每次都会掉get方法,如果value改成obj[key]的话相当于一值再走get方法,所以形成了死循环。

obj[key]改回value我们测试下,此时控制台输出

WechatIMG381.png

可以看到现在data下的属性已经存在getset方法。

此时我们在data下添加一个person属性,现在data对象为

data: {
    msg: 'hello',
    count: 1,
    perser: {
        name: 'lzx'
    }
}

控制台输出为

WechatIMG382.png

可以看到现在person是有getset方法的,但是person下属性name是没有getset方法的,为了解决这一问题,其实很简单,就是在调用defineReaction方法的时候再调用一下walk方法,实现递归的处理。

defineReaction(obj, key, value) {
    this.walk(value)
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get() {
            return value
        },

        set() {
            if (newValue === value) {
                return;
            }
            value = newValue;
        }
    })
}

再测试下,可以看到问题已经解决

WechatIMG383.png

现在我们将vm的msg属性赋值成一个对象来看看结果

vm.msg = {text: 'hello'}

打开控制台来看下输出

WechatIMG384.png

可以看到现在msg对象下的属性并没有添加getset方法,这个问题怎么解决呢?同样我们只需要在set值的时候,重新调用下walk方法即可。

defineReaction(obj, key, value) {
    let that = this;
    this.walk(value)
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get() {
            return value
        },

        set() {
            if (newValue === value) {
                return;
            }
            value = newValue;
            that.walk(newValue)
        }
    })
}

这块需要注意下this指向问题,然后我们打开控制台看下输出

WechatIMG385.png

现在可以看到问题解决,我们observer类的基本功能已经实现,我们会在后面实现数据变化发送通知的功能。