对于这个简易的 Vue 还有一些问题,比如在实现 v-model 的时候我们还没有实现在输入的同时改变绑定的 data 属性。
双向绑定
这里对于输入框的绑定很简单,监听他的 input 事件,然后修改 data 中的属性为输入框中的值即可:
modelUpdater (node, value, key) {
...
node.addEventListener('input', () => {
this.vm[key] = node.value
})
}
给 Vue 实例新增一个成员不是响应式的
在 Vue 类中,给已有的属性重新赋值,那么重新赋值后的属性还是响应式的,这是因为我们给 $data 中属性的 setter 里又执行了 walk 方法。但是直接给 Vue 实例新增一个成员,那么新增的成员不是响应式的。
在我们这个简单的模拟示例中没有给出这个问题的解决方案,但是在 Vue 中,是提出了解决方案的,
使用 Vue.set 或者 vm.$set 方法就可以让新增的成员是响应式的。
data中属性的死循环
我在自己重写一遍代码的过程中,发现最容易错的地方就是在 defineReactive 中将 data 属性转换为 getter/setter 时,直接使用了 data[key],从而导致了死循环。
defineReactive(data, key) {
let value = data[key]
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
// get/set 中都要用 value,而不是 data[key],这里用 data[key] 会造成死循环
get() {
return value
},
set(newValue) {
if (newValue === value) return
value = newValue
}
})
}
由于 data[key] 的 get/set 中用到了 value,所以 defineReactive 就形成了闭包,其中的 value 一直没被销毁,获取 data[key] 的时候直接取了闭包中的 value 值,修改的时候也是直接修改了闭包中 value 的值,所以可以达成修改 value 的值但是作用于 data[key] 的效果。