跳到主要内容

谈谈vue响应式

vue文档响应式系统原理

什么是响应式?​

  • 数据变化是可以监视到的,数据改变后, 和数据相关的内容视图也可以相应的变化
  • 页面视图渲染中使用了数据,数据改变后,视图也会自动更新

双向绑定/单向绑定原理​

单向绑定:

  • 单向绑定,数据流是单向的,状态变化需要通过手动调用通知
  • 单向绑定非常简单,就是把Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新。
    • 单向绑定 视图层不能影响数据层,只能数据层改变视图层

双向绑定:

  • 双向绑定 是 视图层和数据层 数据可以互相影响同步
  • 所谓双向数据绑定, 无非就是数据层M和视图层V中的数据同步, 在写入数据时视图层实时的跟着更新
  • vue3.0版本之前是通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发响应的监听回调,vue3版本之后通过proxy实现对象拦截监听变化
  • vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变

reactive原理​

  • 1、reactive内部实现通过proxy代理拦截对象监听

  • 2、get/set 都会触发拦截器监听对象属性,进行依赖收集

  • 3、当访问属性的时候会触发拦截器get函数进行监听对象属性, 进行依赖收集【把访问的属性收集到依赖中】, 更新值的时候触发set拦截器,触发视图的更新

ref原理​

  • 1、Proxy只能代理对象类型,不能代理基本数据类型,为把基本数据类型转化为响应式数据,因此引入了ref,对于引用类型,会转换为reactive
  • 2、ref的内部实现是,通过返回ref的实现类 ,这个类会做一个判断 【RefImpl => ref实现类】
    • 1、如果是对象,会调用reactive转换响应式数据
    • 2、如果是基础类型,返回当前值,通过 RefImpl 实现类来处理基础类型
      • RefImpl实现类内部会维护_value属性,用于存储基本数据类型值,具有响应式特性,当值发生变化,触发依赖更新
    • 3、当设置值的时候,会进行比较,如果值变化了,就会更新值,并且触发依赖更新,来更新视图,当访问的时候,才会收集依赖
  • 3、track收集依赖:注意,在取值的时候,会进行依赖收集,调用track方法,会判断检查是否有正在运行的副作用 【activeEffect】
    • activeEffect: 正在运行的副作用 【用于标记当前活动作用的副作用】
      • 即有没有对外部产生依赖影响,如果有,会查找到一个存储了所有追踪了该属性的订阅者,然后将当前这个副作用添加到订阅者中
    • 当副作用执行完之后,把activeEffect 设置为 null

当访问属性的时候,才会触发依赖收集监听,依赖收集的track方法会进行判断当前是否存在副作用,如果存在副作用,会找到所有追踪该属性的订阅者,把当前这个副作用添加到订阅者中

  • 4、trigger依赖更新,当赋值的时候,进行依赖更新,trigger方法会再查找到该属性的所有订阅副作用。依次遍历执行这个订阅的副作用进行依赖更新

object.definePeorty和proxy​

object.definePeorty​

  • Object.defindProperty对于数组的监听会造成性能问题,但是可以监听数组变化,只是对于长列表来说,性能差,不建议监听数组,与用户体验不成正比
    • 为了解决这个问题,经过 vue 2内部处理把原生数组的方法push、pop、splice等等7个方法进行重写
  • 只能劫持对象的属性,因此需要对对象的每个属性进行递归遍历
  • 数据量复杂时 性能低

监听数组​

js
const arr = [1,2,3]

arr.forEach((item,index)=>{
Object.defineProperty(arr,index,{
set:(val)=>{
console.log('set');
item = val
},
get:()=>{
console.log('get');
return item
}
})
})

arr[1] // 触发get
arr[1] = '4' // 触发set

proxy​

  • proxy可以监听整个对象的变化,不需要深度遍历

  • 可以直接监听数组的变化。

  • Proxy有13中拦截方法

  • proxy在访问对象属性的时候才触发拦截器函数进行监听对象属性,而Object.defineProperty在使用的时候一次性就递归所有对象监听,所以proxy在性能上有所提升

    Proxy 是基于代理对象的机制,只有在访问代理对象的属性时才会触发拦截器函数

    在访问对象属性的性能方面,Proxy 通常比 Object.defineProperty 更高效