computed()、watch() 與 watchEffect()

下面,我們來系統的梳理關于 computed、watch 與 watchEffect 的基本知識點:


一、核心概念與響應式基礎

1.1 響應式依賴關系

Vue 的響應式系統基于 依賴收集觸發更新 的機制:

響應式數據
依賴收集
創建依賴關系
數據變更
觸發更新
執行副作用

1.2 三大 API 對比

特性computedwatchwatchEffect
返回值Ref 對象停止函數停止函數
依賴收集自動手動指定自動
執行時機惰性求值響應變化立即執行
主要用途派生狀態響應變化執行操作自動追蹤副作用
新舊值提供新舊值
異步支持同步支持異步支持異步
首次執行訪問時執行可配置總是執行

二、computed 深度解析

2.1 基本使用與類型

import { ref, computed } from 'vue'// 只讀計算屬性
const count = ref(0)
const double = computed(() => count.value * 2)// 可寫計算屬性
const fullName = computed({get: () => `${firstName.value} ${lastName.value}`,set: (newValue) => {[firstName.value, lastName.value] = newValue.split(' ')}
})

2.2 實現原理

用戶代碼 computed() ReactiveEffect Dep 數據變更 用戶再次訪問 創建計算屬性 創建 ReactiveEffect 收集依賴 訪問 .value 執行計算函數 追蹤依賴 返回計算結果 觸發更新 標記為臟值 重新計算 用戶代碼 computed() ReactiveEffect Dep 數據變更 用戶再次訪問

2.3 核心特性

  1. 惰性求值:僅在訪問 .value 時計算
  2. 結果緩存:依賴未變化時返回緩存值
  3. 依賴追蹤:自動收集響應式依賴
  4. 類型安全:完美支持 TypeScript 類型推斷

2.4 最佳實踐

// 避免在計算屬性中產生副作用
const badExample = computed(() => {console.log('This is a side effect!') // 避免return count.value * 2
})// 復雜計算使用計算屬性
const totalPrice = computed(() => {return cartItems.value.reduce((total, item) => {return total + (item.price * item.quantity)}, 0)
})// 組合多個計算屬性
const discountedTotal = computed(() => {return totalPrice.value * (1 - discountRate.value)
})

三、watch 深度解析

3.1 基本使用與語法

import { watch, ref } from 'vue'// 偵聽單個源
const count = ref(0)
watch(count, (newValue, oldValue) => {console.log(`Count changed: ${oldValue}${newValue}`)
})// 偵聽多個源
const firstName = ref('John')
const lastName = ref('Doe')
watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {console.log(`Name changed: ${oldFirst} ${oldLast}${newFirst} ${newLast}`)
})// 深度偵聽對象
const state = reactive({ user: { name: 'Alice' } })
watch(() => state.user,(newUser, oldUser) => {console.log('User changed', newUser, oldUser)},{ deep: true }
)

3.2 配置選項詳解

watch(source, callback, {// 立即執行回調immediate: true,// 深度偵聽deep: true,// 回調執行時機flush: 'post', // 'pre' | 'post' | 'sync'// 調試鉤子onTrack: (event) => debugger,onTrigger: (event) => debugger
})

3.3 高級用法

// 異步操作與取消
const data = ref(null)
watch(id, async (newId, oldId, onCleanup) => {const controller = new AbortController()onCleanup(() => controller.abort())try {const response = await fetch(`/api/data/${newId}`, {signal: controller.signal})data.value = await response.json()} catch (error) {if (error.name !== 'AbortError') {console.error('Fetch error:', error)}}
})// 限制執行頻率
import { throttle } from 'lodash-es'
watch(searchQuery,throttle((newQuery) => {search(newQuery)}, 500)
)

3.4 性能優化

// 避免深度偵聽大型對象
watch(() => state.items.length, // 僅偵聽長度變化(newLength) => {console.log('Item count changed:', newLength)}
)// 使用淺層偵聽
watch(() => ({ ...shallowObject }), // 創建淺拷貝(newObj) => {console.log('Shallow object changed')}
)

四、watchEffect 深度解析

4.1 基本使用

import { watchEffect, ref } from 'vue'const count = ref(0)// 自動追蹤依賴
const stop = watchEffect((onCleanup) => {console.log(`Count: ${count.value}`)// 清理副作用onCleanup(() => {console.log('Cleanup previous effect')})
})// 停止偵聽
stop()

4.2 核心特性

  1. 自動依賴收集:無需指定偵聽源
  2. 立即執行:創建時立即運行一次
  3. 清理機制:提供 onCleanup 回調
  4. 異步支持:天然支持異步操作

4.3 高級用法

// DOM 操作副作用
const elementRef = ref(null)
watchEffect(() => {if (elementRef.value) {// 操作 DOMelementRef.value.focus()}
})// 響應式日志
watchEffect(() => {console.log('State updated:', {count: count.value,user: user.value.name})
})// 組合多個副作用
watchEffect(async () => {const data = await fetchData(params.value)processData(data)
})

4.4 性能優化

// 使用 flush 控制執行時機
watchEffect(() => {// 在 DOM 更新后執行updateChart()},{ flush: 'post' }
)// 調試依賴
watchEffect(() => {// 副作用代碼},{onTrack(e) {debugger // 依賴被追蹤時},onTrigger(e) {debugger // 依賴變更觸發回調時}}
)

五、三者的區別與選擇指南

5.1 使用場景對比

場景推薦 API理由
派生狀態computed自動緩存,高效計算
數據變化響應watch精確控制,獲取新舊值
副作用管理watchEffect自動依賴收集
異步操作watch/watchEffect支持異步和取消
DOM 操作watchEffect自動追蹤 DOM 依賴
調試依賴watch精確控制偵聽源

5.2 性能考慮

  1. computed:適合同步計算,避免復雜操作
  2. watch:適合需要精確控制的場景
  3. watchEffect:適合自動依賴收集的副作用
// 計算屬性 vs 偵聽器
// 推薦:使用計算屬性派生狀態
const fullName = computed(() => `${firstName.value} ${lastName.value}`)// 不推薦:使用偵聽器模擬計算屬性
const fullName = ref('')
watch([firstName, lastName], () => {fullName.value = `${firstName.value} ${lastName.value}`
})

5.3 組合使用模式

// 組合 computed 和 watch
const discountedTotal = computed(() => total.value * (1 - discount.value))watch(discountedTotal, (newTotal) => {updateUI(newTotal)
})// 組合 watchEffect 和 computed
const searchResults = ref([])
const searchQuery = ref('')const validQuery = computed(() => searchQuery.value.trim().length > 2
)watchEffect(async (onCleanup) => {if (!validQuery.value) returnconst controller = new AbortController()onCleanup(() => controller.abort())searchResults.value = await fetchSearchResults(searchQuery.value, controller.signal)
})

六、原理深入剖析

6.1 Vue 響應式系統核心

訂閱
1
*
使用
Dep
+depend()
+notify()
ReactiveEffect
+run()
+stop()
ComputedRefImpl
-_value
-_dirty
+get value()
+set value()

6.2 computed 實現原理

class ComputedRefImpl {constructor(getter, setter) {this._getter = getterthis._setter = setterthis._value = undefinedthis._dirty = truethis.effect = new ReactiveEffect(getter, () => {if (!this._dirty) {this._dirty = truetrigger(this, 'value')}})}get value() {if (this._dirty) {this._value = this.effect.run()this._dirty = falsetrack(this, 'value')}return this._value}set value(newValue) {this._setter(newValue)}
}

6.3 watch 和 watchEffect 的異同

實現機制watchwatchEffect
依賴收集基于指定源自動收集執行中的依賴
內部實現基于 watchEffect基礎 API
調度機制支持 flush 配置支持 flush 配置
清理機制通過回調參數通過 onCleanup

七、最佳實踐與性能優化

7.1 性能優化策略

  1. 避免不必要的重新計算

    // 使用 computed 緩存結果
    const filteredList = computed(() => largeList.value.filter(item => item.active)
    )
    
  2. 合理使用偵聽選項

    // 減少深度偵聽范圍
    watch(() => state.user.id, // 僅偵聽 ID 變化(newId) => fetchUser(newId)
    )
    
  3. 批量更新處理

    watch([data1, data2],() => {// 合并處理多個變化updateVisualization()},{ flush: 'post' }
    )
    

7.2 常見模式

數據獲取模式

const data = ref(null)
const error = ref(null)watchEffect(async (onCleanup) => {data.value = nullerror.value = nullconst controller = new AbortController()onCleanup(() => controller.abort())try {const response = await fetch(url.value, {signal: controller.signal})data.value = await response.json()} catch (err) {if (err.name !== 'AbortError') {error.value = err.message}}
})

表單驗證模式

const formState = reactive({ email: '', password: '' })
const errors = reactive({ email: '', password: '' })watch(() => [formState.email, formState.password],() => {errors.email = formState.email.includes('@') ? '' : 'Invalid email'errors.password = formState.password.length >= 6 ? '' : 'Too short'},{ immediate: true }
)

八、常見問題與解決方案

8.1 響應式依賴問題

問題: watchEffect 未正確追蹤依賴

const state = reactive({ count: 0 })watchEffect(() => {// 當 state.count 變化時不會觸發console.log(state.nested?.value) // 可選鏈導致依賴丟失
})

解決方案:

watchEffect(() => {// 顯式訪問確保依賴追蹤if (state.nested) {console.log(state.nested.value)}
})

8.2 異步競態問題

問題: 多個異步請求可能導致舊數據覆蓋新數據

watch(id, async (newId) => {const data = await fetchData(newId)currentData.value = data // 可能舊請求覆蓋新
})

解決方案:

watch(id, async (newId, _, onCleanup) => {let isCancelled = falseonCleanup(() => isCancelled = true)const data = await fetchData(newId)if (!isCancelled) {currentData.value = data}
})

8.3 無限循環問題

問題: 偵聽器中修改依賴數據導致循環

watch(count, (newVal) => {count.value = newVal + 1 // 無限循環
})

解決方案:

// 添加條件判斷
watch(count, (newVal, oldVal) => {if (newVal < 100) {count.value = newVal + 1}
})// 使用 watchEffect 替代
watchEffect(() => {if (count.value < 100) {count.value += 1}
})

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/86336.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/86336.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/86336.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Linux驅動開發 ---- 4.2_平臺設備(Platform Devices)概述】

Linux驅動開發 ---- 4.2_平臺設備&#xff08;Platform Devices&#xff09;概述 目錄 Linux驅動開發 ---- 4.2_平臺設備&#xff08;Platform Devices&#xff09;概述前述主要特點&#xff1a;平臺設備的作用平臺設備的注冊與注銷1. platform_device_register_simple()2. pla…

深入學習入門--(一)前備知識

一.Python基礎知識 1.1 Python算數運算 1.2 變量 1.3 數據類型 1.3.1 int&#xff08;整數&#xff09; float&#xff08;浮點數&#xff09; str&#xff08;字符串&#xff09; 1.3.2 bool&#xff08;布爾值&#xff09;: 表示真或假 取值:True,False 1.3.3 list&…

iClone 中創建的面部動畫導入 Daz 3D

以下是如何將 iClone 中創建的面部動畫導入 Daz 3D 的簡要指南。簡而言之&#xff0c;您可以通過 FBX&#xff08;使用 3DXchange 或 Character Creator 的導出工具&#xff09;導出 iClone 面部動畫&#xff0c;然后將其導入 Daz Studio 并將變形或骨骼重新映射到 Genesis 角色…

OceanBase向量檢索在貨拉拉的探索和實踐

貨拉拉成立于2013年&#xff0c;成長于粵港澳大灣區&#xff0c;是從事同城跨城貨運、企業版物流服務、搬家、零擔、跑腿、冷運、汽車租售及車后市場服務的互聯網物流商城。截至2024年&#xff0c;貨拉拉在全球擁有1670萬月活用戶和168萬月活司機&#xff0c;業務覆蓋全球11個市…

Flask(五) 表單處理 request.form、WTForms

文章目錄 1. 基本表單處理&#xff0c;使用 request.form&#xff08;輕量&#xff09;示例一創建 HTML 表單處理表單數據 示例二HTML 表單&#xff08;login.html&#xff09;Flask 路由處理表單 2. 使用 Flask-WTF 擴展安裝設置 Secret Key&#xff08;CSRF 防護&#xff09;…

c++虛繼承復習

深入理解C虛繼承&#xff1a;解決菱形繼承問題的利器 在C面向對象編程中&#xff0c;多重繼承是一個強大但容易誤用的特性。今天我們來探討一個特殊的多重繼承形式——虛繼承&#xff08;Virtual Inheritance&#xff09;&#xff0c;它是解決著名的"菱形繼承問題"的…

魔樂社區國產算力應用創新大賽重磅開啟!

當國產算力崛起成為 AI 發展新引擎&#xff0c;你是否渴望用創新方案解鎖無限可能&#xff1f;魔樂社區國產算力應用創新大賽重磅來襲&#xff01;聚焦國產算力前沿&#xff0c;無論你是開發者、研究者&#xff0c;還是技術愛好者&#xff0c;都能在這里一展身手。 現在報名參…

WebView 性能調試與優化全流程:加載速度與渲染性能雙提升

移動端 WebView 頁面通常用于承載復雜的前端應用&#xff0c;尤其是動態加載大量數據或進行高頻率交互時&#xff0c;性能問題尤為突出。用戶常常會遇到頁面加載緩慢、滾動卡頓、甚至是部分內容顯示不完全的情況。在這種情況下&#xff0c;如何優化數據加載與渲染過程&#xff…

51c嵌入式~CAN~合集2

我自己的原文哦~ https://blog.51cto.com/whaosoft/14016935 一、CAN總線常見信號干擾問題 定位干擾原因 當總線有干擾時&#xff0c;有經驗的工程師能夠迅速定位&#xff0c;但是對于新手來說卻很麻煩。 造成總線干擾的原因有很多&#xff0c;比如通過電磁輻射耦合到通…

【cursor實戰】分析python下并行、串行計算性能

提示語 寫一個Python并行計算、串行計算性能對比的代碼。并行計算要包括多線程和多進程兩種,計算的內容要比較復雜 模型 claude-4-sonnet 生成的代碼 #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Python并行計算與串行計算性能對比程序 包含串行…

ubuntu中53端口被占用導致dnsmasq無法使用。已解決。

方案一&#xff1a;修改參數&#xff0c;但不影響使用 編輯配置文件 vim /etc/systemd/resolved.conf將此參數修改為&#xff1a; DNSStubListenerno重啟服務 sudo systemctl daemon-reload sudo systemctl disable systemd-resolved.service方案一&#xff1a;直接禁用 編…

【多模態大模型】訓練與推理直觀解讀

1.直觀案例解讀-圖文問答 假設我們的輸入是一張包含小貓的圖片&#xff0c;以及一個文本提問&#xff1a;“其中是否有小貓&#xff1f;”。下面我將以最詳盡的方式&#xff0c;描述數據在nanoVLM模型中從輸入到輸出的完整流動過程&#xff0c;并解釋每一步中數據的形狀和含義…

uni-app項目實戰筆記17--獲取系統信息getSystemInfo狀態欄和膠囊按鈕

接著上一篇筆記&#xff0c;在添加頭部導航欄后&#xff0c;H5顯示正常&#xff1a; 但在微信小程序中&#xff0c;由于劉海屏的存在&#xff0c;添加的頭部導航欄跟狀態欄重疊在一起&#xff1a; 因此需要獲取狀態欄的高度以便狀態欄和導航欄錯開不重疊在一起。同時頭部導航欄…

Windows下Zookeeper客戶端啟動緩慢問題分析與解決方案

文章目錄 1. 問題描述2. 問題分析2.1 性能分析2.2 根本原因 3. 解決方案3.1 臨時解決方案3.2 長期解決方案 4. 注意事項5. 結論 1. 問題描述 在Windows 8.1 64-bit操作系統環境下&#xff0c;使用Curator框架連接Zookeeper時出現客戶端啟動異常緩慢的問題。具體表現為&#xf…

在 Java 中生成 PDF 縮略圖(教程)

Java 本身無法自動生成 PDF 頁面縮略圖&#xff0c;但幸運的是&#xff0c;有許多軟件庫可以實現這一功能。本文示例使用我們自家的 JPedal 庫&#xff0c;僅需幾行 Java 代碼即可創建縮略圖。JPedal 是開發者使用的最佳 Java PDF 庫。 如何使用 JPedal 將 PDF 轉換為縮略圖 …

基于大模型的甲狀腺結節預測及綜合診療技術方案大綱

目錄 一、技術方案概述二、術前預測與方案制定2.1 結節特征分析與良惡性預測2.2 手術方案建議2.3 麻醉方案優化三、術中輔助決策3.1 實時數據監測與分析3.2 麻醉深度監控與調節四、術后護理與并發癥預測4.1 術后恢復預測4.2 并發癥風險預警五、統計分析與技術驗證5.1 數據分割與…

SpringCloud系列(36)--SpringCloud Gateway簡介

1、SpringCloud GateWay概述 SpringCloud Gateway是 Spring Cloud的一個全新項目&#xff0c;基于Spring 5.0Spring Boot 2.0和Project Reactor等技術開發的網關&#xff0c;它旨在為微服務架構提供一種簡單有效的統—的API路由管理方式&#xff1b;SpringCloud Gateway作為Sp…

TensorFlow深度學習實戰:構建神經網絡全指南

引言&#xff1a;深度學習與TensorFlow概覽 深度學習作為機器學習的一個重要分支&#xff0c;近年來在計算機視覺、自然語言處理、語音識別等領域取得了突破性進展。TensorFlow是由Google Brain團隊開發的開源深度學習框架&#xff0c;自2015年發布以來&#xff0c;已成為最受…

K8S: etcdserver: too many requests

Kubernetes etcdserver: too many requests 錯誤解決方案 當Kubernetes集群出現 etcdserver: too many requests 錯誤時&#xff0c;表明etcd數據庫接收到的請求量超過了其處理能力。etcd作為Kubernetes的核心組件&#xff0c;存儲著集群的所有狀態數據&#xff0c;處理請求過…

銀河麒麟高級服務器操作系統(全架構)OpenGauss 數據庫部署手冊

一、部署前準備工作 1. 環境檢查 項目配置描述內存功能調試建議 32GB 以上。性能測試和商業部署時&#xff0c;單實例部署建議 128GB 以上。復雜的查詢對內存的需求量比較高&#xff0c;在高并發場景下&#xff0c;可能出現內存不足。此時建議使用大內存的機器&#xff0c;或…