当前位置:网络安全 > Vue响应性原理详解

Vue响应性原理详解

  • 发布:2023-10-05 11:23

Vue最显着的特点之一就是反应性系统。模型层(model)只是一个普通的JavaScript对象。修改它会更新视图(view)。

Vue 响应式系统的底层细节

如何跟踪更改

将普通 JavaScript 对象传递给 Vue 实例的 data 选项。 Vue 会遍历这个对象的所有属性,并使用 Object.defineProperty 将所有这些属性转换为 getter/setter。 Object.defineProperty 仅 ES5 支持,无法 shimmed 的功能是 Vue 不支持 IE8 及更低版本浏览器的原因。

getter/setter 对用户不可见,但在内部它们让 Vue 跟踪依赖关系并在访问和修改属性时通知更改。这里需要注意的问题是,浏览器控制台在打印数据对象时对 getter/setter 的格式不同,因此您可能需要安装 vue-devtools 以获得更友好的检查界面。

每个组件实例都有一个对应的watcher实例对象,该对象在组件渲染过程中将属性记录为依赖项。稍后,当调用依赖项的 setter 时,会调用 watcher 重新计算,从而导致其关联组件被更新。

变更检测问题

由于现代 JavaScript 的限制,Vue 无法检测对象属性的添加和删除。由于Vue在初始化实例时会对属性进行getter/setter转换过程,因此该属性必须存在于数据对象上,Vue才能对其进行转换,这样才能对应起来。例如:

var vm=nrew Vue({

  el:'#app',

  数据:{

    a:1

  }

});

//vm.a 有响应

vm.b=3;//vm.b无响应

Vue 不允许在已创建的实例上动态添加新的根级反应属性。不过,可以使用 Vue.set(object,key,value) 方法

向嵌套对象添加响应属性:

Vue.set(object.someObject,'b',2);

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

this.$set(this.someObject,'b',2)

有时你想给已有的对象添加一些属性,比如使用 Object.assign() 或 _.extend() 方法添加属性。但是,向对象添加新属性不会触发更新。在这种情况下,您可以创建一个新对象并让它包含元对象的属性和新属性:

//替换 'object.assign(this.someObject,{a:1,b:2})'

this.someObject=Object.assign({},this.someObject,{a:1,b:2});

声明反应性属性

由于 Vue 不允许动态添加根级响应式属性,因此您必须在初始化实例之前声明根级响应式属性,即使它只是一个空值:

var vm = 新的 Vue({
 数据: {
 // 将消息声明为空字符串
 信息: ''
 },
 模板:'{{消息}}'
})
// 稍后设置`message`
vm.message = '你好!'

如果 data 选项中没有声明 message,Vue 会警告你视图中渲染函数访问的属性不存在。

此限制背后有技术原因。它消除了依赖跟踪系统中的一种边缘情况,并且还允许 Vue 实例在类型检查系统的帮助下更有效地运行。在代码可维护性方面还有一个重要的考虑因素:数据对象就像组件状态的摘要,提前声明所有反应式属性可以使组件代码在以后重新阅读或其他开发人员阅读时更容易理解它。

异步更新队列

Vue 异步执行 DOM 更新。只要观察到数据变化,Vue就会打开一个队列并缓冲同一事件循环中发生的所有数据变化。如果同一个观察者被触发多次,它只会被推入队列一次。缓冲期间的重复数据删除对于避免不必要的计算和 DOM 操作非常重要。然后,在下一个事件循环“tick”时,Vue 刷新队列并执行实际工作。 Vue 内部尝试使用原生 Promise.then 和 MutationObserver 进行异步队列。如果执行环境不支持,则会使用setTimeout(fn,0)代替。

例如,当您设置 vm.someData='new value' 时,组件不会立即重新渲染。当队列被刷新时,当事件循环队列被清除时,组件将在下一个“tick”处更新。大多数情况下我们不需要担心这个过程,但是如果你想在 DOM 状态更新后做一些事情,这可能会有点棘手。虽然 Vue.js 通常鼓励开发人员以“数据驱动”的方式思考并避免直接接触 DOM,但有时我们确实需要这样做。为了在数据更改后等待 Vue 完成更新 DOM,可以在数据更改后立即使用 Vue.nextTick(callback)。这样,DOM更新完成后就会调用回调函数。例如:

{{留言}}
var vm = 新的 Vue({
 el: '#example',
 数据: {
 消息:'123'
 }
})
vm.message = '新消息' //更改数据
vm.$el.textContent === '新消息' // false
Vue.nextTick(函数 () {
 vm.$el.textContent === '新消息' // true
})

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

Vue.component('示例', {
 模板: '{{ 消息 }}',
 数据:函数(){
 返回 {消息:“未更新”
 }
 },
 方法: {
 更新消息:函数(){
  this.message = '已更新'
  console.log(this.$el.textContent) // => '未使用的更新'
  this.$nextTick(函数 () {
  console.log(this.$el.textContent) // => '更新完成'
  })
 }
 }
})

相关文章