vue3+定义响应式reactive()和ref()

503 阅读3分钟

基础用法

  • vue3中,我们使用reactive()函数创建响应式对象或者数组
import { reactive } from "vue"

const obj = reactive({ x: 1});
const arr = reactive(["a"])
const str = reactive("b")
const num = reactive(1)
console.log(obj) // Proxy {x:1}
console.log(arr) // Proxy [0:"a"]
console.log(str) // b
console.log(num) // 1
obj.x++ // 2
arr[0] = "c" // c
str = "d" // Assignment to constant variable.
num++ // Assignment to constant variable.

所以使用reactive()只能创建响应式对象和数组和Map、Set这样的集合类型,而对字符串、数值、布尔值等都无效。

import { reactive } from "vue"

const obj = reactive({ x: 1 });
console.log(obj); // Proxy {x:1}
obj = reactive({ y: 2 }); // Assignment to constant variable.

所以reactive()定义的一个响应式对象不能被替换

import { reactive } from "vue"

const obj = reactive({ x: 1 });
console.log(obj); // Proxy {x:1}
let n = obj.x;
n++; // 2
obj.x; // 1
let { x } = obj;
x++; // 2
obj.x; // 1

当我们将响应式对象的属性赋值或者解构到本地变量时,还有函数,会使响应式对象失去响应

  • ref()定义响应式变量
import { ref } from 'vue'

const str = ref("a");
console.log(str, str.value); // RefImpl {value:"a"}, "a"
str.value += "b"; // "ab"

const num = ref(1);
console.log(num, num.value); // RefImpl {value:1}, 1
num.value++; // 2

const obj = ref({ x: 1 });
obj.value.x++; // 2
let { x } = obj.value;
x++; //3

const objNew = { y: ref(1) };
objNew.y.value++; // 2
let { y } = objNew; 
y.value++ // 3

使用ref()函数创建响应式对象、数组、字符串、数值、布尔值等都可以,并进行解构使用。

原理剖析

  • 什么是响应式?

如果a=b+c;当b或者c改变时,a也随即自动更新。在JavaScript中默认并非如此,现在我们用JavaScrip来实现这个功能。

let b = 1;
let c = 2;
let a
function updata(){
	a = b + c
}
  1. 这个 updata() 函数会产生一个副作用,或者就简称为作用 (effect),因为它会更改程序里的状态。
  2. bc 被视为这个作用的依赖 (dependency),因为它们的值被用来执行这个作用。因此这次作用也可以说是一个它依赖的订阅者 (subscriber)

我们需要一个魔法函数,能够在 bc (这两个依赖) 变化时调用 updata() (产生作用)。

whenDepsChange(updata)

这个whenDepsChange()函数有如下的任务:

  1. 当一个变量被读取时进行追踪。例如我们执行了表达式 b + c 的计算,则 bc 都被读取到了。
  2. 如果一个变量在当前运行的副作用中被读取了,就将该副作用设为此变量的一个订阅者。例如由于 bcupdata() 执行时被访问到了,则 updata() 需要在第一次调用之后成为 bc 的订阅者。
  3. 探测一个变量的变化。例如当我们给 b 赋了一个新的值后,应该通知其所有订阅了的副作用重新执行。
  • Vue中的响应式是如何运行的

JavaScript中有俩种劫持property访问的方式:getter/settersProxies。在vue2中使用的getter/setters是为了支持旧版本的浏览器限制。而在vue3中则使用Proxy来创建响应式对象,仅将getter/setters用于ref

const mutableHandlers = {
    get(target, key, recevier) {
        let res = target[key]
        return res
    },
    set(target, key, value, recevier) {
        target[key] = value
        return true
    }
}
const createReactiveObject = (obj) => {
     const proxy = new Proxy(obj, mutableHandlers)
     return proxy
}
const reactive = (obj) => {
     return createReactiveObject(obj)
}