解释:
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)return
data.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)return
value = newValue
}
})
}
不能检测数组和对象的变化
但由于 JavaScript 的限制,Vue不能检测数组和对象的变化
对于对象
可以使用:Vue.set(object,propertyName,value)
或vm.$set(object,propertyName,value)
const vm = new Vue({
data:{
obj:{ //如果data里面只有一层,没有obj,那Vue就会检测到b的问题,会报出一个Vue warn
a: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完成同样的事情