《Vue设计与实现》第四章 第三节 嵌套的effect

本文探讨了Vue中effect函数嵌套导致的activeEffect覆盖问题,并提出了使用副作用函数栈effectStack来解决这个问题。通过在执行副作用函数时压栈和弹栈,确保activeEffect始终指向栈顶的副作用函数,从而正确管理多个嵌套的副作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.《Vue设计与实现》第四章 第三节 嵌套的effect

嵌套的effect代码

effect(effectFn1(){
	console.log('effectFn1函数执行了')
	effect(effectFn2(){
		console.log('effectFn2函数执行了');
		obj.ok;
	})
	obj.name;
})

从上面这段代码的执行来看
effect函数的定义

function effect(fn) {
	let effectFn = () => {
		activeFffect = effectFn; 
		cleanup(effectFn)
		fn();
	}
	effectFn.deps = [];
	effectFn();
}

结果时这样的
在这里插入图片描述
结论

  1. 在同一时间内activeFffect 变量只能为一个值。
  2. 内层的副作用函数,会覆盖外层的副作用函数。
  3. 所以obj.name, obj.ok保存的副作用函数都是同一个。

如何解决这个问题
4. 我们需要一个副作用函数栈 effectStack。
5. 在副作用函数执行时,将当前的副作用函数压入栈中。
6. 待副作用函数执行完毕之后将其从栈中弹出,并始终让activeEffect指向栈顶的副作用函数

let activeEffect;
let effectStack = [];
function effect(fn) {
	const effectFn = () => {
		activeEffect = effectFn;
		effectStack.push(effectFn);
		fn();
		effectStack.pop();
		activeEffect = effectStack[effectStack.length - 1];
	}
	effectFn.deps = [];
	effectFn();
}

在这里插入图片描述
这样就完美的解决了这个effect嵌套的问题。

<think>嗯,用户之前问过关于Vue.js中如何销毁keep-alive缓存的组件,现在又问Vue3的响应式原理和如何停止响应式。看起来他们可能是在深入学习Vue,特别是Vue3的新特性。需要先回顾Vue2的响应式系统,然后对比Vue3的变化,这样用户更容易理解。 首先,用户想知道Vue3如何实现响应式。Vue3用的是Proxy,而Vue2用的是Object.defineProperty。需要解释Proxy的优势,比如可以检测到属性的添加和删除,以及更好的处理数组变化。另外,Vue3将响应式API拆分成了ref和reactive,可能需要详细说明两者的区别和使用场景。 然后是停止响应式,用户可能遇到了需要避免不必要的响应式追踪的情况,或者想优化性能。Vue3提供了markRaw和shallowRef/shallowReactive这些方法。需要解释这些API的作用,以及如何正确使用它们来停止响应式。 另外,用户可能想知道为什么需要停止响应式,比如处理大型不可变数据或第三方库集成时,防止不必要的性能开销。可能需要给出实际例子,比如使用markRaw包装一个不需要响应式的对象,或者用shallowRef来避免深层递归。 还要注意避免之前回答中提到的一些技术细节过于深入,但又要确保准确。比如,解释Proxy和Reflect的配合使用,依赖收集和触发更新的过程,以及如何通过标记对象为__v_skip来跳过代理。 最后,对比Vue2和Vue3的响应式实现,强调Vue3在性能和功能上的改进,比如对数组和对象属性的全面检测,以及更灵活的API设计。这样用户能清楚了解升级后的优势。</think>在 Vue3 中,响应式系统经过完全重构,采用 `Proxy` 替代 Vue2 的 `Object.defineProperty`。以下是详细实现原理和停止响应式的方法: --- ### 一、Vue3 响应式原理(核心流程) #### 1. 核心架构 $$ \text{响应式系统} = \text{Proxy} + \text{Reflect} + \text{依赖追踪} $$ #### 2. 实现步骤 ```javascript // 简化版响应式实现 function reactive(target) { return new Proxy(target, { get(obj, key, receiver) { track(obj, key) // 依赖收集 return Reflect.get(obj, key, receiver) }, set(obj, key, value, receiver) { Reflect.set(obj, key, value, receiver) trigger(obj, key) // 触发更新 return true } }) } ``` #### 3. 关键特性对比 | 特性 | Vue2 (Object.defineProperty) | Vue3 (Proxy) | |--------------------|-----------------------------|-----------------------| | 检测属性添加/删除 | ❌ 需特殊处理 | ✅ 自动支持 | | 数组索引修改 | ❌ 需拦截方法 | ✅ 直接检测 | | 性能表现 | 递归劫持所有属性 | 按需触发 | | Map/Set 支持 | ❌ | ✅ | --- ### 二、停止响应式的方法 #### 1. 原始值处理(自动跳过) ```javascript const rawObj = { count: 0 } // 未包裹 reactive/ref ``` #### 2. 显式停止响应式 ```javascript // 方法一:使用 markRaw import { markRaw, reactive } from 'vue' const nonReactive = markRaw({ data: '不可代理' }) const obj = reactive({ nested: nonReactive }) // nested 属性不会被代理 // 方法二:浅层响应式 const shallow = shallowReactive({ deepData: { a: 1 } }) // 仅第一层响应式 const shallowRef = shallowRef({ x: 2 }) // .value 变化触发更新,内部不递归代理 // 方法三:解除引用 let reactiveObj = reactive({ data: 'test' }) reactiveObj = null // 当没有其他引用时,GC 会自动回收 ``` #### 3. 特殊标记(内部机制) ```javascript // 添加 __v_skip 属性 const obj = { __v_skip: true, data: '跳过代理' } ``` --- ### 三、响应式控制 API 对比表 | 方法 | 作用范围 | 可逆性 | 适用场景 | |---------------------|------------------|--------|------------------------------| | `markRaw` | 对象/嵌套属性 | 永久 | 第三方库实例、大型静态数据 | | `shallowReactive` | 第一层属性 | 可转换 | 表层数据频繁变化的场景 | | `shallowRef` | .value 层级 | 可转换 | DOM 元素引用、非对象值 | | `toRaw` | 获取原始对象 | 临时 | 需要操作非代理对象时 | | 自定义 __v_skip | 整个对象 | 永久 | 框架内部特殊处理 | --- ### 四、最佳实践建议 1. **性能优化**: - 对超过 1000 个属性的对象使用 `shallowReactive` - 表格渲染数据推荐使用 `shallowRef` 2. **内存管理**: ```javascript // 清除大型响应式对象 const bigData = reactive(new Array(1e6).fill({})) // 正确释放方式 bigData.length = 0 // 保留响应式 // 完全释放 bigData.splice(0) // 触发更新后解除引用 ``` 3. **组合式 API 模式**: ```javascript import { effect, stop } from 'vue' const runner = effect(() => { console.log('响应式副作用') }) // 停止特定 effect stop(runner) ``` --- ### 五、原理级注意事项 1. Proxy 代理层级: - 默认递归代理所有嵌套对象 - 遇到 `__v_skip` 标记时停止递归 2. 响应式标识: ```javascript // 检查对象是否被代理 const isProxy = Vue.isProxy(obj) // 获取原始对象 const raw = Vue.toRaw(proxyObj) ``` 3. 性能监测: ```javascript // 在开发模式下可追踪依赖 const debugData = reactive(data, { onTrack(e) { console.log('追踪:', e) }, onTrigger(e) { console.log('触发:', e) } }) ``` --- ### 六、 Vue2 的差异总结 1. **响应式粒度**: - Vue2:组件级别追踪 - Vue3:属性级别追踪(更精准) 2. **数组处理**: - 直接通过索引修改可触发更新 ```javascript const arr = reactive([1, 2, 3]) arr[0] = 9 // 自动触发更新 ``` 3. **集合类型支持**: ```javascript const map = reactive(new Map()) map.set('key', 'value') // 触发更新 ``` 根据实际需求选择合适的响应式控制方式,合理使用这些特性可以使应用性能提升 30%-50%(根据场景不同)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值