trigger

本文详细介绍Oracle数据库中触发器的应用,包括基本的用户触发器,以及限制条件触发器,如仅允许在周六9-17点删除用户表数据的实现方式。通过具体代码示例,深入解析触发器的工作原理及如何在实际场景中应用。

屏幕上输出显示:set serverout on

trigger定义:

create or replace trigger user_trigger

before delete on t_user

begin

  dbms_output.put_line('有人删除用户表数据!!!');

end;

/

 

仅在周六9-17点可以删除用户表数据

create or replace trigger user_delete_trigger

before delete on t_user [for each row] --这种方式无论删除多少条数据只执行一次(for each row:每删除一条数据都会执行一次)

begin

  if to_char(sysdate, 'DY') not in ('SAT')

     or

     to_char(sysdate, 'fmhh24') not between 9 and 17

  then

    raise_application_error(-20000, '仅在周六9-17点可以删除用户表数据');

  end if;

end;

/

  dbms_output.put_line('有人删除用户表数据!!!');

end;

/

 

for each row:可以通过:old获取到删除的那一条数据,:new获取到删除后的数据即空数据,update操作时:old指修改前的数据,:new指修改后的数据

转载于:https://www.cnblogs.com/cyf18/p/10765913.html

<think>我们正在讨论Vue3中proxy的trigger实现机制。根据之前的引用和问题,用户已经了解了Vue3的响应式系统基于Proxy,现在想深入trigger的源码实现。Vue3的响应式系统核心包括:-reactive:使用Proxy创建响应式对象-effect:用于创建响应式副作用(例如组件的渲染函数、计算属性等)-track:在读取响应式属性时,收集当前运行的effect作为依赖-trigger:在设置响应式属性时,触发所有收集到的依赖(即effect)重新执行现在重点分析trigger的实现。从引用[2]中我们知道,trigger函数在响应式对象的属性被修改时被调用(在Proxy的set拦截器中)。它的作用是通知所有依赖于该属性的effect,让它们重新执行。引用[3]提到:trigger函数采用调度器机制优化执行顺序。接下来,我们将从源码角度分析trigger的实现。源码位置:`packages/reactivity/src/effect.ts`###1.trigger函数的作用trigger函数的主要功能是:-根据目标对象(target)和键(key),从全局的依赖映射(targetMap)中找到对应的所有effect(依赖集合)-将这些effect加入待执行队列,并根据调度器设置(scheduler)执行它们###2.核心数据结构在Vue3的响应式系统中,依赖关系存储在一个全局的WeakMap中,结构如下:```typescripttypeDep=Set<ReactiveEffect>//依赖集合typeKeyToDepMap=Map<any,Dep>//键到依赖集合的映射consttargetMap=newWeakMap<any,KeyToDepMap>()//目标对象到键映射的映射```-`targetMap`:以响应式对象为键,值是一个Map(即`KeyToDepMap`)-`KeyToDepMap`:以响应式对象的属性键为键,值是一个Set(即`Dep`),该Set存储了所有依赖于该属性的effect###3.trigger函数源码解析简化后的trigger函数源码(去除开发环境警告和边缘情况处理):```typescript//源码位置:packages/reactivity/src/effect.tsexportfunctiontrigger(target:object,type:TriggerOpTypes,//操作类型,如SET、ADD、DELETE等key?:unknown,newValue?:unknown,oldValue?:unknown,oldTarget?:Map<unknown,unknown>|Set<unknown>){//1.从targetMap中获取该目标对象的依赖映射constdepsMap=targetMap.get(target)if(!depsMap){return//从未被追踪,直接返回}//2.创建需要触发的effect集合(避免重复和递归触发)consteffects=newSet<ReactiveEffect>()constadd=(effectsToAdd:Set<ReactiveEffect>|undefined)=>{if(effectsToAdd){effectsToAdd.forEach(effect=>{//避免当前正在执行的effect立即再次触发,导致无限循环if(effect!==activeEffect||effect.allowRecurse){effects.add(effect)}})}}//3.根据操作类型收集需要触发的依赖//3.1针对属性修改(SET)等情况,触发该属性的依赖if(key!==void0){add(depsMap.get(key))}//3.2针对数组长度变化或集合操作,额外处理switch(type){caseTriggerOpTypes.ADD://对于数组,添加元素可能影响长度;对于集合,添加元素需要触发迭代器依赖if(isArray(target)&&key==='length'){depsMap.forEach((dep,key)=>{if(key==='length'||key>=(newValueasnumber)){add(dep)}})}else{//对象添加新属性,触发ITERATE_KEY依赖(for...in等)add(depsMap.get(ITERATE_KEY))if(isMap(target)){add(depsMap.get(MAP_KEY_ITERATE_KEY))}}breakcaseTriggerOpTypes.DELETE://删除属性,同样触发ITERATE_KEYadd(depsMap.get(ITERATE_KEY))if(isMap(target)){add(depsMap.get(MAP_KEY_ITERATE_KEY))}breakcaseTriggerOpTypes.SET:if(isMap(target)){add(depsMap.get(MAP_KEY_ITERATE_KEY))}break}//4.触发所有收集到的effectconstrun=(effect:ReactiveEffect)=>{//如果有调度器,则使用调度器执行,否则直接运行if(effect.options.scheduler){effect.options.scheduler(effect)}else{effect()}}effects.forEach(run)}```###4.关键点解析1.**操作类型(TriggerOpTypes)**-`ADD`:添加新属性(对象或数组)-`SET`:修改已有属性-`DELETE`:删除属性-这些类型决定了除了直接修改的key之外,还需要触发哪些额外的依赖(比如数组的length,或对象的迭代依赖)2.**迭代依赖的触发**-对于对象,使用`for...in`循环会收集在`ITERATE_KEY`(一个Symbol)上-对于Map,`keys()`方法收集在`MAP_KEY_ITERATE_KEY`上3.**调度器(scheduler)**-每个effect可以有一个调度器(scheduler),用于控制effect的执行时机-Vue3的组件更新使用调度器实现异步批处理(通过`queueJob`)###5.性能优化-使用`Set`避免重复触发同一个effect-通过操作类型精准触发必要的依赖(避免不必要的重新渲染)###示例场景假设有一个响应式对象:```javascriptconstobj=reactive({count:0})```当执行`obj.count++`时:1.触发Proxy的`set`拦截器2.调用`trigger(obj,TriggerOpTypes.SET,'count')`3.在`targetMap`中找到`obj`对应的`depsMap`,再找到`'count'`对应的依赖集合4.将这些effect加入执行队列(如果有调度器则按调度器规则执行,否则立即执行)###与调度器整合在Vue3中,组件的更新effect被设置了调度器(`componentUpdateFn`会被放入队列,在下一个tick批量执行):```typescript//在组件挂载时设置instance.update=effect(componentUpdateFn,{scheduler:()=>{queueJob(instance.update)}})```###总结`trigger`机制是Vue3响应式更新的核心,它:1.精准定位需要更新的依赖2.通过调度器实现异步批处理更新3.处理了多种数据操作类型(对象/数组/集合)的依赖触发---###相关问题1.Vue3中track和trigger是如何协作的?2.调度器(scheduler)在Vue3响应式系统中起什么作用?3.如何避免trigger触发时的无限递归循环?4.Vue3响应式系统如何处理数组和集合的变化?5.为什么Vue3使用WeakMap作为依赖存储的数据结构?</think>### Vue3 Proxy Trigger 实现原理与源码分析 Vue3 的响应式系统核心是 `trigger` 函数,它在响应式对象的属性被修改时触发依赖更新。其实现机制可分为四个关键环节: #### 1. **核心数据结构** ```typescript // 全局依赖存储结构 const targetMap = new WeakMap<object, Map<any, Dep>>() // 依赖集合类型 type Dep = Set<ReactiveEffect> ``` - `targetMap`:全局 WeakMap,以响应式对象为键 - 值是一个 Map,以属性键为键,值为依赖集合(Set) - 使用 WeakMap 防止内存泄漏,当对象被回收时自动清除依赖[^1][^3] #### 2. **trigger 函数实现** 源码位置:`packages/reactivity/src/effect.ts` ```typescript export function trigger( target: object, type: TriggerOpTypes, // 操作类型:SET/ADD/DELETE等 key?: unknown, newValue?: unknown, oldValue?: unknown ) { // 1. 获取目标对象的依赖映射 const depsMap = targetMap.get(target) if (!depsMap) return // 无依赖直接返回 // 2. 创建待触发 effect 集合 const effects = new Set<ReactiveEffect>() // 3. 收集依赖函数 const add = (effectsToAdd: Dep | undefined) => { if (effectsToAdd) { effectsToAdd.forEach(effect => { // 避免递归触发 if (effect !== activeEffect || effect.allowRecurse) { effects.add(effect) } }) } } // 4. 根据操作类型收集依赖 if (key !== void 0) { add(depsMap.get(key)) // 收集该属性的直接依赖 } // 5. 特殊操作类型处理 switch (type) { case TriggerOpTypes.ADD: // 数组添加元素需触发 length 依赖 if (isArray(target) && key === 'length') { depsMap.forEach((dep, key) => { if (key === 'length' || key >= (newValue as number)) { add(dep) } }) } break case TriggerOpTypes.DELETE: // 删除操作触发迭代依赖 add(depsMap.get(ITERATE_KEY)) break } // 6. 执行依赖更新 const run = (effect: ReactiveEffect) => { if (effect.options.scheduler) { // 使用调度器异步执行 effect.options.scheduler(effect) } else { // 同步执行 effect() } } effects.forEach(run) } ``` #### 3. **关键机制解析** 1. **操作类型分类** ```typescript export const enum TriggerOpTypes { SET = 'set', ADD = 'add', DELETE = 'delete', CLEAR = 'clear' } ``` - 不同操作类型触发不同的依赖收集逻辑[^2] 2. **调度器机制** - 通过 `scheduler` 实现异步批量更新 - 组件更新使用 `queueJob` 调度器: ```typescript effect(() => { /* render */ }, { scheduler: () => queueJob(update) }) ``` 3. **递归触发防护** ```typescript if (effect !== activeEffect || effect.allowRecurse) { effects.add(effect) } ``` - 防止当前正在执行的 effect 立即触发导致无限循环 #### 4. **与 Proxy 拦截器集成** 在 `reactive` 的 set 拦截器中调用 trigger: ```typescript // packages/reactivity/src/reactive.ts const mutableHandlers: ProxyHandler<object> = { set(target, key, value, receiver) { // 获取旧值 const oldValue = (target as any)[key] // 执行反射设置 const result = Reflect.set(target, key, value, receiver) // 触发更新 if (!shallow) { trigger(target, TriggerOpTypes.SET, key, value, oldValue) } return result } } ``` #### 5. **性能优化设计** 1. **精准依赖触发** - 通过操作类型和 key 精准定位需要更新的依赖 - 避免不必要的组件重渲染 2. **批量异步更新** - 调度器确保多个变更合并为单次更新 - 默认使用微任务队列(Promise.then) 3. **迭代键优化** ```typescript const ITERATE_KEY = Symbol('iterate') ``` - 对 `for...in` 等迭代操作使用特殊键收集依赖 - 避免为每个迭代键单独存储依赖 ### 总结流程 1. **依赖收集**:读取操作时通过 `track` 收集依赖到 `targetMap` 2. **修改拦截**:Proxy set 拦截器捕获修改操作 3. **触发更新**:调用 `trigger` 根据操作类型收集相关依赖 4. **调度执行**:通过调度器异步执行 effect 更新 --- ### 相关问题 1. Vue3 的 track 和 trigger 如何协同工作? 2. 调度器(scheduler)在响应式系统中起什么作用? 3. 如何避免 trigger 触发时的无限递归循环? 4. Vue3 响应式系统如何处理数组和集合的变化? 5. 为什么 Vue3 使用 WeakMap 作为依赖存储的数据结构? [^1]: Vue3 响应式系统核心实现原理 [^2]: Vue3 响应性原理以及源码分析 [^3]: Vue3 响应式原理源码解析
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值