哈嘍,各位小夥伴,歡迎來到我是wangfang呀的博客!我是我是wangfang呀,雖然還在編程的“菜鳥”階段,但我已經迫不及待地想和大家分享我一路上踩過的坑和學到的小技巧。如果你也曾為bug頭疼,那麼你來對地方了!今天的內容希望能夠給大家帶來一些靈感和幫助。
前言
第一次寫 Vue 時,很多人都會驚歎:“咦,data 裏一改值,頁面就自己刷新了!”。
別被這份“理所當然”騙了——Vue 背後可是暗藏着一整套監聽、依賴收集、批量更新的高能機制。今天這篇,我們從 Vue 2 到 Vue 3,把響應式的來龍去脈、坑點、API 區別一次講透!
1 | Vue 2:Object.defineProperty 下的極限“劫持”
核心思路
- 遞歸遍歷
data對象,對每個屬性調用Object.defineProperty。 - 重寫
get:渲染時收集依賴(把當前 watcher 加進“訂閲列表”)。 - 重寫
set:數據變化時 發佈通知,觸發視圖更新。
function defineReactive(obj, key, val) {
const dep = new Dep() // 依賴收集器
Object.defineProperty(obj, key, {
get() {
Dep.target && dep.add(Dep.target) // 收集依賴
return val
},
set(newVal) {
if (newVal !== val) {
val = newVal
dep.notify() // 派發更新
}
}
})
}
硬傷
| 場景 | 問題 | 解決姿勢 |
|---|---|---|
| 新增/刪除屬性 | this.obj.foo = 1 視圖不刷 |
Vue.set(this.obj,'foo',1) |
| 數組索引修改 | arr[0] = 1 不刷 |
Vue.set(arr,0,1) 或 arr.splice(0,1,1) |
| 深層嵌套 | 遞歸 defineProperty,性能差 |
——(只能忍) |
2 | Vue 3:Proxy 帶來的全場“無死角”監聽
核心思路
- 用
Proxy一次性包裹整個對象,對 任意 get / set / delete / in 做統一攔截。 Reflect保證語義一致、返回值更準確。
const handler = {
get(target, key, receiver) {
track(target, key) // 依賴收集
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
const res = Reflect.set(target, key, value, receiver)
trigger(target, key) // 派發更新
return res
}
}
const state = new Proxy(obj, handler)
爽點
- 任意層級、數組、
Map/Set都能自動響應。 - 不用
Vue.set/Vue.delete,直接增刪屬性即可。 - 多份代理共享同一依賴收集表,內存更省。
兼容性提醒
- 徹底告別 IE11;如要兼容老瀏覽器,只能繼續用 Vue 2。
3 | 響應式“坑點”與深度監聽
-
結構性解構會丟響應
const state = reactive({ x: 1 }) const { x } = state // x 只是普通值 x++ // 視圖!不會!動!- 正確姿勢:
const { x } = toRefs(state)或const x = toRef(state, 'x')
- 正確姿勢:
-
基本類型必須
refconst count = ref(0) count.value++ // 視圖刷! -
數組 & Set/Map
- 內部元素是對象時,仍會被自動代理;但 數組解構 同樣會丟響應。
4 | API 大亂鬥:reactive、ref、toRef、toRefs 到底啥區別?
| API | 語義 | 典型用法 | 常見誤區 |
|---|---|---|---|
reactive(obj) |
對象/數組 響應式代理 | const s = reactive({ a: 1 }) |
不能直接包基本類型 |
ref(value) |
包裝任何值 為響應式 Ref |
const num = ref(0),num.value++ |
忘記 .value |
toRef(obj,'key') |
把對象裏某字段 ➜ Ref |
const nameRef = toRef(user,'name') |
源對象刪了字段,Ref 變 undefined |
toRefs(obj) |
對象所有鍵 ➜ Ref 批量解構 |
const { a,b } = toRefs(s) |
源對象不是 reactive 時意義不大 |
🧾 結語:理解響應式,寫 Vue 才能胸有成竹
Vue 幫你把 數據 → 視圖 的髒活累活全包了,但如果你不瞭解背後的攔截原理、API 侷限,就會掉進“頁面怎麼不更新”的大坑。
- Vue 2:
defineProperty+ 手工Vue.set/$delete - Vue 3:
Proxy+ 全局攔截,API 簡潔感人 - 深度監聽 & Ref 系列:實戰中最易掉坑的痛點,一定要記住
最後一句靈魂拷問:
你的組件不刷新的時候,是怪 Vue,還是該反思自己是不是真·懂響應式?
好啦,今天的內容就先到這裏!如果覺得我的分享對你有幫助,給我點個贊,順便評論吐個槽,當然不要忘了三連哦!感謝大家的支持,記得常回來,我是wangfang呀等着你們的下一次訪問!