首先你需要先了解一下vue3响应式的核心api,和他们的用法。你可以去官方查看。这里我就简单的介绍一下用法和特性
vue3响应式核心api有4个
注意:接下来开发都在reactivity这个包,所以这个包现在是根目录
我们需要在src下面创建一个reactive.js
文件,这个文件中存放我们响应式的api,也就是上面所的那四个核心的api
首先我们将四个方法都先写出来,内容后续填充
// 深度代理
export function reactive(target) {}
// 浅度代理 对象中的引用类型不会被代理
export function shallowReactive(target) {}
// 深度只读代理 被代理的对象只读
export function readonly(target) {}
// 浅度只读代理 对象中的引用类型不会被代理且可以修改
export function shallowReadonly(target) {}
那么这四个函数的逻辑其实总结来就两个,是否是只读的 是否是深度遍历,现在我们希望有这么一个公共的函数,可以通过不同参数去做不同的处理,所以我们可以去创建一个公共的函数createReactiveObject
// 该方法是将对象进行代理并且返回
// target被代理的对象
// isReadonly是否是只读的
// baseHandlers 拦截方法
export function createReactiveObject(target, isReadonly, baseHandlers) {}
现在我们需要写一些工具方法,我们将这些方法都放到shared
包中
shared/src/index.ts
// 判断是否是一个对象
export const isObject = (value) => typeof value === 'object' && value !== null
// 将两个对象合并
export const extend = Object.assign
现在需要创建一个baseHandleers.ts
文件,这个文件用来存放不同的api的拦截方法,如果将这些方法都写在reactive.ts中,那么会导致内容太多,难以维护
baseHandleers.ts
// 实现new Proxy的handler
export const mutableHandlers = {}
export const shallowReactiveHandlers = {}
export const readonlyHandlers = {}
export const shallowReadonlyHandlers = {}
现在我们回过头来编写createReactiveObject
函数
我们知道其实这些api的底层就使用new Proxy,所以最核心的就是对数据的修改和数据的获取进行拦截,所以这个函数要做的就是将代理的后对象返回
// 存放被代理的对象 weakmap会被垃圾回收 不会造成内存泄漏
const reactiveMap = new WeakMap() // 存放非只读的代理
const readonlyMap = new WeakMap() // 存放只读的代理
export function createReactiveObject(target, isReadonly, baseHandlers) {
// 判断是否是对象 reactive这个api只能拦截对象类型
if (!isObject(target)) return target
// 如果这个对象被代理过,就不用二次代理了,直接将结果返回即可
// 所以我们需要一个存储空间来存储,那么存储空间分为两种 一种是只读的代理,一种是非只读的代理,所以我们应该创建两个空间,reactiveMap和readonlyMap
const proxyMap = isReadonly ? readonlyMap : reactiveMap
const existProxy = proxyMap.get(target)
if (existProxy) return existProxy
// 创建代理
const proxy = new Proxy(target, baseHandlers)
return proxy
}
现在我们需要将4个api中调用createReactiveObject
方法,并传入不同参数
// 深度代理
export function reactive(target) {
return createReactiveObject(target, false, mutableHandlers)
}
// 浅度代理 对象中的引用类型不会被代理
export function shallowReactive(target) {
return createReactiveObject(target, false, shallowReactiveHandlers)
}
// 深度只读代理 被代理的对象只读
export function readonly(target) {
return createReactiveObject(target, true, readonlyHandlers)
}
// 浅度只读代理 对象中的引用类型不会被代理且可以修改
export function shallowReadonly(target) {
return createReactiveObject(target, true, shallowReadonlyHandlers)
}
现在我们只需要关心每个api的handler即可,将视线拉到baseHandlers.ts
中
其实这四个handlers核心都是一样的 都要 get 和set ,我们先来编写get,还是和上面一样,我们创建公共的createGetter
方法,用来根据参数不同生成不同的get
const get = createGetter()
const shallowGet = createGetter(false, true)
const readonlyGet = createGetter(true)
const shallowReadonlyGet = createGetter(true, true)
// isReadonly 是否是只读
// 是否是浅代理
function createGetter(isReadonly = false, shallow = false) {
}
export const mutableHandlers = {
get,
}
export const shallowReactiveHandlers = {
get: shallowGet,
}
export const readonlyHandlers = {
get: readonlyGet,
}
export const shallowReadonlyHandlers = {
get: shallowReadonlyGet,
}
现在我们来看一下createGetter
这个方法是怎么编写的
function createGetter(isReadonly = false, shallow = false) {
// vue3的代理模式是懒代理 只有当获取值的时候才会进行代理 所以性能很高
// vue2的代理是一上来就进行递归
// target, key, receiver这些参数是new Proxy中handlers的参数,具体可以去mdn查
return function (target, key, receiver) {
// 以下写法等同 target[key] 不过使用Reflect会有很多好处 自行百度
const res = Reflect.get(target, key, receiver)
// 如果是只读代理 那么就没必要收集依赖
if (!isReadonly) {
// 收集依赖,等到数据变化后更新对应的视图
track(target, TrackOptypes.GET, key)
}
// 如果是浅代理 直接返回当前获取的值
if (shallow) return res
// 如果获取的值是对象(隐藏含义:获取的值不是浅代理且是对象,所以要继续代理当前值),判断当前代理的对象是否是readonly 如果是那么用readonly继续代理当前获取的值 反之用reactive代理当前获取的值
if (isObject(res)) return isReadonly ? readonly(res) : reactive(res)
return res
}
}
现在我们已经完成了reactive基本的框架了,并且已经实现了每个api的get方法,现在需要在index.ts将所有的api导出即可
// 导出方法 不实现功能
export {
reactive,
shallowReactive,
readonly,
shallowReadonly,
} from './reactive'
写了这么多,总要测试一下,我们可以在根目录创建一个examples文件夹,然后创建html文件进行测试
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<script src="./../node_modules/@vue/reactivity/dist/reactivity.global.js">script>
<script>
const { reactive, shallowReactive, readonly, shallowReadonly, } = VueReactivity
let obj = { name: 'zs', info: { age: 18 } }
const reactiveObj = reactive(obj)
const shallowReactiveObj = shallowReactive(obj)
const readonlyObj = readonly(obj)
const shallowReadonlyObj = shallowReadonly(obj)
console.log(reactiveObj.info)
script>
body>
html>
地址 :https://github.com/wscymdb/vu3-code/tree/reactivity1
github