<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
前言:
Vue3重寫了響應式系統,和Vue2相比底層採用Proxy
物件實現,在初始化的時候不需要遍歷所有的屬性再把屬性通過defineProperty轉換成get和set。另外如果有多層屬性巢狀的話只有存取某個屬性的時候才會遞迴處理下一級的屬性所以Vue3中響應式系統的效能要比Vue2好。
接下來我們自己實現Vue3響應式系統的核心函數(reactive/ref/toRefs/computed/effect/track/trigger)
來學習一下響應式原理。
首先我們使用Proxy來實現響應式中的第一個函數reactive
。
reactive
接收一個引數,首先要判斷這個引數是否是一個物件,如果不是直接返回,reactive
只能將物件轉換成響應式物件,這是和ref不同的地方。
接著會建立攔截器物件handler, 其中抱哈get
,set
,deleteProperty
等攔截方法,最後建立並返回Proxy物件。
// 判斷是否是一個物件 const isObject = val => val !== null && typeof val === 'object' // 如果是物件則呼叫reactive const convert= target => isObject(target) ? reactive(target) : target // 判斷物件是否存在key屬性 const haOwnProperty = Object.prototype.hasOwnProperty const hasOwn = (target, key) => haOwnProperty.call(target, key) export function reactive (target) { if (!isObject(target)) { // 如果不是物件直接返回 return target } const handler = { get (target, key, receiver) { // 收集依賴 const result = Reflect.get(target, key, receiver) // 如果屬性是物件則需要遞迴處理 return convert(result) }, set (target, key, value, receiver) { const oldValue = Reflect.get(target, key, receiver) let result = true; // 需要判斷當前傳入的新值和oldValue是否相等,如果不相等再去覆蓋舊值,並且觸發更新 if (oldValue !== value) { result = Reflect.set(target, key, value, receiver) // 觸發更新... } // set方法需要返回布林值 return result; }, deleteProperty (target, key) { // 首先要判斷當前target中是否有自己的key屬性 // 如果存在key屬性,並且刪除要觸發更新 const hasKey = hasOwn(target, key) const result = Reflect.deleteProperty(target, key) if (hasKey && result) { // 觸發更新... } return result; } } return new Proxy(target, handler) }
至此reactive
函數就寫完了,接著我們來編寫一下收集依賴的過程。
在依賴收集的過程會建立三個集合,分別是targetMap
,depsMap
以及dep
。
其中targetMap
是用來記錄目標物件和字典他使用的是weakMap
,key是目標物件,targetMap的值是depsMap, 型別是Map,這裡面的key是目標物件的屬性名稱,值是一個Set集合,集合中儲存的元素是Effect函數。因為可以多次呼叫同一個Effect在Effect存取同一個屬性,這個時候這個屬性會收集多次依賴對應多個Effect函數。
一個屬性可以對應多個Effect函數,觸發更新的時候可以通過屬性找到對應的Effect函數,進行執行。
我們這裡分別來實現一下effect
和track
兩個函數。
effect函數接收一個函數作為引數,我們首先在外面定一個變數儲存callback, 這樣track函數就可以存取到callback了。
let activeEffect = null; export function effect (callback) { activeEffect = callback; // 存取響應式物件屬性,收集依賴 callback(); // 依賴收集結束要置null activeEffect = null; }
track
函數接收兩個引數目標物件和屬性, 他的內部要將target
儲存到targetMap
中。需要先定義一個這樣的Map。
let targetMap = new WeakMap() export function track (target, key) { // 判斷activeEffect是否存在 if (!activeEffect) { return; } // depsMap儲存物件和effect的對應關係 let depsMap = targetMap.get(target) // 如果不存在則建立一個map儲存到targetMap中 if (!depsMap) { targetMap.set(target, (depsMap = new Map())) } // 根據屬性查詢對應的dep物件 let dep = depsMap.get(key) // dep是一個集合,用於儲存屬性所對應的effect函數 if (!dep) { // 如果不存在,則建立一個新的集合新增到depsMap中 depsMap.set(key, (dep = new Set())) } dep.add(activeEffect) }
track
是依賴收集的函數。需要在reactive
函數的get方法中呼叫。
get (target, key, receiver) { // 收集依賴 track(target, key) const result = Reflect.get(target, key, receiver) // 如果屬性是物件則需要遞迴處理 return convert(result) },
這樣整個依賴收集就完成了。接著就要實現觸發更新,對應的函數是trigger,這個過程和track的過程正好相反。
trigger
函數接收兩個引數,分別是target和key。
export function trigger (target, key) { const depsMap = targetMap.get(target) // 如果沒有找到直接返回 if (!depsMap) { return; } const dep = depsMap.get(key) if (dep) { dep.forEach(effect => { effect() }) } }
trigger函數要在reactive函數中的set
和deleteProperty
中觸發。
set (target, key, value, receiver) { const oldValue = Reflect.get(target, key, receiver) let result = true; // 需要判斷當前傳入的新值和oldValue是否相等,如果不相等再去覆蓋舊值,並且觸發更新 if (oldValue !== value) { result = Reflect.set(target, key, value, receiver) // 觸發更新... trigger(target, key) } // set方法需要返回布林值 return result; }, deleteProperty (target, key) { // 首先要判斷當前target中是否有自己的key屬性 // 如果存在key屬性,並且刪除要觸發更新 const hasKey = hasOwn(target, key) const result = Reflect.deleteProperty(target, key) if (hasKey && result) { // 觸發更新... trigger(target, key) } return result; }
ref接收一個引數可以是原始值也可以是一個物件,如果傳入的是物件並且是ref建立的物件則直接返回,如果是普通物件則呼叫reactive來建立響應式物件,否則建立一個只有value屬性的響應式物件。
export function ref (raw) { // 判斷raw是否是ref建立的物件,如果是直接返回 if (isObject(raw) && raw.__v__isRef) { return raw } // 之前已經定義過convert函數,如果引數是物件就會呼叫reactive函數建立響應式 let value = convert(raw); const r = { __v__isRef: true, get value () { track(r, 'value') return value }, set value (newValue) { // 判斷新值和舊值是否相等 if (newValue !== value) { raw = newValue value = convert(raw) // 觸發更新 trigger(r, 'value') } } } return r }
toRefs
接收reactive
函數返回的響應式物件,如果不是響應式物件則直接返回。將傳入物件的所有屬性轉換成一個類似ref返回的物件將準換後的屬性掛載到一個新的物件上返回。
export function toRefs (proxy) { // 如果是陣列建立一個相同長度的陣列,否則返回一個空物件 const ret = proxy instanceof Array ? new Array(proxy.length) : {} for (const key in proxy) { ret[key] = toProxyRef(proxy, key) } return ret; } function toProxyRef (proxy, key) { const r = { __v__isRef: true, get value () { // 這裡已經是響應式物件了,所以不需要再收集依賴了 return proxy[key] }, set value (newValue) { proxy[key] = newValue } } return r }
toRefs
的作用其實是將reactive
中的每個屬性都變成響應式的。reactive方法會建立一個響應式的物件,但是如果將reactive返回的物件進行解構使用就不再
是響應式了,toRefs
的作用就是支援解構之後仍舊為響應式。
接著再來模擬一下computed
函數的內部實現
computed
需要接收一個有返回值的函數作為引數,這個函數的返回值就是計算屬性的值,需要監聽函數內部響應式資料的變化,最後將函數執行的結果返回。
export function computed (getter) { const result = ref() effect(() => (result.value = getter())) return result }
computed
函數會通過effect
監聽getter
內部響應式資料的變化,因為在effect中執行getter的時候存取響應式資料的屬性會去收集依賴,當資料變化會重新執行effect函數,將getter的結果再儲存到result中。
到此這篇關於vue3.0響應式函數原理詳細的文章就介紹到這了,更多相關vue3.0響應式函數原理內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45