Vue响应式原理

Vue最独特的特性之一,是其非侵入性的响应式系统,数据模型仅仅是普通的JavaScript对象,而当你修改它们时,视图会进行更新,这使得状态管理非常简单。接下来就讲讲Vue响应式系统的原理。

1、如何追踪变化

当一个Vue实例创建时,Vue会遍历data选项(你传入的普通JavaScript对象)中的所有属性,并使用Object.defineProperty把它们转化为getter/setter,并在内部追踪相关依赖,在属性被访问和修改时通知变更。
每个组件实例都对应一个watcher实例,它会在组件渲染的过程中将“解触”过的数据属性记录为依赖。当之后依赖项的setter触发时,会通知watcher,从而使它关联的组件重新渲染。
在这里插入图片描述

2、检测变化的注意事项

受现代JavaScript的限制(而且Object.observe也已经废弃),Vue无法检测到对象属性的添加或删除。由于Vue会在初始化实例时对属性执行getter/setter转化,所以属性必须在data对象上存在才能让Vue将它转换为响应式的。例如:

var vm = new Vue({
  data:{
    a:1,
    userProfile:{
			name:‘Alice’,
		},
  }
})

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

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

对于已经创建的实例,Vue不允许动态添加根级别的响应式属性。但是可以使用Vue.set(object,propertyName,value)方法向嵌套对象添加响应式属性。例如:

Vue.set(vm.userProfile,'age',6);

还可以使用vm.$set实例方法,这也是全局Vue.set方法的别名,例如:

this.$set(this.userProfile,'age',6);

有时你可能需要为已有的对象赋值多个新属性,比如使用Object.assign()_.extend()。但是,这样添加到对象上的新属性不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的属性一起创建一个新对象。例如:

this.userProfile = Object.assign({},this.userProfile,{
	'age':6,
	'job':'student'
});

3、异步更新队列

Vue在更新DOM时是异步执行的。只要侦听到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲事去除重复数据对于避免不必要的计算和DOM操作非常重要。然后,在下一个的事件循环“tick”中,Vue刷新队列并执行实际(已去重的)工作。Vue在内部对异步队列尝试使用原生的Promise.thenMutationObserversetImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。
例如,当你设置 vm.someData = 'new value',该组件不会立即重新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新。多数情况我们不需要关心这个过程,但是如果你想基于更新后的 DOM 状态来做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员使用“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们必须要这么做。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成后被调用。例如:

<div id="example">{{message}}</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: '123'
  }
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
  vm.$el.textContent === 'new message' // true
})

在组件内使用 vm.$nextTick() 实例方法特别方便,因为它不需要全局 Vue,并且回调函数中的 this 将自动绑定到当前的 Vue 实例上:

Vue.component('example', {
  template: '<span>{{ message }}</span>',
  data: function () {
    return {
      message: '未更新'
    }
  },
  methods: {
    updateMessage: function () {
      this.message = '已更新'
      console.log(this.$el.textContent) // => '未更新'
      this.$nextTick(function () {
        console.log(this.$el.textContent) // => '已更新'
      })
    }
  }
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值