深圳3d网站建设,沈阳网站seo排名优化,网站备案安全责任书是谁盖章,Wordpress安装购物车在前端开发中#xff0c;Vue 的数据响应机制、脚本加载策略以及函数式编程技巧是高频考点和日常开发的核心基础。本文将围绕这几个关键点展开详细解析#xff0c;帮助开发者深入理解其原理与应用。一、Vue2 与 Vue3 的数据响应原理对比Vue 的核心特性之一是数据响应式—— 当…在前端开发中Vue 的数据响应机制、脚本加载策略以及函数式编程技巧是高频考点和日常开发的核心基础。本文将围绕这几个关键点展开详细解析帮助开发者深入理解其原理与应用。一、Vue2 与 Vue3 的数据响应原理对比Vue 的核心特性之一是数据响应式—— 当数据变化时视图自动更新。但 Vue2 和 Vue3 实现这一特性的底层原理存在显著差异。1. Vue2 的数据响应原理Object.definePropertyVue2 通过 **Object.defineProperty劫持对象的 getter 和 setter** 实现响应式。其核心逻辑是
初始化时遍历data中的属性为每个属性设置getter获取值时收集依赖和setter修改值时触发更新。实现核心步骤递归遍历data中的所有属性包括嵌套对象对每个属性调用Object.defineProperty重写get和set方法当属性被访问时get收集当前依赖如组件渲染函数当属性被修改时set通知所有依赖更新触发视图重新渲染。代码示例简化版
function defineReactive(obj, key, value) {// 递归处理嵌套对象observe(value);Object.defineProperty(obj, key, {get() {console.log(获取${key}的值: ${value});// 收集依赖实际中会关联Dep和WatcherDep.target dep.addSub(Dep.target);return value;},set(newVal) {if (newVal ! value) {console.log(更新${key}的值: ${newVal});value newVal;// 通知依赖更新dep.notify();}}});
}// 遍历对象属性批量设置响应式
function observe(obj) {if (typeof obj ! object || obj null) return;Object.keys(obj).forEach(key {defineReactive(obj, key, obj[key]);});
}
局限性无法监听数组索引变化如arr[0] 1和对象新增属性如obj.newKey 1需通过Vue.set或this.$set手动触发响应式本质是为新增属性重新设置getter/setter对数组的监听通过重写push/pop/splice等 7 个方法实现修改数组时触发更新。2. Vue3 的数据响应原理ProxyVue3 改用 **Proxy代理对象 ** 实现响应式解决了 Vue2 的局限性。Proxy可以直接代理整个对象而非单个属性支持监听更多场景。实现核心优势代理整个对象无需递归遍历属性初始化性能更好支持监听新增属性 / 删除属性如obj.newKey 1或delete obj.key原生支持数组索引修改如arr[0] 1可直接被监听支持Map、Set等复杂数据结构的响应式。代码示例简化版
function reactive(obj) {return new Proxy(obj, {get(target, key) {console.log(获取${key}的值: ${target[key]});// 收集依赖类似Vue2的Deptrack(target, key);return target[key];},set(target, key, value) {if (target[key] ! value) {console.log(更新${key}的值: ${value});target[key] value;// 通知更新类似Vue2的Watchertrigger(target, key);}},deleteProperty(target, key) {console.log(删除${key});delete target[key];trigger(target, key); // 删除也触发更新}});
}
总结特性Vue2Object.definePropertyVue3Proxy监听范围仅已定义的属性整个对象含新增 / 删除数组支持需重写方法不支持索引修改原生支持索引修改和方法调用初始化性能递归遍历属性性能较差懒代理性能更优复杂数据结构Map不支持支持二、Vue2 如何监听 data 里的数据变化Vue2 对data的监听是一个递归劫持 依赖收集的过程核心通过Observer、Dep、Watcher三个类协同实现。1. 核心流程初始化data组件初始化时data函数返回的对象会被传入observe函数创建Observer实例observe函数会为对象创建Observer实例负责劫持属性劫持属性Observer通过Object.defineProperty重写对象的getter/setter收集依赖Dep当属性被访问时如渲染时getter会将当前Watcher依赖添加到Dep依赖管理器中触发更新当属性被修改时setter会通知DepDep再通知所有Watcher执行更新如重新渲染组件。2. 数组的特殊处理由于Object.defineProperty无法监听数组索引变化Vue2 通过重写数组原型方法实现监听
// 重写数组的7个变更方法
const arrayMethods Object.create(Array.prototype);
[push, pop, shift, unshift, splice, sort, reverse].forEach(method {arrayMethods[method] function(...args) {// 执行原数组方法const result Array.prototype[method].apply(this, args);// 通知更新触发Observer的depthis.__ob__.dep.notify();return result;};
});// 为数组设置新原型
function observeArray(arr) {arr.__proto__ arrayMethods; // 覆盖数组原型for (let i 0; i arr.length; i) {observe(arr[i]); // 递归监听数组元素}
}
三、watch 与 computed 的区别watch和computed都是 Vue 中监听数据变化的工具但应用场景截然不同。1. 核心差异对比特性computed计算属性watch监听器本质基于依赖的衍生数据类似 “变量”数据变化后的回调函数类似 “事件”缓存机制有缓存依赖不变则不重新计算无缓存数据变化即触发回调返回值必须有返回值用于页面渲染无返回值用于执行副作用如异步操作异步支持不支持不能包含异步逻辑否则缓存失效支持可执行异步操作如接口请求适用场景简单的衍生数据计算如拼接字符串、计算总价复杂的副作用处理如数据变化后请求接口2. 代码示例
// computed示例计算全名有缓存
computed: {fullName() {// 依赖firstName和lastName只有它们变化时才重新计算return ${this.firstName} ${this.lastName};}
}// watch示例监听name变化执行异步操作
watch: {name(newVal, oldVal) {// 支持异步如请求接口this.$axios.get(/user?name${newVal}).then(res {this.userInfo res.data;});}
}
3. 总结当需要根据已有数据生成新数据时用computed利用缓存提升性能当需要在数据变化时执行异步操作或复杂逻辑时用watch。四、script 标签的 defer 和 async 区别script标签的defer和async属性用于控制脚本的加载与执行时机解决默认加载阻塞 HTML 解析的问题。1. 默认行为不添加defer或async时脚本加载会阻塞 HTML 解析浏览器遇到script标签时会暂停 HTML 解析下载脚本并立即执行执行完成后再继续解析 HTML。2. defer 与 async 的差异特性asyncdefer加载时机并行下载脚本不阻塞 HTML 解析并行下载脚本不阻塞 HTML 解析执行时机下载完成后立即执行可能打断 HTML 解析下载完成后等待 HTML 解析完毕再执行执行顺序不保证顺序哪个先下载完就先执行严格按照 HTML 中脚本的顺序执行适用场景独立脚本如统计脚本、广告脚本依赖 DOM 或顺序执行的脚本如 jQuery 插件3. 执行流程图示默认无属性加载阻塞解析 → 执行阻塞解析async加载不阻塞解析 → 执行阻塞解析顺序不确定defer加载不阻塞解析 → 执行在 HTML 解析完成后顺序与标签一致。4. 总结若脚本无需依赖 DOM 且无顺序要求如独立工具库用async若脚本依赖 DOM 或需要按顺序执行如 jQuery 后加载插件用defer。五、函数柯里化及常见应用场景函数柯里化Currying 是将多参数函数转化为一系列单参数函数的技术形如f(a,b,c) → f(a)(b)(c)。1. 核心原理通过闭包收集参数当参数数量满足原函数需求时执行原函数否则返回一个新函数继续收集参数。实现示例
// 将多参数函数柯里化
function curry(fn) {const argsLength fn.length; // 原函数的参数数量return function curried(...args) {// 若收集的参数足够执行原函数if (args.length argsLength) {return fn.apply(this, args);}// 否则返回新函数继续收集参数return function(...nextArgs) {return curried.apply(this, args.concat(nextArgs));};};
}// 测试柯里化add函数
const add (a, b, c) a b c;
const curriedAdd curry(add);curriedAdd(1)(2)(3); // 6
curriedAdd(1, 2)(3); // 6
curriedAdd(1)(2, 3); // 6
2. 常见应用场景参数复用固定部分参数简化函数调用。例计算商品税后价格固定税率
const calculateTax (taxRate, price) price * (1 taxRate);
const withTax curry(calculateTax)(0.1); // 固定税率10%
withTax(100); // 110无需重复传税率
延迟执行分步传递参数直到条件满足再执行。例事件绑定中分步传参
// 柯里化事件处理函数
const handleClick curry((id, event) {console.log(点击了ID为${id}的元素事件:, event);
});// 绑定事件时先传id事件触发时传event
document.getElementById(btn1).onclick handleClick(1);
document.getElementById(btn2).onclick handleClick(2);
函数式编程配合compose、pipe等工具实现函数组合。总结本文解析了前端开发中的 5 个核心知识点Vue2 通过Object.defineProperty劫持属性Vue3 通过Proxy代理对象后者更全面Vue2 对data的监听是递归劫持 依赖收集的过程数组需特殊处理computed适用于衍生数据计算有缓存watch适用于异步副作用无缓存async脚本加载完立即执行乱序defer等待 HTML 解析后执行顺序柯里化通过闭包分步收集参数适用于参数复用、延迟执行等场景。