vue响应式原理整理
Posted lsaxy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue响应式原理整理相关的知识,希望对你有一定的参考价值。
vue是数据响应性,这是很酷的一个地方。本文只为理清逻辑。详细请看官方文档 https://cn.vuejs.org/v2/guide/reactivity.html
vue的data在处理数据时候,会遍历data内对象的所有属性,并使用Object.defineProperty将属性转为getter/setter。 这里的getter/setter对用户是不可见的,但是方便vue对数据进行内部跟踪,来维护数据。
用Object.defineProperty这是一个ES5无法支持特性,所有vue不支持IE8以及更低级的浏览器
流程入上图,每个data里的数据都相应的有一个watcher实例,用来监听数据变化,当数据发布变化时候,watcher去触发相应的组件渲染函数并讲具体数据渲染到DOM上面
原始数据?数据处理?增加监听实例?调用渲染函数?组件变化
由于jacascript的限制,vue不能直接检测到数组和对象的变化。
数组:
1 // 数组 2 3 const app = new Vue({ 4 data: { 5 arr: [‘a‘, ‘b‘, ‘c‘] 6 } 7 }) 8 9 10 11 app.arr[0] = ‘d‘ // 非响应式 12 app.arr.length = 4 // 非响应式
Vue为了解决改变数组内元素的问题,采取了两种方法
第一种: Vue.set,vue的全局方法,可以用来改变数据,或者是直接使用实例的 app.$set(),它是Vue的全局方法的一个别名。
如:
1 // 全局 2 Vue.set(app.arr, indexOfItem, newValue) 3 4 // 实例 5 app.$set(app.arr, indexOfitem, newValue)
第二种:使用数组的splice方法
1 app.arr.splice(indexofItem, 1, newValue)
对于改变数组长度,也可以采取splice方法
1 app.arr.splice(newLength)
对象:
vue无法检测到property的添加和删除,由于data如上述所讲,需要进行初始化getter/setter来进行管理,而类似于直接添加这种方法是无法响应的
如:
1 const app = new Vue({ 2 data: { 3 a: 1 4 } 5 }) 6 7 8 app.a // 是响应式,因为数据已经初始化 9 10 app.b = 2 // 不是响应式,无法监听到
对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。
但是,可以使用Vue.set这个全局方法向嵌套对象添加响应式属性
如:
1 Vue.set(vm.someObject, ‘b‘, 2)
同上,也可以直接使用实例的$set方法
由于 Vue 不允许动态添加根级响应式属性,所以你必须在初始化实例前声明所有根级响应式属性,哪怕只是一个空值
重点:vue的更新DOM是异步的,当Vue监听到数据变化时候,会创建一个队列,并缓冲在同一事件循环中数据变更。如果一个watcher被多次触发,也不会造成重复推入,Vue会去除重复数据来避免不必要的计算。在下一个事件循环’tick‘。 Vue 在内部对异步队列尝试使用原生的 Promise.then
、MutationObserver
和 setImmediate
,如果执行环境不支持,则会采用 setTimeout(fn, 0)
代替。
这往往是需要注意的,由于更新DOM是异步的,而如果你需要在数据更新后来操作DOM,这往往会产生奇怪的错误。
踩坑点:
讲一个例子。比如需要在一个dom上面挂载或者监听一个内容时候,往往会出错。比如使用v-if来进行dom渲染,由于dom还未渲染,会直接导致挂载失败。这也是Vue不建议直接操作dom的原因。
或者是通过data里的数据来重新刷新一些插件的值,由于数据是异步刷新的,可能会导致传输到插件的值还是原值,导致插件刷新失败。
这里Vue提供一个Vue.nextTick(callback)的方法,这样可以在dom都渲染完毕后再执行我们相关的业务代码。
以上是关于vue响应式原理整理的主要内容,如果未能解决你的问题,请参考以下文章
Vue3官网-高级指南(十七)响应式计算`computed`和侦听`watchEffect`(onTrackonTriggeronInvalidate副作用的刷新时机`watch` pre)(代码片段