【Vue3】computed计算属性和watch监视

本文详细介绍了Vue3中的computed、watch和watchEffect的使用方法及其与Vue2的区别。通过实例展示了如何定义计算属性、监听数据变化以及响应式回调的触发条件。同时,探讨了watchEffect在关注过程而非计算结果时的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

computed函数

watch函数

监视ref所定义的响应式数据 

监视reactive所定义的响应式数据

watchEffect函数


computed函数

  • 与Vue2.x中computed配置功能一致
  • 写法:
<template>
     <h1>一个人的信息</h1>
     姓:<input type="text" v-model="person.firstName">
     名:<input type="text" v-model="person.lastName">
     <br>
     <span>全名:{
  
  {person.fullName}}</span>
</template>

<script>
import {reactive,computed} from 'vue'
export default {
  name: 'HelloWorld',
  
  setup(){
    //数据
    let person = reactive({
      firstName:'张',
      lastName:'三'
    })
     
    //计算属性(简写 没有考虑计算属性被修改的情况)
     person.fullName = computed(()=>{
        return person.firstName +'-'+person.lastName
      })
     //计算属性(完整写法  考虑读和写)
     person.fullName = computed({
       get(){
         return person.firstName +'-'+person.lastName
       },
       set(){
        const nameArr = value.split('-')
        person.firstName = 
<think>我们正在讨论Vue 3计算属性的实现原理。Vue 3使用Proxy替代了Vue 2的Object.defineProperty,并且对响应式系统进行了重构,引入了Reactive Effect(反应式副作用)的概念。计算属性Vue 3中仍然是基于响应式依赖的缓存机制,但实现细节有所不同。 根据引用[5]:Vue 3中的计算属性通过ComputedRefImpl类实现,它接收一个getter函数(或包含getset的对象)并返回一个ref对象计算属性内部会创建一个副作用(effect),该副作用具有调度器(scheduler)用于在依赖变化时重新计算。 下面详细解释Vue 3计算属性的实现原理: ### 1. 核心类:ComputedRefImpl 在Vue 3的源码中,计算属性是通过`ComputedRefImpl`类来实现的。这个类包含以下关键属性: - `_dirty`:标记缓存是否失效(相当于Vue2中的`dirty`) - `_value`:缓存的值 - `_effect`:一个Reactive Effect实例,用于追踪依赖重新计算 - `__v_isRef`:标记这是一个ref对象(因此需要通过`.value`访问) ### 2. 创建计算属性 当我们在组件中定义计算属性时,例如: ```javascript const count = ref(1) const double = computed(() => count.value * 2) ``` Vue 会调用`computed`函数,该函数内部会创建一个`ComputedRefImpl`实例: ```javascript function computed(getterOrOptions) { let getter, setter if (isFunction(getterOrOptions)) { getter = getterOrOptions setter = () => { /* 默认setter,如果尝试设置会发出警告 */ } } else { getter = getterOrOptions.get setter = getterOrOptions.set } return new ComputedRefImpl(getter, setter) } ``` ### 3. ComputedRefImpl 内部实现 ```javascript class ComputedRefImpl { constructor(getter, setter) { this._setter = setter this._dirty = true // 初始为脏,需要计算 this._value = undefined // 创建副作用,传入getter一个调度器函数 this._effect = new ReactiveEffect( () => getter(), () => { // 调度器:当依赖变化时,不会立即重新计算,而是标记为脏 if (!this._dirty) { this._dirty = true // 触发依赖该计算属性的副作用(例如模板渲染) triggerRefValue(this) } } ) this._effect.computed = this // 标记这是一个计算属性的effect } get value() { // 如果脏,则重新计算 if (this._dirty) { this._dirty = false // 执行计算,同时会收集依赖 this._value = this._effect.run() } // 收集当前正在运行的副作用(例如渲染函数)作为此计算属性的依赖 trackRefValue(this) return this._value } set value(newValue) { this._setter(newValue) } } ``` ### 4. 依赖收集与触发更新 - **依赖收集**:当计算属性的`get value`被访问时(例如在模板中使用`double.value`),会执行`trackRefValue(this)`,将当前正在运行的副作用(如组件的渲染副作用)收集到计算属性的依赖集合中。 - **触发更新**:当计算属性依赖的响应式数据变化时,会触发计算属性的调度器(scheduler),该调度器将`_dirty`标记为`true`,并触发计算属性的依赖(即那些依赖于该计算属性的副作用,如组件的渲染函数)重新执行。当这些副作用重新执行时,会再次访问计算属性的`value`,此时因为`_dirty`为`true`,所以会重新计算。 ### 5. 缓存机制 计算属性的缓存机制体现在: - 首次访问时,计算并缓存结果(`_value`),并将`_dirty`设为`false`。 - 后续访问时,如果`_dirty`为`false`,直接返回缓存值。 - 当依赖变化时,通过调度器将`_dirty`设为`true`(但不会立即重新计算),等到下次访问时才会重新计算。 ### 6.Vue 2的区别 1. **底层响应式系统**:Vue 3使用Proxy实现响应式,而Vue 2使用Object.defineProperty。 2. **依赖追踪**:Vue 3使用Reactive Effect来管理依赖,而Vue 2使用WatcherDep。 3. **计算属性的表现**:Vue 3计算属性是一个ref对象,需要通过`.value`访问(在模板中自动解包,但在JavaScript中需要显式使用`.value`),而Vue 2的计算属性直接挂载到组件实例上。 ### 7. 示例流程 ```mermaid graph TD A[访问计算属性.value] --> B{_dirty?} B -->|是| C[执行计算函数并缓存结果] C --> D[标记_dirty=false] D --> E[收集当前副作用] E --> F[返回缓存值] B -->|否| F G[依赖的响应式数据变化] --> H[触发计算属性的调度器] H --> I[标记_dirty=true] I --> J[触发计算属性的依赖更新] ``` ### 8. 为什么需要调度器? 调度器的作用是延迟计算:当依赖变化时,不立即重新计算,而是标记为脏。这样,如果连续多次修改依赖,计算属性只会重新计算一次(在下次访问时),避免不必要的计算开销[^5]。 ### 9. 计算属性与副作用 计算属性本身也是一个ref,因此它可以被其他副作用(如组件的渲染函数、watchEffect等)依赖。当计算属性的值变化时,会触发这些副作用的重新执行。 ### 10. 总结 Vue 3计算属性实现原理可概括为: - 通过`ComputedRefImpl`类实现 - 使用`_dirty`标志控制缓存 - 依赖变化时通过调度器标记缓存失效并触发依赖更新 - 访问时根据`_dirty`决定是否重新计算 这种设计确保了计算属性的高效性响应性[^3][^5]。 §§相关问题§§ 1. Vue 3计算属性ref有什么区别? 2.Vue 3中,如何实现计算属性的setter? 3. Vue 3的响应式系统与Vue 2相比有哪些改进? 4. 为什么Vue 3计算属性JavaScript中需要通过.value访问? 5. Vue 3的调度器(scheduler)在响应式系统中还扮演什么角色?</think>### Vue 3 计算属性Computed)实现原理详解 Vue 3计算属性基于**响应式系统重构**,使用 `Proxy` 替代 Vue 2 的 `Object.defineProperty`,核心通过 `ComputedRefImpl` 类实现缓存机制。以下是关键实现原理: --- #### 1. 核心类:`ComputedRefImpl` ```typescript class ComputedRefImpl<T> { private _value!: T private _dirty = true // 缓存失效标志 public readonly effect: ReactiveEffect<T> constructor( getter: ComputedGetter<T>, private readonly _setter: ComputedSetter<T> ) { // 创建副作用,包含自定义调度器 this.effect = new ReactiveEffect(getter, () => { if (!this._dirty) { this._dirty = true // 依赖变更时标记缓存失效 triggerRefValue(this) // 触发依赖更新 } }) } get value() { if (this._dirty) { this._value = this.effect.run() // 重新计算 this._dirty = false // 更新缓存状态 } trackRefValue(this) // 收集当前依赖 return this._value } set value(newValue: T) { this._setter(newValue) // 执行setter函数 } } ``` --- #### 2. 核心工作流程 1. **初始化阶段** 当调用 `computed()` 时,创建 `ComputedRefImpl` 实例: ```javascript function computed(getterOrOptions) { const getter = isFunction(getterOrOptions) ? getterOrOptions : getterOrOptions.get return new ComputedRefImpl(getter, setter) } ``` 2. **依赖收集** - 首次访问计算属性时,执行 `get value()` 触发 `effect.run()` - 计算函数执行期间,访问的响应式数据会通过 `trackRefValue()` 建立依赖关系[^3] 3. **缓存更新机制** - 依赖数据变更时,触发副作用调度器(scheduler) - 调度器将 `_dirty` 设为 `true`(标记缓存失效),但不立即重新计算 ```javascript () => { if (!this._dirty) { this._dirty = true // 仅标记,不计算 triggerRefValue(this) // 通知依赖方 } } ``` 4. **惰性计算** 再次访问计算属性时,检测到 `_dirty=true` 才重新执行计算函数: ```javascript get value() { if (this._dirty) { // 检查缓存状态 this._value = this.effect.run() // 重新计算 this._dirty = false // 更新缓存 } return this._value } ``` --- #### 3. 关键技术图解 ```mermaid graph TD A[访问计算属性.value] --> B{_dirty=true?} B -->|是| C[执行effect.run重新计算] C --> D[标记_dirty=false] D --> E[返回新值] B -->|否| F[直接返回缓存值] G[依赖数据变更] --> H[触发调度器] H --> I[标记_dirty=true] I --> J[通知依赖更新] ``` --- #### 4.Vue 2 的核心区别 | 特性 | Vue 2 | Vue 3 | |--------------------|--------------------------------|---------------------------------| | **响应式基础** | `Object.defineProperty` | `Proxy` | | **实现核心** | `Watcher` 类 | `ComputedRefImpl` 类 | | **依赖追踪** | 通过 `Dep` `Watcher` | 通过 `ReactiveEffect` 副作用 | | **缓存失效处理** | Watcher 直接标记 dirty | 调度器(scheduler)延迟标记 | | **数据类型** | 直接挂载组件实例 | Ref 对象(需通过 `.value` 访问)| > 示例:当计算属性依赖变化时,Vue 3 仅通过调度器标记 `_dirty=true`,直到下次访问时才实际重新计算,避免不必要的计算开销[^5]。 --- #### 5. 优势特性 1. **惰性计算** 仅在访问时计算,避免不必要的执行 2. **智能缓存** 依赖未变化时直接返回缓存值 3. **精准更新** 通过调度器控制更新时机,优化性能 4. **组合式集成** 作为 Ref 对象,可无缝用于 Composition API > 关键设计:通过**调度器(scheduler)** 将依赖变更与重新计算解耦,实现更精细的更新控制[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

翘阳啦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值