vue响应式原理

vue响应式原理

一、响应式框架

  • 侦听数据变化(数据拦截)
  • 收集谁依赖了数据,比如视图、计算属性等(订阅更新)
  • 数据发生变化,通知依赖进行更新(派发更新)

操作DOM带来的问题

  • 需要操作多个DOM元素,非常不方便
  • 每一次数据变化都要操作DOM
  • 频繁操作DOM带来的性能问题
  • 思路:不在逻辑计算过程中操作DOM,尽量减少DOM操作

MVVM模式

  • Android的消息队列模型
  • RxJava的响应式编程
  • AngularJS等

Vue不支持IE8及以下浏览器是由于低版本浏览器不支持defineProperty是ES5新方法

二、Vue如何实现响应式框架

关于对象

  1. 对象属性包括数据属性和访问器属性
  2. 数据属性描述符:configurable、enumerable、writable、value
  3. 访问器属性描述符:configurable、enumerable、get、set
  4. 属性描述符属于内部特性,用于描述属性行为,无法直接访问,但可通过defineProperty进行修改
  5. 访问器属性没有数据值,取而代之的是get、set函数
  6. 将数据属性改为访问器属性,使用getter和setter来拦截数据的访问和修改是响应式的基石

关于数据拦截

组件init阶段,Vue的data属性会被Observe类reactive化,即加上getter和setter函数,并建立依赖Dep

function observe(obj){
    if(!obj || typeof obj !== object){
        return
    }
    Object.keys(obj).forEach(key=>{
        defineReactive(obj,key,obj[key])
    })
    function defineReactive(obj,key,value){
        observe(value)
        Object.defineProperty(obj,key,{
            enumerable:true,
            configurable:true,
            get:function(){
                // 依赖收集
                return value
            },
            set:function(){
                observe(newValue)
                if(newValue !== value){
                    value = newValue
                    //派发更新
                }
            }
        })
    }
}

三、Vue如何保证UI更新?

对象变更检测

  1. 由于javascript的限制,目前Vue无法检测到对象属性的添加或删除(必须注册到Vue的data中)
  2. 根级别的响应式属性:不允许动态添加、删除
  3. 嵌套对象的响应式属性:允许添加、删除,但是需要通过Vue提供的方法进行操作,通过Vue将属性修改为响应式属性
// 动态添加响应式属性
Vue.set(vm.person,'age',18)
// 删除响应式属性
Vue.delete(vm.person,'name')
// 不能再根级别添加响应式属性
Vue.set(vm,'count',12)

特殊的数组的更新检测

  1. 所有的对象上都有Object.defineProperty方法,可以通过get&set方法实现数据劫持,但是数组没有这两个方法
  2. Vue将被侦听的数组的变异方法进行了包裹,使得他们也能触发视图更新
push、pop、shift、unshift、slice、sort、reverse
  1. 数组替换也能通知到Vue,从而触发视图更新

Vue无法检测到的数组更新

  1. 通过索引直接修改数组项
// 错误方法:
vm.items[index] = newVal
// 修正方法:
Vue.set(vm.items,index,newVal)  or vm.items.splice(index,1,newVal)
  1. 修改数组的长度
// 错误方法:
vm.items.length
// 修正方法:
vm.items.splice(items.length)

JavaScript中的事件循环

宏任务与微任务

  1. 宏任务和微任务都是异步任务
  2. 宏任务和微任务都会被注册到两个不同的队列中
  3. 宏任务队列不是一次性清空执行,而是执行一个宏任务后,去清空执行一次微任务队列
  4. 在执行微任务的过程中加入微任务队列的微任务,也会在当前循环中执行
常见宏任务:
setTimeout、setInterval、setImmediate、script、MessageChannel
常见微任务:
Promise、MutationObserver、Object.observe(废弃)、process.nextTick(node)

DOM更新也是一个微任务

异步刷新机制

  1. 只要侦听到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watch被多次触发,只会只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作是非常重要的。然后在下一个事件循环tic中,Vue刷新队列并执行实际(已去重)工作。
  2. Vue在内部对异步队列尝试使用原生Promise.then、MutationObserver和setImmediate,如果执行环境不支持,则会采用setTimeout(fn,0)代替。

异步队列

  1. 所有同步任务都在主线程上执行,形成一个执行栈
  2. 主线程之外,还存在一个或多个任务队列,主要异步任务有了运行结果,就在异步队列中放置一个事件
  3. 一旦执行栈中的所有同步任务执行完毕,系统就会读取“任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
  4. 主线程不断重复上面第3步
posted @ 2020-07-25 21:51  南华秋水  阅读(667)  评论(0编辑  收藏  举报