MiniCPM 历史专题技术详解:BitCPM4 三值量化与 MiniCPM4 应用生态实战
2026/9/23 22:06:36
面向前端工程师的系统性解析:从设计目标到数据结构、从拦截细节到依赖收集与调度,再到ref、computed与数组、Map/Set 等容器的特殊处理。文章配套一个可运行的精简版响应式系统,帮助在源码级别建立完整心智模型。
Proxy+WeakMap实现响应式,替代 Vue2 的definePropertyeffect执行时的访问轨迹完成,核心是track与triggertargetMap: WeakMap<object, Map<key, Set<effect>>>set、add、delete与数组length、迭代依赖computed基于懒执行的effect,用dirty标记与调度器缓存结果ref以对象包装原始值,通过get/set触发依赖WeakMap避免内存泄漏;按需调度readonly、shallow、custom schedulertypeEffectFn=(()=>any)&{deps?:Set<Set<EffectFn>>;scheduler?:(job:()=>void)=>void;lazy?:boolean;};consttargetMap=newWeakMap<object,Map<any,Set<EffectFn>>>();letactiveEffect:EffectFn|null=null;consteffectStack:EffectFn[]=[];constITERATE_KEY=Symbol('iterate');constMAP_KEY_ITERATE_KEY=Symbol('map_key_iterate');effect(fn)负责在执行过程中记录所有访问到的响应式属性track(target, key)将当前activeEffect放入targetMap[target][key]的Settrigger(target, key, type)找到依赖集合并逐个执行或交由调度器处理effectStack支持嵌套effect与正确的activeEffect恢复functioncleanup(effect:EffectFn){if(!effect.deps)return;for(constdepofeffect.deps)dep.delete(effect);effect.deps.clear();}exportfunctioneffect(fn:()=>any,options:{scheduler?:(job:()=>void)=>void;lazy?:boolean}={}):EffectFn{conste:EffectFn=functionwrappedEffect(){cleanup(e);activeEffect=e;effectStack.push(e);try{returnfn();}finally{effectStack.pop();activeEffect=effectStack[effectStack.length-1]||null;}}asEffectFn;e.deps=newSet();e.scheduler=options.scheduler;e.lazy=!!options.lazy;if(!e.lazy)e();returne;}functiontrack(target:object,key:any){if(!activeEffect)return;letdepsMap=targetMap.get(target);if(!depsMap){depsMap=newMap();targetMap.set(target,depsMap);}letdep=depsMap.get(key);if(!dep){dep=newSet();depsMap.set(key,dep);}if(!dep.has(activeEffect)){dep.add(activeEffect);activeEffect.deps!.add(dep);}}functiontrigger(target:object,key:any,type:'set'|'add'|'delete'){constdepsMap=targetMap.get(target);if(!depsMap)return;consteffects=newSet<EffectFn>();constaddEffects=(dep?:Set<EffectFn>)=>{if(!dep)return;for(consteofdep)effects.add(e);};addEffects(depsMap.get(key));if(type==='add'||type==='delete')addEffects(depsMap.get(ITERATE_KEY));construn=(e:EffectFn)=>{if(e.scheduler)e.scheduler(()=>e());elsee();};for(consteofeffects)run(e);}get中进行track,并返回属性值;对对象值递归包装以保持深度响应set区分新增与修改,从而决定是否触发迭代依赖has、ownKeys读操作也需要track,迭代依赖采用ITERATE_KEYconstreactiveMap=newWeakMap<object,any>();exportfunctionreactive<Textendsobject>(obj:T):T{constexisting=reactiveMap.get(obj);if(existing)returnexisting;constproxy=newProxy(obj,{get(target,key,receiver){constres=Reflect.get(target,key,receiver);track(target,key);if(typeofres==='object'&&res!==null)returnreactive(resasobject)asany;returnres;},set(target,key,value,receiver){consthadKey=Object.prototype.hasOwnProperty.call(target,key);constoldVal=(targetasany)[key];constresult=Reflect.set(target,key,value,receiver);if(!hadKey)trigger(target,key,'add');elseif(oldVal!==value)trigger(target,key,'set');returnresult;},has(target,key){constres=Reflect.has(target,key);track(target,key);returnres;},ownKeys(target){track(target,ITERATE_KEY);returnReflect.ownKeys(target);},deleteProperty(target,key){consthadKey=Object.prototype.hasOwnProperty.call(target,key);constresult=Reflect.deleteProperty(target,key);if(hadKey&&result)trigger(target,key,'delete');returnresult;}});reactiveMap.set(obj,proxy);returnproxyasT;}ref将原始值包成对象,在value的get/set上track/triggercomputed用懒执行的effect,首次访问求值,后续由依赖变更时标记为dirtyexportfunctionref<T>(raw:T){constr={getvalue(){track(r,'value');returnraw;},setvalue(v:T){raw=v;trigger(r,'value','set');}};returnr;}exportfunctioncomputed<T>(getter:()=>T){letcached:T;letdirty=true;construnner=effect(()=>{cached=getter();},{lazy:true,scheduler:job=>job()});return{getvalue(){if(dirty){runner();dirty=false;}track(this,'value');returncached!;}};}// 基本使用conststate=reactive({count:0,nested:{a:1}});constdoubled=computed(()=>state.count*2);effect(()=>{document.querySelector('#app')!.textContent=`count=${state.count}, doubled=${doubled.value}`;});setInterval(()=>{state.count++;},1000);length变化会影响索引依赖;迭代依赖通过ITERATE_KEYMAP_KEY_ITERATE_KEY与ITERATE_KEYset.add、map.set在内部调用triggerfor...in、for...of、Object.keys、ownKeys)都需要track(ITERATE_KEY)effect可选scheduler,用于将同步触发改为异步或批处理constqueue=newSet<Function>();letflushing=false;functionqueueJob(job:Function){queue.add(job);if(!flushing){flushing=true;Promise.resolve().then(()=>{for(constjofqueue)j();queue.clear();flushing=false;});}}conststate2=reactive({x:0});conste2=effect(()=>{console.log(state2.x);},{scheduler:queueJob});state2.x=1;state2.x=2;WeakMap避免持有对已释放对象的强引用,降低泄漏风险effect中访问响应式数据,导致未收集依赖length与索引的耦合,更新行为不一致shallowReactive只包装一层;readonly禁止写入readonly、shallow、toRaw、markRaw等TrackOpTypes/TriggerOpTypes的枚举细分Vue3 响应式的核心在于“读时收集、写时触发”这条主线。以Proxy为载体、以WeakMap → Map → Set为依赖索引,辅以effect栈与调度器策略,既保证正确性与性能,又为容器类型与高级特性预留扩展空间。理解这些细节后,能够在业务中更精准地使用ref、reactive与computed,在复杂场景中定位与优化更新行为。