实现 reactive 函数
第 1 步#
packages/reactivity/src/reactiev.ts
import { isObject } from "@vue/shared";
const mutableHandlers:ProxyHandler<Record<any, any>> = {
// recevier 表示代理对象的本身,即 createdReactiveObject 函数中的 proxy
get(target, key, recevier) {
// 只是相当于 target[key],并不是等于
// 使用 Reflect 主要是他会对一些边界条件做一些处理,如取不到该属性,同时设置值的时候,还会自动返回 true 或者 false,同时,他还可以处理 this 问题
const res = Reflect.get(target, key, recevier)
return res
},
set(target, key, value, recevier) {
// 相当于 target[key] = value,同时会返回 true
return Reflect.set(target, key, value, recevier)
}
}
function createdReactiveObject(target: object) {
// 判断是否是个对象,reactive 只能对对象做处理
if (!isObject(target)) {
return target
}
const proxy = new Proxy(target, mutableHandlers)
return proxy
}
export function reactive(target: object) {
return createdReactiveObject(target)
}
packages/reactivity/src/index.ts
export { reactive } from './reactive'
packages/reactivity/example/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="../dist/reactivity.global.js"></script>
<script>
const {reactive, effect} = VueReactivity
const obj = {
name: 'xiaoming',
age: 12
}
const state = reactive(obj)
console.log(state);
</script>
</body>
</html>
效果如下:
第 2 步#
第 1 步的问题:当同一个对象被代理两次的时候,两次代理的结果是不相同的,所以需要对代理对象进行缓存packages/reactivity/src/reactiev.ts
import { isObject } from "@vue/shared"
const mutableHandlers:ProxyHandler<Record<any, any>> = {
// recevier 表示代理对象的本身,即 createdReactiveObject 函数中的 proxy
get(target, key, recevier) {
// 等价于 target[key]
// 使用 Reflect 主要是他会对一些边界条件做一些处理,如取不到该属性,同时设置值的时候,还会自动返回 true 或者 false
const res = Reflect.get(target, key, recevier)
return res
},
set(target, key, value, recevier) {
// 等价于 target[key] = value,同时会返回 true
return Reflect.set(target, key, value, recevier)
}
}
// --------------------------新增 start-----------------------------
// 当对同一个对象进行两次代理的时候,两次的结果应该是相同的,所以这里需要保存一下代理的对象
const reactiveMap = new WeakMap()
// --------------------------新增 end-------------------------------
function createdReactiveObject(target: object) {
// 判断是否是个对象,reactive 只能对对象做处理
if (!isObject(target)) {
return target
}
// --------------------------新增 start-----------------------------
// 如果当前对象已经被代理过,则直接返回,不需要再进行代理
const existingProxy = reactiveMap.get(target)
if (existingProxy) {
return existingProxy
}
// --------------------------新增 end-------------------------------
const proxy = new Proxy(target, mutableHandlers)
// --------------------------新增 start-----------------------------
// 保存当前代理的对象
reactiveMap.set(target, proxy)
// --------------------------新增 end-------------------------------
return proxy
}
export function reactive(target: object) {
return createdReactiveObject(target)
}
packages/reactivity/example/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="../dist/reactivity.global.js"></script>
<script>
const {reactive, effect} = VueReactivity
const obj = {
name: 'xiaoming',
age: 12
}
const state1 = reactive(obj)
const state2 = reactive(obj)
console.log(state1 === state2) // true
</script>
</body>
</html>
效果如下:
第 3 步#
第 2 步的问题:当对一个代理对象再次代理后,和第一次代理的结果不相等,但应该是相等的,所以需要添加一个标识来判断是否是代理对象。
这里和第 2 步解决的问题是不一样的,第 2 步解决的是一个普通对象被重复代理的问题,第 3 步解决的是一个代理对象被再次代理的问题
packages/reactivity/src/reactiev.ts
import { isObject } from "@vue/shared"
// --------------------------新增 start-----------------------------
const enum ReactiveFlags {
IS_REACTIVE = '__v_isReactive'
}
// --------------------------新增 end-------------------------------
const mutableHandlers:ProxyHandler<Record<any, any>> = {
// recevier 表示代理对象的本身,即 createdReactiveObject 函数中的 proxy
get(target, key, recevier) {
// --------------------------新增 start-----------------------------
// 判断 target 对象中是否有 __v_isReactive 属性,如果有,则表示该对象被代理过
if (key === ReactiveFlags.IS_REACTIVE) {
return true
}
// --------------------------新增 end-----------------------------
// 等价于 target[key]
// 使用 Reflect 主要是他会对一些边界条件做一些处理,如取不到该属性,同时设置值的时候,还会自动返回 true 或者 false
const res = Reflect.get(target, key, recevier)
return res
},
set(target, key, value, recevier) {
// 等价于 target[key] = value,同时会返回 true
return Reflect.set(target, key, value, recevier)
}
}
// 当对同一个对象进行两次代理的时候,两次的结果应该是相同的,所以这里需要保存一下代理的对象
const reactiveMap = new WeakMap()
function createdReactiveObject(target: object) {
// --------------------------新增 start-----------------------------
// 默认认为 target 已经被代理过了
// 此时先去 target 找 __v_isReactive 属性,如果 target 没有被代理过,那么就会返回 undefined,则会直接往下走进行代理
if ((target as any)[ReactiveFlags.IS_REACTIVE]) {
return target
}
// --------------------------新增 end-----------------------------
// 判断是否是个对象,reactive 只能对对象做处理
if (!isObject(target)) {
return target
}
// 如果当前对象已经被代理过,则直接返回,不需要再进行代理
const existingProxy = reactiveMap.get(target)
if (existingProxy) {
return existingProxy
}
const proxy = new Proxy(target, mutableHandlers)
// 保存当前代理的对象
reactiveMap.set(target, proxy)
return proxy
}
export function reactive(target: object) {
return createdReactiveObject(target)
}
packages/reactivity/example/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="../dist/reactivity.global.js"></script>
<script>
const {reactive, effect} = VueReactivity
const obj = {
name: 'xiaoming',
age: 12
}
const state1 = reactive(obj)
const state2 = reactive(obj)
console.log(reactive(state1) === state2)
</script>
</body>
</html>
效果如下:
推荐文章: