Vue3 响应式系统 |小册免费学

501 阅读3分钟

前言

vue3中最核心的就是基于Proxy实现的响应式API(用于代理对象类型)

响应式系统 API,顾名思义,就是指在新的特性中是如何去实现 Vue 的响应式功能。在这里我们介绍一下 reactive、ref、2 个响应式 API 的使用方法及Vue3响应式实现原理。

Vue3 响应式Api

reactive

返回对象的响应式副本

const obj = reactive({ count: 0 })

响应式转换是“深层”的——它影响所有嵌套 property。在基于 ES2015 Proxy 的实现中,返回的 proxy 是等于原始对象的。建议只使用响应式 proxy,避免依赖原始对象。

reactive 参数必须是对象

reactive 将解构所有深层的 refs,同时维持 ref 的响应性。

const count = ref(1)
const obj = reactive({ count })

// ref 不会被解构
console.log(obj.count === count.value) // true

// 它会更新 `obj.value`
count.value++
console.log(count.value) // 2
console.log(obj.count) // 2

// 它也会更新 `count` ref
obj.count++
console.log(obj.count) // 3
console.log(count.value) // 3

reactive 同样可以包裹数组进行响应式赋能。(数组可以看成是特殊的 对象)

ref

refreactive 一样,同样是实现响应式数据的方法。在业务开发中,我们可以使用它来定义一些简单数据类型数据

<template>
  <p>{{ count }}</p>
</template>

<script>
import { ref } from 'vue'
export default {
  name: 'App',
  setup() {
    const count = ref(0)

    return {
      count
    }
  }
}
</script>

修改数据,可以通过 count.value = 1 类似这样的语法去修改。但是为什么它需要这样去修改变量,而 reactive 返回的对象可以直接修改如 state.count = 1

原因是 Vue 3.0 内部将 ref 悄悄的转化为 reactive,如上述代码会被这样转换:

ref(0) => reactive({ value: 0 })

所以 count 相当于 reactive 返回的一个值,根据 reactive 修改值的方式,就可以理解为什么 ref 返回的值是通过 .value 的形式修改值了。

  • shallowReactive:vue3中能够将对象变成响应式的API,只代理最外层对象
  • readonly:将对象属性变为只读,且不管对象有多少层
  • shallowReadonly:将对象属性变为只读,但是只代理最外层

Vue响应式实现原理

  • vue2 响应式原理回顾

  1. 对象响应化:遍历每个key,通过 Object.defineProperty API定义getter,setter
// 伪代码
function observe(){
    if(typeof obj !='object' || obj == null){
        return
    }
    if(Array.isArray(obj)){
        Object.setPrototypeOf(obj,arrayProto)
    }else{
    const keys = Object.keys()
    for(let i=0;i<keys.length;i++){
      const key = keys[i]
      defineReactive(obj,key,obj[key])
    }
    }
}
function defineReactive(target, key, val){
  observe(val)
  Object.defineProperty(obj, key, {
    get(){
      // 依赖收集
      dep.depend()
      return val
    },
    set(newVal){
      if(newVal !== val){
        observe(newVal)
        val = newVal
        // 通知更新
        dep.notify()
      }
    }
  })
}
  1. 数组响应化:覆盖原生JS数组的原型方法,增加通知变更的逻辑
// 伪代码
const originalProto = Array.prototype
const arrayProto = Object.create(originalProto)
['push','pop','shift','unshift','splice','reverse','sort'].forEach(key=>{
   arrayProto[key] = function(){
       originalProto[key].apply(this.arguments)
       notifyUpdate()
   }
})

vue2响应式痛点

  • 递归,消耗大
  • 新增/删除属性,需要额外实现单独的API
  • 数组,需要额外实现
  • Map Set Class等数据类型,无法响应式

vue3响应式方案

使用ES6的 Proxy 进行数据响应化,解决上述Vue2所有痛点

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

相比 Object.defineProperty ,Proxy支持的对象操作十分全面:get、set、has、deleteProperty、ownKeys、defineProperty......等等

// reactive 伪代码
function reactice(obj){
  return new Proxy(obj,{
    get(target, key, receiver){
      const ret = Reflect.get(target, key, receiver)
      return isObject(ret) ? reactice(ret) : ret
    },
    set(target, key, val, receiver){
      const ret = Reflect.set(target, key, val, receiver)
      return ret
    },
    deleteProperty(target, key){
      const ret = Reflect.deleteProperty(target, key)
      return ret
    },
  })
}

作者:chenuvi

邮箱: chenui@outlook.com

参考内容:

vue3 官网教程

Vue3响应式原理 + 手写reactive

Vue 3.0 企业级项目实战

本文正在参与「掘金小册免费学啦!」活动, 点击查看活动详情