响应式原理
响应式原理主要通过以下三个阶段来实现:依赖追踪、依赖收集和触发更新
- 依赖追踪:Vue.js 在初始化时,会遍历 data 对象中的每个属性,为每个属性创建一个“依赖”(也可以叫“观察者”),并将这个依赖与属性建立关联关系。这个关联关系可以理解为属性“依赖于”这个依赖。这是通过 Object.defineProperty() 方法实现的,该方法允许精确添加或修改对象的属性,并可以在这些属性的 getter 和 setter 方法上添加自定义逻辑。
- 依赖收集:在组件渲染的过程中,模板中的表达式会执行对应的 getter 函数。getter 函数会把表达式中用到的属性“收集”起来,与当前的渲染上下文建立关联关系。这个关联关系可以理解为当前的渲染上下文“依赖于”这些属性。在依赖收集阶段,每个依赖都会被记录在当前的渲染上下文中,这样当数据发生变化时,就可以根据当前的渲染上下文,找到所有依赖,并依次触发它们的更新操作。每个组件实例都会对应一个 watcher 实例,它会在组件渲染的过程中把使用过的数据属性通过 getter 收集为依赖。
- 触发更新:当数据发生变化时,即属性的 setter 被调用时,会通知对应的 Watcher 实例,从而使它关联的组件重新渲染。这是通过触发依赖的 setter 方法实现的,当 setter 被调用时,会通知所有依赖这个属性的 Watcher 实例进行更新操作
总的来说,Vue.js 的响应式原理是通过 Object.defineProperty() 方法劫持各个属性的 setter 和 getter,在数据变动时发布消息给订阅者,触发相应的监听回调,从而实现数据驱动的视图更新。
Vue 中的 key 到底有什么用
- 提高 DOM 更新性能:Vue 使用
key来标识每个节点,从而可以更精确地找到需要更新的节点。当 Vue 更新 DOM 时,它会基于新数据生成一棵新的虚拟 DOM 树,并与旧的虚拟 DOM 树进行对比。key的存在帮助 Vue 更准确地找到需要更新的节点,而不是对整个 DOM 树进行重新渲染,从而提高了更新性能。 - 管理可复用的元素:Vue 会尽可能地复用已有的元素,而不是从头开始渲染。当使用
v-for指令渲染列表时,key的存在可以确保 Vue 准确地找到对应的节点,并根据key的变化重新排列元素顺序,或者移除不存在的元素。 - 强制重新渲染:在某些特殊场景下,可以使用
key来强制重新渲染某个组件或元素。例如,当某个组件的数据依赖于外部环境的变化时,可以通过改变key的值来强制该组件重新渲染,从而确保数据的正确性。
需要注意的是,key 的值必须是唯一的,以确保 Vue 能够准确地识别每个节点。在实际使用中,通常使用数据对象的唯一标识符(如 id)作为 key 的值。同时,当使用 v-for 渲染动态数据时,为了避免 DOM 元素复用错误或渲染异常,不应直接修改数据源,而应使用变异方法或重新赋值来更新数据。
computed 的实现原理
computed 属性的实现原理主要涉及以下几个方面:
-
依赖收集:
当computed属性被访问时(例如,在模板中或通过 Vue 实例直接访问),它会触发其内部依赖的收集过程。Vue 会遍历computed属性的 getter 函数,执行过程中遇到的所有响应式依赖(即其他数据属性)都会被收集,并与当前的computed属性建立关联。 -
计算缓存:
computed属性的值会被缓存起来。这意味着只要依赖属性没有发生改变,多次访问同一个computed属性会立即返回之前缓存的计算结果,而不会重新执行 getter 函数。这种缓存机制可以显著提高性能,特别是当computed属性的计算过程非常耗时或依赖于大量数据时。 -
依赖变化通知:
当任何一个computed属性所依赖的数据属性发生变化时,Vue 的响应式系统会通知这些computed属性,告诉它们需要重新计算。这是通过依赖收集阶段建立的关联关系来实现的。一旦收到通知,computed属性会重新执行其 getter 函数,并更新缓存的值。 -
观察者模式:
computed属性的实现基于 Vue 的观察者模式。每个响应式数据属性都有一个观察者,当数据属性变化时,观察者会通知所有依赖这个属性的computed属性。这种观察者模式使得 Vue 能够以非常灵活和高效的方式处理数据的依赖关系。 -
计算属性与方法的区别:
尽管computed属性和方法都可以用来处理复杂逻辑和计算,但它们之间有一个重要的区别。方法每次被调用时都会重新计算,而computed属性则会缓存计算结果,只有当依赖的数据发生改变时才会重新计算。这使得computed属性在计算复杂逻辑时更加高效。
computed 和 watch 有什么区别及运用场景
区别:
- 用途和功能性:
computed是计算属性,主要用于处理复杂逻辑和基于其他数据属性的计算。它返回一个缓存的结果,只有当依赖的数据发生变化时才会重新计算。watch用于监听单个数据属性的变化,并在数据变化时执行特定的回调函数。它主要用于异步操作或开销较大的操作。
- 缓存支持:
computed属性支持缓存,只有当其依赖的数据发生变化时,才会重新计算。如果依赖没有变化,它会从缓存中读取之前的结果。watch则不支持缓存,每次监听的数据变化时都会执行回调。
- 异步操作和开销:
computed属性不支持异步操作,因为计算属性需要同步返回结果。watch更适合执行异步操作或开销较大的操作,因为你可以在数据变化时触发这些操作。
- 返回值要求:
computed属性的函数必须使用return语句返回计算结果。watch的回调函数则没有这样的要求。
运用场景:
- 使用
computed的场景:- 当一个属性受多个属性影响时,例如,需要根据用户输入的长度和宽度来计算面积。
- 当需要在数据变化时执行复杂计算,并且这些计算的结果需要被缓存以提高性能时。
- 使用
watch的场景:- 当需要监听某个数据属性的变化,并在变化时执行异步操作,例如,数据变化时发送 AJAX 请求。
- 当一个数据属性的变化会影响多个其他数据属性时,例如,监听搜索框的输入,并根据输入内容过滤数据列表。
为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?
-
更全面的拦截能力:Proxy API 支持拦截目标的各种操作,包括读取、设置、删除、枚举等,甚至还可以拦截函数调用和构造函数实例化。相比之下,Object.defineProperty 只能监听属性的 get 和 set 操作,无法覆盖其他重要的操作。
-
更好的数组变化检测:Vue 3.0 使用 Proxy API 改善了数组的变化检测机制。Proxy 可以直接拦截数组的索引访问和修改,使得对数组的变化更容易被监听到,从而提供了更可靠的响应式行为。相比之下,Vue 2.x 中使用 Object.defineProperty 无法检测到数组下标的变化,需要额外的逻辑来处理这个问题。
-
更易于处理嵌套对象:Proxy API 能够递归地拦截对象的嵌套属性,而 Object.defineProperty 无法自动递归处理嵌套对象。这使得在 Vue 3.0 中处理嵌套对象更加简单和方便。
-
更好的错误提示:相比于 Object.defineProperty,Proxy API 提供了更好的错误追踪和调试信息。当使用 Proxy API 时,如果访问或修改了一个不存在的属性,会直接抛出错误,从而更容易发现和修复问题。
-
性能优化:在某些场景下,Object.defineProperty 的实现可能会导致性能问题。而 Proxy 的性能表现通常更好,能够更高效地处理响应式操作。
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://mi-blog.cn/index.php/2020/03/02/vue%e9%9d%a2%e8%af%95%e9%a2%98/