kankan-web / coding

主要是用来记录前端工程化学习的库
0 stars 0 forks source link

【vue】Vue是如何实现数据双向绑定的?【响应式】 #9

Open kankan-web opened 1 month ago

kankan-web commented 1 month ago

【vue】Vue是如何实现数据双向绑定的?

相关问题

  1. Vue的响应式原理(Vue2与Vue3)
  2. Vue框架是怎么实现对象和数组的监听
  3. 使用Object.defineProperty()来进行数据劫持有什么缺点?
  4. Vue中封装的数组方法有哪些,其如何实现页面刷新
  5. Vue是如何收集依赖的?
  6. Vue 中给 data 中的对象属性添加一个新的属性时会发生什么?如何解决?
kankan-web commented 1 month ago

双向绑定是指:数据变化更新视图,视图变化更新数据。 vuejs是采用数据劫持结合发布者-订阅者模式的方式实现。通过Object.defineProperty()来劫持各个属性的setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。主要分为以下几个步骤:

  1. 对需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上getter和setter,一旦属性值发生变动,就会触发setter,那么就监听到了数据变化。
  2. compile:解析模板指令,将模板中的变量都替换为数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,就开始更新视图。
  3. Watcher订阅者:是Observer和Compile之间的通信桥梁,主要做的事情是:
    • 在自身实例化时往属性订阅器(dep)里面添加自己
    • 自身必须有一个update()方法
    • 等待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
  4. Dep订阅器:采用发布-订阅设计模式,用来收集订阅者Watcher,对监听器Observer和订阅者Watcher进行统一管理。 image
kankan-web commented 1 month ago

Object.defineProperty

Vue2中使用Object.defineProperty来进行数据劫持。 但是它存在以下缺点:

  1. 添加或删除对象的属性时,Vue检测不到,因为添加或删除的对象没有在初始化时进行响应式处理,只能通过$set来调用Object.defineProperty()处理
  2. 无法监控到数组下标和长度的变化

    Vue2中如何检测数组的变化?

    简单来说就是,重写了数组中的那些原生方法,Vue将data中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了检测数组变化。

    vm.$set的实现原理

    • 如果目标是数组,则直接使用数组的splice方法触发响应式
    • 如果目标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用defineReactive方法进行响应式处理(defineReactive方法就是Vue在初始化对象时,给对象属性采用Object.defineProperty动态添加getter和setter的功能所调用的方法)
kankan-web commented 1 month ago

Proxy

vue3使用Proxy机制代理需要响应化的数据。 Proxy时ES6中提供的功能,具有以下特点:

  1. Proxy直接代理整个对象而非对象属性,这样做只需做一层代理就可以监听到同级结构下的所有属性变化,包括新增属性和删除属性
  2. Proxy可以监听数组的变
  3. Proxy有多达13中拦截方法,不限于apply、ownKeys、deleteProperty、has等

    Proxy只会代理对象的第一层,那么Vue3又是怎样处理这个问题呢?

    判断当前Reflect.get的返回值是否为Object,如果是则通过reactive方法做代理,这样就实现了深度观测

    监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?

    我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger。