vue3响应式reactive的实现原理;proxy深层代理;vue3响应式api
-
参考链接: https://segmentfault.com/a/1190000039194351
我们已经知道vue3的响应式实现从defineProperty变成了proxy
defineProperty有个弊端,只能监听已有属性的变化,新增属性就监听不到,vue2时,需要配合Vue.set来把新增的属性变成响应式;
defineProperty实现响应式:
const obj1 = {}; Object.defineProperty(obj1, 'a', { get() { console.log('get1'); }, set() { console.log('set1'); } }); obj1.b = 2; // 监听不到
proxy实现响应式
const o = { name: 'tom', info: { age: 18 } } const po = new Proxy(o, { get(target, key, proxy) { console.log(`get key:${key}`); return Reflect.get(...arguments); }, set(target, key, value, proxy){ console.log(`set key:${key}, value: ${value}`); return Reflect.set(...arguments); } })
上面就是proxy实现响应式的基本原理
但是有个问题,上面的代码只是潜监听,也就是只监听到了对象第一层属性;
下面是vue3中reacttive源码精简后的代码,看看是如何深层监听
// 工具方法:判断是否是一个对象(注:typeof 数组 也等于 'object' const isObject = val => val !== null && typeof val === 'object'; // 工具方法:值是否改变,改变才触发更新 const hasChanged = (value, oldValue) => value !== oldValue && (value === value || oldValue === oldValue); // 工具方法:判断当前的 key 是否是已经存在的 const hasOwn = (val, key) => hasOwnProperty.call(val, key); // 闭包:生成一个 get 方法 function createGetter() { return function get(target, key, receiver) { const res = Reflect.get(target, key, receiver); console.log(`getting key:${key}`); // track(target, 'get' /* GET */, key); // 深层代理对象的关键!!!判断这个属性是否是一个对象,是的话继续代理动作,使对象内部的值可追踪 if (isObject(res)) { return reactive(res); } return res; }; } // 闭包:生成一个 set 方法 function createSetter() { return function set(target, key, value, receiver) { const oldValue = target[key]; const hadKey = hasOwn(target, key); const result = Reflect.set(target, key, value, receiver); // 判断当前 key 是否已经存在,不存在的话表示为新增的 key ,后续 Vue “标记”新的值使它其成为响应式 if (!hadKey) { console.log(`add key:${key},value:${value}`); // trigger(target, 'add' /* ADD */, key, value); } else if (hasChanged(value, oldValue)) { console.log(`set key:${key},value:${value}`); // trigger(target, 'set' /* SET */, key, value, oldValue); } return result; }; } const get = createGetter(); const set = createSetter(); // 基础的处理器对象 const mutableHandlers = { get, set // deleteProperty }; // 暴露出去的方法,reactive function reactive(target) { return createReactiveObject(target, mutableHandlers); } // 创建一个响应式对象 function createReactiveObject(target, baseHandlers) { const proxy = new Proxy(target, baseHandlers); return proxy; } const proxyObj = reactive({ id: 1, name: 'front-refined', childObj: { hobby: 'coding' } }); proxyObj.childObj.hobby // get key:childObj // get key:hobby proxyObj.childObj.hobby="play" // get key:childObj // set key:hobby,value:play
这样就监听到更深层的属性变化了
ref实现:
// 工具方法:值是否改变,改变才触发更新 const hasChanged = (value, oldValue) => value !== oldValue && (value === value || oldValue === oldValue); // 工具方法:判断是否是一个对象(注:typeof 数组 也等于 'object' const isObject = val => val !== null && typeof val === 'object'; // 工具方法:判断传入的值是否是一个对象,是的话就用 reactive 来代理 const convert = val => (isObject(val) ? reactive(val) : val); function toRaw(observed) { return (observed && toRaw(observed['__v_raw' /* RAW */])) || observed; } // ref 实现类 class RefImpl { constructor(_rawValue, _shallow = false) { this._rawValue = _rawValue; this._shallow = _shallow; this.__v_isRef = true; this._value = _shallow ? _rawValue : convert(_rawValue); } get value() { // track(toRaw(this), 'get' /* GET */, 'value'); return this._value; } set value(newVal) { if (hasChanged(toRaw(newVal), this._rawValue)) { this._rawValue = newVal; this._value = this._shallow ? newVal : convert(newVal); // trigger(toRaw(this), 'set' /* SET */, 'value', newVal); } } } // 创建一个 ref function createRef(rawValue, shallow = false) { return new RefImpl(rawValue, shallow); } // 暴露出去的方法,ref function ref(value) { return createRef(value); } // 暴露出去的方法,shallowRef function shallowRef(value) { return createRef(value, true); }
vue3响应式api
reactive:把对象变成响应式(深度监听)
function isRef(r) { return Boolean(r && r.__v_isRef === true); } function unref(ref) { return isRef(ref) ? ref.value : ref; }
triggerRef:个人理解是对shallowRef做的一个api,shallowRef只监听对value的改变,如果对深层的属性改变是不会触发页面渲染的,当改变深层属性后,用triggerRef再去触发页面更新
const shallowRefObj = shallowRef({ name: 'front-refined' }); // 这里不会触发副作用,因为是这个 ref 是浅层的 shallowRefObj.value.name = 'hello~'; // 手动执行与 shallowRef 关联的任何副作用,这样子就能触发了。 triggerRef(shallowRefObj);
customRef: 自定义的 ref 。这个 API 就更显式的让我们了解 track 与 trigger,看个例子:
<template> <div>name:{{name}}</div> <input v-model="name" /> </template> // ... setup() { let value = 'front-refined'; // 参数是一个工厂函数 const name = customRef((track, trigger) => { return { get() { // 收集依赖它的 effect track(); return value; }, set(newValue) { value = newValue; // 触发更新依赖它的所有 effect trigger(); } }; }); return { name }; }
toRef:可以用来为响应式对象上的 property 新创建一个 ref ,从而保持对其源 property 的响应式连接
const object = reactive({ name: 'tom', info: { age: 19 } }) const name = toRef(object, 'name');
name.value = 1; // object.name也会变成1
toRefs:把响应式对象的所有propety都创建成ref对象(内部是toRef实现)
compouted:计算属性(依赖响应式数据),如果计算公式里没有响应式数据,那么compouted就不是响应式数据
watch:监听响应式数据
watch( [() => state.id, () => state.name], ([id, name], [oldId, oldName]) => { /* ... */ } );
watchEffect( (onInvalidate) => { console.log(a.value, 'a==='); onInvalidate(()=>{ console.log('清除'); }) }, { onTrigger(e) { console.log(e, 'onTrigger=='); }, onTrack(e) { console.log(e, 'onTrack==='); } }) setTimeout(() => { a.value += 1; }, 1000)
isReactive:检查对象是不是reactive创建的响应式proxy
isProxy:检查对象是否是由 reactive 或 readonly 创建的 proxy。
toRaw: 把响应式对象转化为非响应式对象(去除响应式),当转化ref生成的响应式对象时,要传入refObject.value,才能正确转化,否则还会返回ref的格式
isRef:判断是否是 ref 对象
-
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· 单线程的Redis速度为什么快?
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
2019-06-03 调用七牛云存储文件,返回url