美文网首页
浅谈vue2和vue3响应式

浅谈vue2和vue3响应式

作者: wyc0859 | 来源:发表于2022-04-20 19:31 被阅读0次

    Vue 2 那个时代基本只有两个技术选型,Facebook 家的 Flow.js 和微软家的 TypeScript
    Vue 2 选 Flow.js ,但是现在 Flow.js 已停止维护了
    Vue 3 选择了 TypeScript。TS带来更方便的提示,和让代码更健壮

    Vue 2 的响应式机制

    是基于 Object.defineProperty() API 实现的, defineProperty 的使用,要明确地写在代码里

    Object.defineProperty(obj, 'title', {
      get() {},
      set() {},
    })
    

    当项目里“读取 obj.title”和“修改 obj.title”的时候被 defineProperty 拦截,但 defineProperty 对不存在的属性无法拦截,所以 Vue 2 中所有数据必须要在 data 里声明。
    而且,如果 title 是一个数组的时候,对数组的操作,并不会改变 obj.title 的指向,虽然可以通过拦截.push 等操作实现部分功能,但是对数组的长度的修改等操作还是无法实现拦截,所以还需要额外的 $set 等 API

    Vue 2 新增功能基本都得修改 data、method 等配置,并且代码多了之后,会经常上下data和method之前反复跳,开发很痛苦


    Vue 3 的响应式机制

    是基于Proxy 代理,并且响应式独立了出来

    new Proxy(obj, {
      get() { },
      set() { },
    })
    

    Proxy 拦截 obj 这个数据,Proxy 不关心 obj 具体是什么属性,统一都拦截

    vue3所有 API 都是 import 引入的,对 Tree-shaking 很友好,没用到功能,打包的时候会被清理掉


    响应式解读

    JS 里的变量,是没有响应式这个概念的。 JS被是自上而下执行代码
    看下面的代码,即使修改了 count 的值后,double 的值也不会有任何改变。

    let count = 1
    let double = count * 2
    console.log(double) //2
    count = 2
    console.log(double) //仍然是2
    

    如果我们想让 doube 能够跟着 count 的变化而变化,那么就需要在每次 count 的值修改后,重新计算 double

    let count = 1
    // 计算过程封装成函数
    let getDouble = (n) => n * 2 //箭头函数
    let double = getDouble(count)
    console.log(double) //2
    
    count = 2
    double = getDouble(count) // 再次调用函数,重新计算double
    console.log(double) //4
    

    vue2响应机制原理
    Object.defineProperty() 直接在一个对象上定义一个新属性,或者修改一个已存在属性,并返回这个对象
    3参数:1对象,2对象需要定义或修改的属性名,3属性描述

    let getDouble = (n) => n * 2
    let obj = {}
    let count = 1
    let double = getDouble(count)
    
    
    Object.defineProperty(obj, 'count', {
      get() {
        return count
      },
      set(val) {
        count = val
        double = getDouble(val)
      }
    })
    console.log(double) // 打印2
    obj.count = 2
    console.log(double) // 打印4  有种自动变化的感觉
    

    但 defineProperty API 作为 Vue 2 实现响应式的原理,它的语法中也有些缺陷。比如我们删除 obj.count 属性,set 函数就不会执行,double 还是之前的数值。这也是为什么在 Vue 2 中,需要 $delete 一个专门的函数去删除数据

    vue3响应机制的原理
    Proxy 是针对对象来监听,而不是针对某个具体属性,所以不仅可以代理那些定义时不存在的属性,还可以代理更丰富的数据结构,比如 Map、Set 等,并且我们也能通过 deleteProperty 实现对删除操作的代理。

    let getDouble = (n) => n * 2
    let obj = {}
    let count = 1
    let double = getDouble(count)
    
    let proxy = new Proxy(obj, {
      get: function (target, prop) {
        return target[prop]
      },
      set: function (target, prop, value) {
        console.log(target, '-', prop, '-', value) //{} - count - 2
        target[prop] = value
        if (prop === 'count') {
          double = getDouble(value)
        }
      },
      deleteProperty(target, prop) {
        delete target[prop]
        if (prop === 'count') {
          double = NaN
        }
      }
    })
    console.log(obj.count, double) //undefined 2
    proxy.count = 2 //set(target=obj, prop=count, value=2)
    console.log(obj.count, double) //2 4
    delete proxy.count
    console.log(obj.count, double) //undefined NaN
    

    Vue 3 的 reactive 函数可以把一个对象变成响应式数据,而 reactive 就是基于 Proxy 实现的。
    Vue 3 中还有另一个响应式实现,是用 ref 这个 API 的实现。

    const b = reactive({ name: 2 }) //reactive 是Proxy对象
    console.log(b) //Proxy {name: 2}
    
    const a = ref()  // ref 是RefImpl对象
    console.log(a) //RefImpl
    

    ref原理类似如下

    let getDouble = (n) => n * 2
    let _value = 1
    double = getDouble(_value)
    
    let count = {
      get value() {
        return _value
      },
      set value(val) {
        _value = val
        double = getDouble(_value)
      }
    }
    console.log(count.value, double) //1 2
    count.value = 2
    console.log(count.value, double) //2 4
    

    相关文章

      网友评论

          本文标题:浅谈vue2和vue3响应式

          本文链接:https://www.haomeiwen.com/subject/twzvertx.html