解释:
Vue数据响应式就是当访问和修改数据时,视图会进行相应的更新
原理:
在生成Vue实例时,Vue会遍历属性,使用Object.defineProperty(),将这些属性转化为getter/setter,使得Vue可以追踪这些属性,当属性被访问和修改时通知视图变更。
代码模拟:
下面代码从对象属性的修改限制上简单模拟Vue的响应式部分原理
let newData = myProxy({data:{n:0}})function myProxy({data}){const proxyObj = {}Object.defineProperty(proxyObj, 'n', {get(){return data.n},set(value){if(value<0)returndata.n = value}})return proxyObj}
上面这种方法,
对于传入参数是一个匿名对象的情况下,通过代理对属性修改的限制是可行的。
但是如果传入一个对象的引用,那么用户可对该传入对象的直接进行修改,绕过代理对象,那么对属性修改的限制就失效了。
比较可行的应该是使用Object.definedProperty()代理原对象,这样对原对象的修改也绕不过代理了
let oldData = {n:0}let newData=myProxy({data:oldData})function myProxy({data}){let value = data.n;return Object.defineProperty(data, 'n', {get(){return value},set(newValue){if(newValue<0)returnvalue = newValue}})}
不能检测数组和对象的变化
但由于 JavaScript 的限制,Vue不能检测数组和对象的变化
对于对象
可以使用:Vue.set(object,propertyName,value)
或vm.$set(object,propertyName,value)
const vm = new Vue({data:{obj:{ //如果data里面只有一层,没有obj,那Vue就会检测到b的问题,会报出一个Vue warna:1}},template : `<div>{{obj.a}}{{obj.b}}<button @click ='setA'>set a</button><button @click ='setB2'>set b2</button><button @click ='setB'>set b</button></div>`,methods:{setA() {this.obj.a += 1; //点击set a,修改a的值, a的值就从1变为2,触发视图渲染},setB2(){this.obj.b = 2; //点击按钮,添加obj的属性b,这种方法并不会触发视图重新渲染,所以2并不会出现在视图里},setB(){Vue.set(this.obj,'b',2)//应该使用Vue提供的这种方法添加对象或对象属性}}})//添加b,使其变成响应式的方法也可以如下Vue.set(vm.obj,'b',2)//或vm.$set(vm.obj,'b',2)//如果先点击set b2 ,再点击 set a 那么obj.b的值2就会因为obj.a改变而触发的视图重新渲染而出现在页面上
对于数组
可以用Vue.set(object,propertyName,value)
或 vm.$set(object,propertyName,value)
也可以使用它的七个变异方法:
push()pop()shift()unshift()splice()sort()reverse()```javascript
var vm = new Vue({
el: ‘#app’,
data:{
arr:[1,2,3,4]
},
template : <div>
<div>{{arr}}</div>
<button @click ='setArr'>set</button>
</div>,
methods:{
setArr(){
this.arr.push(5,6,7)
console.log(this.arr)
}
}
})
声明响应式property
由于 Vue 不允许动态添加根级响应式 property,所以必须在初始化实例前声明所有根级响应式 property,哪怕只是一个空值
**
异步更新队列
Vue 在更新 DOM 时是异步执行的,所以更改data,组件并不会立刻重新渲染,如果此时想对更新后的DOM状态做些什么就需要用到Vue.nextTick(callback),这样回调函数会在DOM更新完成后被调用。
组件内部可以使用vm.$nextTick(callback),这方法不需要全局Vue,并且回调函数里的this自动绑定到当前Vue实例上。
因为$nextTick()方法返回一个Promise对象,所以可以使用 ES2017 async/await完成同样的事情
