vue2.0 双向数据绑定的局限性
我们知道,vue2.0内部是通过 Object.defineProperty 来进行数据劫持等操作的,但也正因为这样,所以会存在一些局限性:
1.如果属性不存在,默认后增加的数据不会更新到视图
我们可以看以下代码:
<div id="app">
{{info.name}} {{info.age}}
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
msg: 'emmmmmmm',
info:{
name:'cy'
}
}
})
vm.info.age=18
</script>
这段代码的运行结果是cy,并没有出现cy 18,这也说明了,如果对没有在data钩子声明了的属性赋值,不会同步更新到视图上面去。
2.数组不能通过长度修改,也不能通过数组的索引修改
我们可以看以下代码:
<div id="app">
{{arr}}
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
msg: 'emmmmmmm',
arr: [1, 2, 3]
}
})
vm.arr.length--
</script>
这段代码的运行结果是[1,2,3],并没有出现我们预期的[1,2],这也说明了数组是不能通过长度进行修改,并且更新到视图。同理,也不能通过数组的索引进行修改,这里就不演示了。
双向数据绑定的局限性原因
那么,造成这种局限性的原因是什么呢,下面我们通过一个模仿vue源码内部实现的简单demo来研究下:
//数据劫持的实现
function observer(obj) {
if (typeof obj == 'object') {
for (let key in obj) {
defineReactive(obj, key, obj[key])
}
}
}
function defineReactive(obj, key, value) {
observer(value)//判断value是不是一个对象 如果是对象 就递归 实现深度监控
Object.defineProperty(obj, key, {
get() {
return value
},
set(newVal) {
observer(newVal)//如果重新给数据绑定一个新的对象 也要监控到新对象的属性
console.log('数据更新了 劫持到了')
value = newVal
}
})
}
通过代码我们可以看到,vue内部实质上是对data挂载的对象进行了一次深度遍历,然后给每个属性都通过 Object.defineProperty() 进行改造,最后实现数据劫持等操作。
这就可以很好的解释我们的第一种局限性:如果我们给data钩子里面不存在的属性进行赋值的时候,实质上没有通过defineReactive()方法对这个新增的属性进行改造,让他拥有数据劫持等的能力,所以它不会更新到视图层。
Object.defineProperty不能对数组进行操作
到这里,很多小伙伴会疑惑,Object.defineProperty不能对数组进行操作,那么vue内部是如果对数组进行数据劫持的呢。其实很简单,vue内部通过重写所有数组的方法来进行数据劫持,这是我实现的一个类似的小demo:
//vue里面把所有的数组方法都重写了
let arr=['push','slice','shift']
arr.forEach(method=>{
let oldMethod=Array.prototype[method]
Array.prototype[method]=function(value){
console.log('通过改写数组的api实现数组的数据劫持')
oldMethod.call(this,value)
}
})
既然我们了解了vue内部对数组的相关操作,我们也可以很好的理解为什么会出现第二种局限性了:因为vue内部只是对数组的所有方法进行重写数据劫持,但是对数组的长度和索引的变化没有进行相应的操作,所以才会出现这种局限性。
这个也是之前开发时候遇到的一些小坑,不过后来也通过一些如$set等来解决了
这是我第一次写文章,虽然涉及的比较简单,但是文笔和表述还是不太清楚,大家见谅一哈。后续会写一下我对vuex、vue-router、vue-cli、ssr、pwa、jwt、vue测试、权限控制等的源码实现或使用的见解,如果感兴趣的小伙伴可以关注下哦。