序
在vue中,相信大家在使用响应式数据的时候经常碰面的两位伙伴就是ref
和reactive
了,今天我们就来聊聊用于将对象变成响应式的reactive
,不去纠结到底是用ref还是reactive,我们先来手搓reactive再来拿捏ref源码,主打一个雨露均沾!
何为reactive
在 Vue 3 中,reactive
是 Vue 提供的一个函数,用于创建响应式的对象。这是 Vue 3 中 Composition API 的一部分,旨在提供更灵活且可组合的方式来组织和重用组件逻辑。
使用 reactive
函数,我们可以将普通 JavaScript 对象转化为响应式对象,这样当对象的属性发生变化时,相关的视图将会自动更新。
让我们通过以下一个简单的例子,看看在 Vue 3 中如何使用 reactive
:
<template>
<div>
<p>{{ state.count }}</p>
<button @click="() => state.count++">add</button>
</div>
</template>
<script setup>
import { reactive } from 'vue';
const state = reactive({
count: 1,
})
</script>
在上述代码中,我们通过reactive创建了一个响应式对象state,并且给按钮添加点击事件来修改响应式对象state中count的值,在页面不刷新的情况下我们看见每当点击都会使数字+1。
手写一个reactive
要搞定reactive
就得首先有一个对象
,然后有方法能够操作里面的数据,并且最后需要将该对象处理成响应式
的。
那么,我们如何设置当响应式对象被处理数据值时触发对应的函数呢?我们来到ES6官方文档中第15条的Proxy
解析可以看到,简单来讲他就是一个agent
,它接收两个参数,第一个是被代理对象,第二个参数用于当被代理对象被读取值,设置值,判断值等等操作时会触发的函数
//baseHandlers.JS
export const mutableHandlers = {
get: function(target, key, receiver) { // target 被代理的原对象,key是原对象中的键,receiver代理后的对象
console.log('target对象被读取值了');
return target[key]
},
set: function(target, key, value, receiver) { // target
console.log('target对象被修改值了', key, value);
target[key] = value;
// 更新浏览器的视图(响应式)
}
}
// reactive.js
import { mutableHandlers } from './baseHandlers.js'
// 保存被代理过的对象
export const reactiveMap = new WeakMap() // new Map() // new WeakMap 对内存的回收更加友好
export function reactive(target) { // 将target变成响应式
return createReactiveObject(target, reactiveMap, mutableHandlers)
}
export function createReactiveObject(target, proxyMap, proxyHandlers) { // 创建响应式的函数
// 判断target是不是一个引用类型
if (typeof target !== 'object' || target === null) { // 不是对象就不给操作
return target
}
// 该对象是否已经被代理过(已经是响应式对象)
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
// 执行代理操作(将target处理成响应式)
const proxy = new Proxy(target, proxyHandlers) // 第二个参数的作用:当target被读取值,设置值,判断值等等操作时会触发的函数
// 往 proxyMap 增加 proxy, 把已经代理过的对象缓存起来
proxyMap.set(target, proxy)
return proxy
}
首先,我们简化设置了Proxy
中触发函数为修改值和设置值两种方法。
接着,我们就需要创建一个对象用来保存被代理过的对象,还有一个方法用于将对象变成响应式。
如何创建响应式对象?在上面我们已经知道了可以通过ES6中的Proxy
来实现。于是我们可以自己创建一个函数createReactiveObject
并且接收三个参数(target
, proxyMap
, proxyHandlers
),这三个参数分别代表代理前对象
,已经被代理过的对象
和该对象被操作修改时会触发的函数
对象。
由于为了避免出现以下情况,我们还需要存储已经代理过的对象,用于避免二次或多次响应式化同一个对象而造成不必要的性能损耗。
const state = reactive({
count: 1,
})
const state2 = reactive({
state
})
在函数createReactiveObject
中我们首先判断代理对象是否是引用类型,接着判断该引用类型是否已经被代理过,然后再执行代理操作, 也就是将该代理前的对象处理成响应式,最后再将该处理后的对象缓存起来,最后返回结果就可以啦!
那么最后,就让我们来在创建一个html文件检验一下手写的这份reactive是否有效呢?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="module">
import { reactive } from './reactive.js';
const state = reactive({
name: 'sAnL1ng',
age: 18
})
console.log(state.name); // 'sAnL1ng'
console.log(state.age); // 18
setInterval(() => {
state.age++
}, 3000)
</script>
</body>
</html>
需要注意打开页面需要用到Open with Live Server插件打开才能正常显示效果
结语
希望这篇reactive剖析加手写对君有所帮助,创作不易可否给个小赞赞支持一下!
假如您也和我一样,在准备春招。欢迎加我微信shunwuyu,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!
原文链接:https://juejin.cn/post/7337208702199726130 作者:sAnL1ng