解释:

Vue数据响应式就是当访问和修改数据时,视图会进行相应的更新

原理:

在生成Vue实例时,Vue会遍历属性,使用Object.defineProperty(),将这些属性转化为getter/setter,使得Vue可以追踪这些属性,当属性被访问和修改时通知视图变更。

代码模拟:

下面代码从对象属性的修改限制上简单模拟Vue的响应式部分原理

  1. let newData = myProxy({data:{n:0}})
  2. function myProxy({data}){
  3. const proxyObj = {}
  4. Object.defineProperty(proxyObj, 'n', {
  5. get(){
  6. return data.n
  7. },
  8. set(value){
  9. if(value<0)return
  10. data.n = value
  11. }
  12. })
  13. return proxyObj
  14. }

上面这种方法,
对于传入参数是一个匿名对象的情况下,通过代理对属性修改的限制是可行的。
但是如果传入一个对象的引用,那么用户可对该传入对象的直接进行修改,绕过代理对象,那么对属性修改的限制就失效了。
比较可行的应该是使用Object.definedProperty()代理原对象,这样对原对象的修改也绕不过代理了

  1. let oldData = {n:0}
  2. let newData=myProxy({data:oldData})
  3. function myProxy({data}){
  4. let value = data.n;
  5. return Object.defineProperty(data, 'n', {
  6. get(){
  7. return value
  8. },
  9. set(newValue){
  10. if(newValue<0)return
  11. value = newValue
  12. }
  13. })
  14. }

不能检测数组和对象的变化

但由于 JavaScript 的限制,Vue不能检测数组和对象的变化

对于对象

可以使用:Vue.set(object,propertyName,value)
vm.$set(object,propertyName,value)

  1. const vm = new Vue({
  2. data:{
  3. obj:{ //如果data里面只有一层,没有obj,那Vue就会检测到b的问题,会报出一个Vue warn
  4. a:1
  5. }
  6. },
  7. template : `
  8. <div>
  9. {{obj.a}}
  10. {{obj.b}}
  11. <button @click ='setA'>set a</button>
  12. <button @click ='setB2'>set b2</button>
  13. <button @click ='setB'>set b</button>
  14. </div>
  15. `,
  16. methods:{
  17. setA() {
  18. this.obj.a += 1; //点击set a,修改a的值, a的值就从1变为2,触发视图渲染
  19. },
  20. setB2(){
  21. this.obj.b = 2; //点击按钮,添加obj的属性b,这种方法并不会触发视图重新渲染,所以2并不会出现在视图里
  22. },
  23. setB(){
  24. Vue.set(this.obj,'b',2)//应该使用Vue提供的这种方法添加对象或对象属性
  25. }
  26. }
  27. })
  28. //添加b,使其变成响应式的方法也可以如下
  29. Vue.set(vm.obj,'b',2)
  30. //或
  31. vm.$set(vm.obj,'b',2)
  32. //如果先点击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完成同样的事情