<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
前言:
本篇內容基於Vue3計算屬性是如何實現的實現。
前面我們聊到計算屬性,它可以自動計算並快取響應式資料的值。而如果我們僅需要在響應式資料變化時,執行一些預設的操作,就可以使用watch
偵聽器。我們還是先來實現一個最簡單的例子,然後來一點一點擴充它。
const data = {foo: 1} const obj = reactive(data) watch(obj, () => { console.log('obj已改變') })
在這個例子中,我們使用了watch
偵聽器,當obj
的屬性被改變時,控制檯應該會列印出obj已改變
。基於前面我們對計算屬性的實現,這裡我們已經有了一個大概的思路。把watch
視為響應式物件的副作用函數,當響應式物件改變時,觸發執行該副作用函數。
想要觸發副作用函數,必須先收集它,還記得副作用函數是如何收集的嗎?對,當響應式資料被get
時,收集副作用函數。所以首先,我們需要讓watch
被響應式物件收集到。
function watch(getter, cb) { effect( () => getter.foo ) }
接著,我們還需要讓我們預設的方法被執行。當響應式資料被set
時,觸發副作用函數。這裡我們想觸發的是cb
這個傳入的回撥函數,這裡我們就又能用到實現計算屬性時的排程器了,當排程器存在時,set
觸發的trigger
會先執行排程器中的函數。
function watch(getter, cb) { effect( () => getter.foo, { scheduler() { cb() } } ) }
一個簡單的偵聽器已經完成了!這裡我們為了簡單,把功能寫死了,僅支援對obj.foo
的偵聽。接下來,我們就要想想,如何實現對響應式物件的任意屬性進行偵聽?
按照前面的思路,想要實現對響應式物件的任意屬性的偵聽,就需要我們get
到該物件的每一個屬性,這就需要我們對響應式物件進行一次遞迴遍歷。
function traverse(value, seen = new Set()) { // (1) if(typeof value !== 'object' || value === null || seen.has(value)) return seen.add(value) for(const key in value) { traverse(value[key], seen) } return value }
為了避免遞迴遍歷物件時,迴圈參照造成的死迴圈,我們在(1)
處建立了Set
,當重複出現相同的物件時,直接返回。
在Vue3中,我們不能直接偵聽響應式物件的屬性值。如果需要偵聽響應式物件的屬性值,就需要一個getter
函數,讓偵聽器能被響應式物件收集到。
const data = { foo: 1 } const obj = reactive(data) watch( () => obj.foo, () => { console.log('obj.foo已改變') })
指定了屬性就意味著,當前的偵聽器僅會被指定的屬性觸發,就無需遞迴遍歷整個響應式物件了。
function watch(getter, cb) { if(typeof getter !== 'function') getter = traverse(getter) // (2) effect( () => getter(), { scheduler() { cb() } } ) }
在(2)處,我們增加了一個判斷,如果傳入的已經是getter
函數,我們直接使用,如果不是getter
函數,則認為是一個響應式物件,就需要進行遞迴遍歷。
在Vue中我們還需要能夠在回撥函數cb()
中拿到響應式資料更新前後的新值與舊值。
const data = { foo: 1 } const obj = reactive(data) watch( () => obj.foo, (newValue, oldValue) => { console.log(newValue, oldValue) })
接下來的問題是,如何獲取newValue
與oldValue
。newValue
好解決,執行完回撥函數cb()
得到的就是newValue
,但這裡如何獲取oldValue
的值呢?要從watch
中拿到舊值,那就不能讓副作用函數被立即執行。這裡想到了什麼?對,在實現計算屬性的時候,我們用到過的lazy
,它可以禁止副作用函數自動執行。
function watch(getter, cb) { if(typeof getter !== 'function') getter = traverse(getter) let oldValue const effectFn = effect( () => getter(), { lazy: true, // (3) scheduler() { cb(oldValue) } } ) oldValue = effectFn() // (4) }
在(3)處我們設定了lazy
開關,設定了lazy
後,副作用函數的執行權就交到了我們自己手上。在(4)處,我們手動執行了副作用函數。這裡可以需要我們向前回顧一下,前面我們傳入的getter
是一個函數() => obj.foo
,而effect
函數的第一個引數就是真正被執行的副作用函數,所以我們手動執行的,其實就是函數() => obj.foo
,這樣我們就拿到了舊值。
如何獲取新值呢?在響應式資料的值更新後,副作用函數effect
會被觸發執行,當排程器屬性存在時,執行排程器。在排程器中,我們可以再次執行副作用函數,通過() => obj.foo
拿到改變後的新值。
function watch(getter, cb) { if(typeof getter !== 'function') getter = traverse(getter) let oldValue, newValue const effectFn = effect( () => getter(), { lazy: true, scheduler() { newValue = effectFn() cb(newValue, oldValue) oldValue = newValue // (5) } } ) oldValue = effectFn() }
在(5)處,執行完回撥函數cb()
,我們進行了一下善後工作,更新了oldValue
的值,為下一次回撥做準備。
有時,我們還希望偵聽器可以在建立時就立即執行回撥函數。
const data = { foo: 1 } const obj = reactive(data) watch( () => obj.foo, (newValue, oldValue) => { console.log('newValue:', newValue,', oldValue:', oldValue) }, { immediate: true } )
當immediate
的值為true
時,需要立即執行。明確了需求,我們來完善watch
偵聽器。
function watch(getter, cb, options = {}) { if(typeof getter !== 'function') getter = traverse(getter) let oldValue, newValue function job() { // (6) newValue = effectFn() cb(newValue, oldValue) oldValue = newValue } const effectFn = effect( () => getter(), { lazy: true, scheduler: job, } ) if(options.immediate) { // (7) job() } else { oldValue = effectFn() } }
在(6)處,我們抽離了回撥函數的執行邏輯,當options.immediate
存在時,直接觸發執行。
到此這篇關於Vue3偵聽器是如何實現的的文章就介紹到這了,更多相關Vue3偵聽器內容請搜尋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