怎么提交网站网站推广公司官网
2026/1/2 10:43:50 网站建设 项目流程
怎么提交网站,网站推广公司官网,深圳软件定制,vue做直播网站本人承接各种网站、跨端、小程序等开发项目#xff0c;有需要可私信我 大家好, 今天我们来聊一聊 Vue 框架中最核心的部分——响应式系统。 引言#xff1a;为什么响应式是 Vue 的灵魂#xff1f; 响应式是Vue的灵魂#xff0c;因为它实现了Vue最核心的承诺#xff1a;数…本人承接各种网站、跨端、小程序等开发项目有需要可私信我大家好, 今天我们来聊一聊 Vue 框架中最核心的部分——响应式系统。引言为什么响应式是 Vue 的灵魂响应式是Vue的灵魂因为它实现了Vue最核心的承诺数据驱动视图。这不仅仅是技术实现更是根本性的设计哲学直观的数据、视图绑定虚拟的DOM实现框架内所有API的支撑等。响应式让Vue从一个UI库变成了一套思维范式。它决定了你如何思考问题关注数据状态而非DOM操作而不仅仅是如何写代码。文章目录引言为什么响应式是 Vue 的灵魂一、响应式系统演进史1.1 初代响应式脏值检测AngularJS1.2 Vue1 的细粒度响应式1.3 Vue2 的改进依赖收集与批量更新二、Vue2 Object.defineProperty 深度解析2.1 Object.defineProperty2.2 存在的局限性2.3 总结三、Vue3 Proxy新响应式方案3.1 Proxy代理3.2 存在的局限性3.3 总结Proxy 的时代意义四、defineProperty VS Proxy 性能优化的全方位对比4.1 内存优化按需代理 vs 全量劫持4.2 性能基准五、Proxy 在实际项目中的应用5.1 Vue3 响应式源码解析5.2 自定义响应式工具库六、高频面试题解析问题1Object.defineProperty 和 Proxy 的主要区别问题2Vue3 的响应式相比 Vue2 有哪些性能优化问题3如何手动实现一个简单的响应式系统七、总结下期预告一、响应式系统演进史1.1 初代响应式脏值检测AngularJS在 Vue 出现之前AngularJS 使用脏值检测机制。问题性能开销大需要遍历所有监视器。// AngularJS 的脏检查scope.$watch(user.name,function(newVal,oldVal){console.log(用户姓名变化:,oldVal,-,newVal);});// 需要手动触发检测scope.$digest();1.2 Vue1 的细粒度响应式Vue1 为每个数据创建 Watcher实现细粒度更新。虽然能精准更新但是Watcher 过多内存消耗大。// Vue1 响应式理念newVue({data:{user:{name:张三}},watch:{user.name:function(val){console.log(姓名变化:,val);}}});1.3 Vue2 的改进依赖收集与批量更新Vue2 引入了虚拟 DOM 和组件级响应式。// Vue2 组件级更新exportdefault{data(){return{user:{name:张三},posts:[]};},methods:{updateUserName(){this.user.name李四;// 触发组件重新渲染}}};二、Vue2 Object.defineProperty 深度解析2.1 Object.definePropertyObject.defineProperty 是 ES5 提供的对象属性定义工具它的核心能力是劫持属性的访问和赋值操作。就像给数据安装了一扇门每次读写都会被记录和控制。普通对象/数组 → 观测器(Observer) → 属性劫持 → 响应式变量 ↓ ↓ ↓ ↓ {name: 张三} 递归遍历每个属性 getter/setter 数据变化自动更新defineProperty 的核心语法Object.defineProperty(obj,property,{configurable:true,// ① 能否删除属性或重新定义enumerable:true,// ② 能否被 for-in 循环枚举writable:true,// ③ 能否被赋值修改value:undefined,// 直接设置值与 get/set 互斥// 核心拦截器get(){...},// 劫持读操作set(newVal){...}// 劫持写操作})下面**手写一个简单的响应式系统Observe函数**来理解原理。functiondefineReactive(obj,key,val){// 每个属性都创建一个依赖收集器constdeps[];Object.defineProperty(obj,key,{enumerable:true,configurable:true,get(){// 收集依赖当前有观察者在读取这个属性吗if(currentWatcher){// 记录这个观察者依赖我deps.push(currentWatcher);}returnval;},set(newVal){if(valnewVal)return;valnewVal;// 更新我的值变了通知所有依赖我的观察者deps.forEach(watcher{watcher.update();// 触发更新});}});}// 遍历对象使其响应式functionobserve(obj){if(typeofobj!object||objnull){return;}Object.keys(obj).forEach(key{defineReactive(obj,key,obj[key]);});}// 使用constdata{user:张三};observe(data);data.user李四;// 触发更新2.2 存在的局限性1数组操作的限制通过索引设置数值vm.items[0] 新值修改数组长度vm.items.length 0都不会触发更新因为并没有对数组的索引、数组的length属性使用Object.defineProperty进行劫持因为这样做的性能开销很大。// 对应解决方案Vue.set(vm.items,0,新值);// 或vm.items.splice(0,1,新值);2动态增删属性对象中的属性增加vm.user.newProp value、删除delete vm.user.name不会触发更新因为新属性没有被Object.defineProperty处理删除属性时vue也无法知道属性被删除了也就无法通知依赖更新。对应解决方案如下// 对于新增属性Vue.set(vm.user,newProp,value);// 或vm.$set(vm.user,newProp,value);// 对于删除属性Vue.delete(vm.user,name);// 或vm.$delete(vm.user,name);2.3 总结Vue2响应式核心思想· 递归劫持深度遍历对象的所有属性用 Object.defineProperty 重写 getter/setter· 依赖收集在 getter 中收集谁在观察这个数据· 派发更新在 setter 中通知所有观察者数据变化了· 数组特例重写数组方法来实现响应式· 动态增删通过 Vue.set/delete 特殊 API 处理三、Vue3 Proxy新响应式方案3.1 Proxy代理Proxy是 ES6 引入的一个强大的元编程特性它允许你创建一个对象的代理从而可以拦截和定义该对象的基本操作就像给对象包装一个盒子对象内数据的读写都要经过这个盒子的处理。Proxy的基本语法const proxy new Proxy(target, handler)。target包装对象任何类型的对象包括数组、函数甚至另一个代理。handler通常为函数函数内各属性中的实现分别定义了在执行各种操作时代理的行为。Handler 对象可以定义以下拦截方法部分get(target,property,receiver)拦截对象属性的读取。set(target,property,value,receiver)拦截对象属性的设置。deleteProperty(target,property)拦截delete操作。defineProperty(target,property,descriptor)拦截 Object.defineProperty()。setPrototypeOf(target,prototype)拦截 Object.setPrototypeOf()。apply(target,thisArg,argumentsList)拦截函数的调用、call 和 apply 操作。construct(target,argumentsList,newTarget)拦截new操作符。Vue 3 的响应式系统使用 Proxy 来追踪对象属性的访问和修改。当读取属性时收集依赖当修改属性时触发更新。下面手写一个基于Proxy的响应式来理解其原理。// 使用 Proxy 实现响应式functionreactive(target){// 返回一个代理对象returnnewProxy(target,{// 拦截读方法get(target,key,receiver){// Reflect 会保持对象的所有约束如不可写属性、setter等constresReflect.get(target,key,receiver);track(target,key);// 收集依赖// 如果结果是对象则递归转为响应式returntypeofresobject?reactive(res):res;},// 拦截写方法set(target,key,value,receiver){constoldValuetarget[key];constresultReflect.set(target,key,value,receiver);if(resultoldValue!value){trigger(target,key);// 触发更新}returnresult;},// 拦截删除属性方法deleteProperty(target,key){consthadKeyhasOwn(target,key);constresultReflect.deleteProperty(target,key);if(resulthadKey){trigger(target,key);// 触发更新}returnresult;}});}// 依赖收集consttargetMapnewWeakMap();functiontrack(target,key){if(activeEffect){letdepsMaptargetMap.get(target);if(!depsMap){targetMap.set(target,(depsMapnewMap()));}letdepdepsMap.get(key);if(!dep){depsMap.set(key,(depnewSet()));}dep.add(activeEffect);}}// 依赖触发functiontrigger(target,key){constdepsMaptargetMap.get(target);if(!depsMap)return;//consteffectsdepsMap.get(key);effectseffects.forEach(effecteffect());}3.2 存在的局限性· Proxy 只能代理对象不能代理基本类型如字符串、数字、布尔值。· Proxy 的 this 问题在 Proxy 的 handler 方法中this 指向的是 handler 对象而不是被代理的目标对象。因此在需要访问目标对象时通常使用第一个参数 target。constobj{name:Vue,getName(){returnthis.name}// this 指向谁}constproxynewProxy(obj,{get(target,key,receiver){// receiver 是 proxy 本身constvalueReflect.get(target,key,receiver);if(typeofvaluefunction){// 绑定正确的 thisreturnvalue.bind(receiver)}returnvalue}})console.log(proxy.getName())// ✅ 正确输出 Vue· 代理对象的原型链Proxy 可以代理整个对象包括原型链。但若目标对象是一个原型链上的对象那么对原型链上属性的访问也会被拦截。3.3 总结Proxy 的时代意义Proxy 代表了 **JavaScript 元编程的成熟。**它不仅仅是 Vue 3 响应式的实现基础更是· 语言能力的体现ES6 给开发者提供的底层钩子· 设计模式的典范代理模式的完美实现· 未来框架的基础为更多创新性框架提供可能· 开发者思维的转变从如何修改对象到如何描述对象行为四、defineProperty VS Proxy 性能优化的全方位对比4.1 内存优化按需代理 vs 全量劫持Object.defineProperty是一次性劫持所有属性Proxy是按需代理。因此在内存优化方面大型对象中Proxy 可以节省 30-50% 的内存。//Object.definePropertyfunctiondefineAllProperties(obj){// 循环挟持多有属性Object.keys(obj).forEach(key{defineReactive(obj,key,obj[key]);});// 对于嵌套对象Object.keys(obj).forEach(key{if(typeofobj[key]object){defineAllProperties(obj[key]);// 递归劫持}});}// ProxyconstproxynewProxy(obj,{get(target,key){constvaluetarget[key];if(typeofvalueobjectvalue!null){// 只有访问到时才代理嵌套对象returnreactive(value);}returnvalue;}});4.2 性能基准编写测试代码来进行性能测试。// 测试代码consttestData{/* 包含1000个属性的对象 */};// Object.defineProperty 版本console.time(defineProperty);constobserved1observeWithDefineProperty(testData);console.timeEnd(defineProperty);// Proxy 版本console.time(proxy);constobserved2observeWithProxy(testData);console.timeEnd(proxy);// 访问性能测试 definePropertyconsole.time(access-defineProperty);for(leti0;i10000;i){observed1[prop(i%1000)];}console.timeEnd(access-defineProperty);// 访问性能测试 proxyconsole.time(access-proxy);for(leti0;i10000;i){observed2[prop(i%1000)];}console.timeEnd(access-proxy);初始化Proxy 稍慢但可接受访问速度两者相当内存占用Proxy 明显更低五、Proxy 在实际项目中的应用5.1 Vue3 响应式源码解析下面是简化版的Vue3的响应式核心functioncreateReactiveObject(target,baseHandlers){// isObject 是类型检查函数判断是否为对象或数组if(!isObject(target)){returntarget;}// 从 proxyMap 中查找是否已经有该对象的代理constexistingProxyproxyMap.get(target);if(existingProxy){returnexistingProxy;}// 不存在的则创建新的 Proxy 代理对象constproxynewProxy(target,baseHandlers);proxyMap.set(target,proxy);returnproxy;}// 基本处理器constmutableHandlers{get(target,key,receiver){// 依赖收集track(target,key);constresReflect.get(target,key,receiver);// 自动解包 refif(isRef(res)){returnres.value;}// 深层响应式// 只在访问对象属性时才递归转换为响应式, 惰性代理if(isObject(res)){returnreactive(res);}returnres;},set(target,key,value,receiver){constoldValuetarget[key];// 处理 ref 赋值if(isRef(oldValue)!isRef(value)){oldValue.valuevalue;returntrue;}// 检查属性是否已经存在用于区分新增和修改// hasOwn 检查对象自身是否有该属性不包括原型链consthadKeyhasOwn(target,key);constresultReflect.set(target,key,value,receiver);// 只有对象变化时才触发更新// 确保触发更新的是原始对象而不是其他代理对象,toRaw 获取代理对象的原始对象, 这个检查避免重复触发更新if(targettoRaw(receiver)){if(!hadKey){trigger(target,key,TriggerOpTypes.ADD);}elseif(hasChanged(value,oldValue)){trigger(target,key,TriggerOpTypes.SET);}}returnresult;}};5.2 自定义响应式工具库// 响应式状态管理器classReactiveStore{// 构造函数constructor(data{}){// 使用 reactive 函数将传入的数据转换为响应式数据this.datareactive(data);// 初始化一个 Map 来存储监听器key: 属性名value: 该属性的监听函数集合Mapthis.listenersnewMap();}// 订阅特定字段subscribe(key,callback){// 获取该属性已有的监听器集合如果没有则创建新的 Setconstlistenersthis.listeners.get(key)||newSet();// 将新的回调函数添加到监听器集合中listeners.add(callback);// 将更新后的监听器集合保存回 Mapthis.listeners.set(key,listeners);// 返回一个取消订阅的函数闭包return(){listeners.delete(callback);if(listeners.size0){this.listeners.delete(key);}};}// 设置值并通知set(key,value){constoldValuethis.data[key];this.data[key]value;if(oldValue!value){this.notify(key,value,oldValue);//通知更新}}// 通知更新notify(key,newValue,oldValue){// 获取该属性的所有监听器constlistenersthis.listeners.get(key);if(listeners){// 遍历所有监听器并执行回调函数listeners.forEach(callback{callback(newValue,oldValue);});}}}// 使用示例conststorenewReactiveStore({user:null,cart:[],settings:{theme:light}});// 订阅用户变化constunsubscribestore.subscribe(user,(newUser,oldUser){console.log(用户变化:,oldUser?.name,-,newUser?.name);});// 更新用户store.set(user,{id:1,name:张三});六、高频面试题解析问题1Object.defineProperty 和 Proxy 的主要区别参考答案· 监测能力Proxy 可以监测到对象的所有操作包括新增、删除属性数组索引修改等· 性能方面Proxy 是浏览器原生支持性能更好特别是在大型对象上· 内存占用Proxy 是按需代理内存占用更优defineProperty是一次劫持全部属性。· API 设计Proxy 提供 13 种拦截操作更加强大和灵活常见如get、set、has、deleteProperty、 setProperty、constructor、apply等。Vue2发布时主要浏览器在IE9Proxy的支持率大概才60%左右考虑兼容性问题。Vue2 的成功证明了其技术选型的正确性——在正确的时间选择了正确的技术满足了当时开发者的真实需求。加分回答可以提到 Vue2 为什么不用 Proxy主要是兼容性问题不支持 IE11。问题2Vue3 的响应式相比 Vue2 有哪些性能优化参考答案· 初始化性能Proxy 按需代理避免了一开始就递归遍历所有属性· 内存占用使用 WeakMap 存储依赖关系垃圾回收更友好· 更新精度可以精确知道哪个属性发生了变化减少不必要的更新· 开发体验不再需要 Vue.set/delete 等特殊 API问题3如何手动实现一个简单的响应式系统思路响应式编程的核心思想数据变化自动触发相关更新第一步创建响应式包装器创建一个Map存储依赖关系使用Proxy包装原始对象拦截get和set操作第二步实现依赖收集get拦截当读取属性时检查是否有当前激活的副作用函数若有将这个函数添加到该属性的依赖集合中若没有则先创建最后返回属性值第三步实现触发更新set拦截设置新的属性值检查该属性是否有依赖集合如果有遍历依赖集合中的所有副作用函数逐个执行这些函数。第四步管理副作用函数创建一个副作用函数包装器在执行副作用函数前将其设置为当前激活的副作用函数执行函数执行期间会触发get自动收集依赖执行完毕后清空当前激活的副作用函数。functioncreateReactive(obj){// 创建一个 Map 对象来存储依赖关系constdependenciesnewMap();returnnewProxy(obj,{// 拦截对象的读取操作get(target,key){// 检查是否有当前正在运行的副作用函数通常在副作用函数执行时将其赋值给 activeEffect// 副作用函数当数据变化时需要执行的函数// 收集依赖if(activeEffect){if(!dependencies.has(key)){dependencies.set(key,newSet());}dependencies.get(key).add(activeEffect);}returnReflect.get(target,key);},// 拦截对象的赋值操作set(target,key,value){constresultReflect.set(target,key,value);// 触发依赖if(dependencies.has(key)){dependencies.get(key).forEach(effecteffect());}returnresult;}});}// 包装副作用函数functioneffect(fn){activeEffectfn;fn();activeEffectnull;}七、总结Vue 响应式的核心理念数据驱动视图数据变化自动更新 UI依赖追踪自动收集依赖精确更新性能平衡在功能和性能间找到最佳平衡点下期预告下一篇我们将深入探讨Vue 状态管理包括 Pinia 的核心原理、Vuex 5 的新特性、状态管理的最佳实践以及在大型项目中如何设计状态管理架构。状态管理是复杂应用的核心不要错过哟如果觉得有帮助请关注点赞收藏这是对我最大的鼓励 如有问题请评论区留言

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询