【Vue3源码】第四章 实现isReadonly和isReactive
前言
上一章节我们实现readonly API,并且优化之前写的reactive API。这一章我们实现isReadonly和isReactive两个API。
1、实现isReactive
官网是这么介绍的:检查一个对象是否是由 reactive()
或 shallowReactive()
创建的代理。
原理很简单我们来实现一下这个函数~
先看单元测试代码
import { reactive,isReactive } from "../reactive"
describe("reactive",() => {
it("happy path",() => {
const originObj = {foo:1}
const observed = reactive(originObj)
expect(observed).not.toBe(originObj)
expect(observed.foo).toBe(1)
//新增
expect(isReactive(observed)).toBe(true)
expect(isReactive(originObj)).toBe(false)
})
})
实现isReactive
isReactive的原理很简单,我们怎么判断一个对象是否是由reactive()创建的呢?
还记得上一章节中我们在get捕获器中加入的isReadonly的参数吗?
是的这个参数就可以帮我们监测对象是reactive还是readonly,如果是reactive就调用track函数触发依赖
那么isReadonly参数也可以用来判断是否是个reactive()对象
所以我们可以在reactive.ts文件里新增如下代码:
// 枚举
export const enum ReactiveFlags {
IS_REACTIVE = "__v_isReactive",
}
// 传入对象后我们会去访问对象
export const isReactive = (value) => {
return !!value[ReactiveFlags.IS_REACTIVE]
}
首先我们创建了一个枚举
枚举中我们设置一个用户基本不可能设置的key
那么我们就可以通过这个key,去访问对象中key对应的value
如果是个普通对象,访问value[ReactiveFlags.IS_REACTIVE]
自然是返回 undefined 所以直接用 !!undefined 转换成 false返回即可
那么如果是响应式对象呢?
如果是个响应式对象读取对象的值时,那么它就会先触发get捕获器
所以我们要到baseHandlers.ts文件下判断key是否是"__v_isReactive"即可
import { track, trigger } from "./effect"
import { ReactiveFlags } from "./reactive"
function createGetter(isReadonly = false) {
return function get(target, key) {
//如果传入的key是ReactiveFlags.IS_REACTIVE
if (key === ReactiveFlags.IS_REACTIVE) {
//get捕获器就返回true告诉调用者这是一个reactive对象
return !isReadonly
}
const res = Reflect.get(target, key)
if (!isReadonly) {
track(target, key)
}
return res
}
}
这就是isReactive函数在监测不同对象时的流程图:
2、实现isReadonly
其实是同理的直接上代码了
reactive.ts
export const enum ReactiveFlags {
IS_REACTIVE = "__v_isReactive",
IS_READONLY = "__v_isReadonly"
}
export const isReactive = (value) => {
return !!value[ReactiveFlags.IS_REACTIVE]
}
export const isReadonly = (value) => {
return !!value[ReactiveFlags.IS_READONLY]
}
baseHandlers.ts
import { ReactiveFlags } from "./reactive"
function createGetter(isReadonly = false) {
return function get(target, key) {
if (key === ReactiveFlags.IS_REACTIVE) {
//get捕获器就返回true告诉调用者这是一个reactive对象
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
// 同理返回isReadonly证明是readonly
return isReadonly
}
const res = Reflect.get(target, key)
if (!isReadonly) {
track(target, key)
}
return res
}
}
流程图:
下节预告《vue3源码 优化stop功能》