專欄分享:vue2源碼專欄,vue3源碼專欄,vue router源碼專欄,玩具項目專欄,硬核??推薦??
歡迎各位ITer關(guān)注點(diǎn)贊收藏??????
在學(xué)習(xí) Vue3 是如何進(jìn)行對象的響應(yīng)式代理之前,我想我們應(yīng)該先去了解下 ES6 新增的API Proxy
與 Reflect
,可參考【Vue3響應(yīng)式入門#02】Proxy and Reflect 。之后我們再手寫下 reactive 和 effect 的源碼
Reactive
定義: 接收一個普通對象然后返回該普通對象的響應(yīng)式代理。等同于 2.x 的 Vue.observable()
const obj = reactive({ count: 0 })
響應(yīng)式轉(zhuǎn)換是“深層的”:會影響對象內(nèi)部所有嵌套的屬性。基于 ES6 的 Proxy 實(shí)現(xiàn),返回的代理對象不等于原始對象。建議僅使用代理對象而避免依賴原始對象。
reactive.ts
Vue3中響應(yīng)數(shù)據(jù)核心是 reactive , reactive 中的實(shí)現(xiàn)是由 proxy 加 effect 組合,先來看一下 reactive 方法的定義
import { isObject } from '@vue/shared'
import { mutableHandlers, ReactiveFlags } from './baseHandler'
// key只能是對象;弱引用,更有效的垃圾回收、釋放內(nèi)存 - https://www.zhangxinxu.com/wordpress/2021/08/js-weakmap-es6/
const reactiveMap = new WeakMap()
/**
* @desc 將數(shù)據(jù)轉(zhuǎn)化成響應(yīng)式的數(shù)據(jù)
*/
export function reactive(target) {
// issue1
if (!isObject(target)) {
return
}
// issue2
if (target[ReactiveFlags.IS_REACTIVE]) {
return target
}
// issue3
let existingProxy = reactiveMap.get(target)
if (existingProxy) {
return existingProxy
}
const proxy = new Proxy(target, mutableHandlers)
reactiveMap.set(target, proxy)
return proxy
}
-
@issue1 只能做對象的代理,不是對象,return
-
@issue2 代理對象被再次代理 可以直接返回代理對象
我們可以利用 Proxy 的 get方法,來判斷他有沒有代理過
如果訪問這個對象的
__v_isReactive
屬性,有值就說明代理過了,當(dāng)然,我們可以約定__v_isReactive
為任何字段 -
@issue3 同一個對象代理多次,返回同一個代理
用 WeakMap去緩存對象和代理對象的映射關(guān)系
代理完成時,將此對象和代理對象添加到 WeakMap緩存中;在代理之前,去 WeakMap中讀取此對象是否有代理對象的映射,若存在,則返回緩存中的代理對象
WeakMap:key只能是對象;弱引用,更有效的垃圾回收、釋放內(nèi)存,詳情請參考JS WeakMap應(yīng)該什么時候使用 ? 張鑫旭-鑫空間-鑫生活
baseHandler.ts
mutableHandlers 是 Proxy 的第二個參數(shù) handler對象提取封裝而來
- track () 依賴收集
- trigger () 觸發(fā)依賴
這兩個函數(shù)為 effect 里的方法,effect 為 reactive 的核心,后面我們會詳細(xì)介紹。先來看一下mutableHandlers 對象的定義
import { track, trigger } from './effect'
export const enum ReactiveFlags {
IS_REACTIVE = '__v_isReactive',
}
export const mutableHandlers = {
// 這里可以監(jiān)控到用戶取值了
get(target, key, receiver) {
if (key === ReactiveFlags.IS_REACTIVE) {
return true
}
track(target, 'get', key)
let res = Reflect.get(target, key, receiver)
// @issue1
// 深度代理實(shí)現(xiàn), 性能好 取值就可以進(jìn)行代理
if (isObject(res)) {
return reactive(res)
}
return res
},
// 這里可以監(jiān)控到用戶設(shè)置值了
set(target, key, value, receiver) {
let oldValue = target[key] // 緩存老值
let result = Reflect.set(target, key, value, receiver)
if (oldValue !== value) {
// 值變化了,觸發(fā)依賴
trigger(target, 'set', key)
}
return result
},
}
-
@issue1 嵌套對象深度代理。只有在取值時,才會進(jìn)行深度代理,性能好
舉個例子,看如下代碼。當(dāng)我們對嵌套對象
product.rate
進(jìn)行取值時,就會觸發(fā) get劫持,然后深度代理嵌套對象product.rate
const product = reactive({
price: 5,
quantity: 2,
rate: {
value: 0.9
}
})
shared.ts
共享模塊文章來源:http://www.zghlxwxcb.cn/news/detail-746532.html
// 判斷是否是JS對象
export const isObject = function(value){
return typeof value === 'object' && value !== null
}
參考資料
JS WeakMap應(yīng)該什么時候使用 ? 張鑫旭-鑫空間-鑫生活文章來源地址http://www.zghlxwxcb.cn/news/detail-746532.html
到了這里,關(guān)于【源碼系列#01】vue3響應(yīng)式原理(Proxy)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!