VUE 响应式原理

Vue 会在初始化实例时对property 执行getter,setter 转化,property 必须在data 对象上存在才能让 Vue 转换为响应式的。

var vm = new Vue({
  data:{
    a:1
  }
})

// `vm.a` 是响应式的

vm.b = 2
// `vm.b` 是非响应式的

如何追踪变化

当你把一个普通的JavaScript 对象传入Vue 实例作为data 选项,Vue 将会遍历此对象所有的property ,并使用Object.defineProperty 把这些property 全部转为getter/setter ,Object.defineProperty 是ES5 中无法shim 的特性,所以Vue 不支持IE8以及更低版本的浏览器。
这些getter/setter 对用户是不可见的,但是在内部他们让Vue 能够追踪依赖,在property 被访问和修改时通知变更。
每个组件实例都对应一个watcher 实例,它会在组件渲染的过程中把“接触”过的数据property 记录为依赖,之后当依赖项的setter 触发时,会通知watcher ,从而使它关联的组件重新渲染。

对象的响应式实现

由于JavaScript 的限制,VUE 不能检测数组和对象的变化,那Vue 是如何保证数组和对象的响应式呢?
使用 Vue.set(object, propertyName, value)
举例:

<div id="app">
  {{obj.a}}
  <button @click="click1">
    click
  </button>
</div>
<script>
const obj={
      a:1,
      b:2
    };
new Vue({
  el:'#app',
  data:{
    a:1,
    obj:obj
  },
  methods:{
    click1(){
      Vue.set(this.obj, 'a', this.obj.a+1)
    }
  }
})
</script>

点击按钮之后会重新渲染视图。

数组的响应式实现

Vue 不能检测以下的数组的变动,例如:

vm.items[indexOfItem]=newValue
vm.items.length=newLength

为了解决这一问题,可以使用 Vue.set(vm.items, indexOfItem, newValue)
举例:

<div id="app">
  {{arr[0]}}
  <button @click="click1">
    click
  </button>
</div>
new Vue({
  el:'#app',
  data:{
    a:1,
    arr:[1,2]
  },
  methods:{
    click1(){
      Vue.set(this.arr,0,this.arr[0]+1)
    }
  }
})

每点击一次按钮,data选项中的arr 的第一项变化会重新渲染视图。
除此之外,Vue 包装了被观察数组的变异方法,它们也可以触发视图更新,被包装的方法有:

push()
pop()
shift()
unshift()
splice()
sort()
reverse()

举例:

<div id="app">
  <button @click="click1">
    {{arr}}click
  </button>
</div>
new Vue({
  el:'#app',
  data:{
    arr:[1,2]
  },
  methods:{
    click1(){
      const temp=this.arr[this.arr.length - 1] + 1;
      this.arr.push(temp)
    }
  }
})

点击按钮时,执行push 之后会触发视图更新。

声明响应式property

所有响应式property 必须在初始化实例前声明,哪怕只是一个空值。

异步更新队列

Vue 在更新DOM 时是异步执行的。
例如: 当你设置 vm.someData =‘new Value’,该组件不会立即重新渲染,当刷新队列时,组件会在下一个时间循环tick 中更新。
所以如果你想基于更新后的DOM 状态来做点什么,可以使用 Vue.nextTick(callback),在下一个时间循环 tick 中,DOM 已经完成更新,在回到函数中执行你想做的操作。
举例:

<div id="app">
  <button @click="click1">
    click{{a}}
  </button>
</div>
new Vue({
  el:'#app',
  data:{
    a:1
  },
  methods:{
    click1(){
      this.a++
      console.log(this.$el.textContent)// DOM 还为渲染
      this.$nextTick(function(){
           console.log(this.$el.textContent)// 已经渲染完成
      })
    }
  }
})

在 $nextTick 中,DOM 渲染已经完成。
$nextTick 返回的是一个Promise 对象,所以可以使用 async 和 await 语法简化操作。
click1 这个方法简化为下面:

 async click1(){
  this.a++
  console.log(this.$el.textContent)
  await this.$nextTick()
  console.log(this.$el.textContent)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值