摘要:進(jìn)階系列一之響應(yīng)式原理及實現(xiàn)進(jìn)階系列二之插件原理及實現(xiàn)進(jìn)階系列三之函數(shù)原理及實現(xiàn)什么是響應(yīng)式表示一個狀態(tài)改變之后,如何動態(tài)改變整個系統(tǒng),在實際項目應(yīng)用場景中即數(shù)據(jù)如何動態(tài)改變。描述符必須是這兩種形式之一,但二者不能共存,不然會出現(xiàn)異常。
(關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo))
Vue進(jìn)階系列匯總?cè)缦拢瑲g迎閱讀,歡迎加高級前端進(jìn)階群一起學(xué)習(xí)(文末)。
Vue 進(jìn)階系列(一)之響應(yīng)式原理及實現(xiàn)
Vue 進(jìn)階系列(二)之插件原理及實現(xiàn)
Vue 進(jìn)階系列(三)之Render函數(shù)原理及實現(xiàn)
什么是響應(yīng)式ReactivityReactivity表示一個狀態(tài)改變之后,如何動態(tài)改變整個系統(tǒng),在實際項目應(yīng)用場景中即數(shù)據(jù)如何動態(tài)改變Dom。
需求現(xiàn)在有一個需求,有a和b兩個變量,要求b一直是a的10倍,怎么做?
簡單嘗試1:let a = 3; let b = a * 10; console.log(b); // 30
乍一看好像滿足要求,但此時b的值是固定的,不管怎么修改a,b并不會跟著一起改變。也就是說b并沒有和a保持?jǐn)?shù)據(jù)上的同步。只有在a變化之后重新定義b的值,b才會變化。
a = 4; console.log(a); // 4 console.log(b); // 30 b = a * 10; console.log(b); // 40簡單嘗試2:
將a和b的關(guān)系定義在函數(shù)內(nèi),那么在改變a之后執(zhí)行這個函數(shù),b的值就會改變。偽代碼如下。
onAChanged(() => { b = a * 10; })
所以現(xiàn)在的問題就變成了如何實現(xiàn)onAChanged函數(shù),當(dāng)a改變之后自動執(zhí)行onAChanged,請看后續(xù)。
結(jié)合view層現(xiàn)在把a(bǔ)、b和view頁面相結(jié)合,此時a對應(yīng)于數(shù)據(jù),b對應(yīng)于頁面。業(yè)務(wù)場景很簡單,改變數(shù)據(jù)a之后就改變頁面b。
document .querySelector(".cell.b") .textContent = state.a * 10
現(xiàn)在建立數(shù)據(jù)a和頁面b的關(guān)系,用函數(shù)包裹之后建立以下關(guān)系。
onStateChanged(() => { document .querySelector(‘.cell.b’) .textContent = state.a * 10 })
再次抽象之后如下所示。
{{ state.a * 10 }}
onStateChanged(() => {
view = render(state)
})
view = render(state)是所有的頁面渲染的高級抽象。這里暫不考慮view = render(state)的實現(xiàn),因為需要涉及到DOM結(jié)構(gòu)及其實現(xiàn)等一系列技術(shù)細(xì)節(jié)。這邊需要的是onStateChanged的實現(xiàn)。
實現(xiàn)實現(xiàn)方式是通過Object.defineProperty中的getter和setter方法。具體使用方法參考如下鏈接。
MDN之Object.defineProperty
需要注意的是get和set函數(shù)是存取描述符,value和writable函數(shù)是數(shù)據(jù)描述符。描述符必須是這兩種形式之一,但二者不能共存,不然會出現(xiàn)異常。
實例1:實現(xiàn)convert()函數(shù)要求如下:
1、傳入對象obj作為參數(shù)
2、使用Object.defineProperty轉(zhuǎn)換對象的所有屬性
3、轉(zhuǎn)換后的對象保留原始行為,但在get或者set操作中輸出日志
示例:
const obj = { foo: 123 } convert(obj) obj.foo // 輸出 getting key "foo": 123 obj.foo = 234 // 輸出 setting key "foo" to 234 obj.foo // 輸出 getting key "foo": 234
在了解Object.defineProperty中getter和setter的使用方法之后,通過修改get和set函數(shù)就可以實現(xiàn)onAChanged和onStateChanged。
實現(xiàn):
function convert (obj) { // 迭代對象的所有屬性 // 并使用Object.defineProperty()轉(zhuǎn)換成getter/setters Object.keys(obj).forEach(key => { // 保存原始值 let internalValue = obj[key] Object.defineProperty(obj, key, { get () { console.log(`getting key "${key}": ${internalValue}`) return internalValue }, set (newValue) { console.log(`setting key "${key}" to: ${newValue}`) internalValue = newValue } }) }) }實例2:實現(xiàn)Dep類
要求如下:
1、創(chuàng)建一個Dep類,包含兩個方法:depend和notify
2、創(chuàng)建一個autorun函數(shù),傳入一個update函數(shù)作為參數(shù)
3、在update函數(shù)中調(diào)用dep.depend(),顯式依賴于Dep實例
4、調(diào)用dep.notify()觸發(fā)update函數(shù)重新運行
示例:
const dep = new Dep() autorun(() => { dep.depend() console.log("updated") }) // 注冊訂閱者,輸出 updated dep.notify() // 通知改變,輸出 updated
首先需要定義autorun函數(shù),接收update函數(shù)作為參數(shù)。因為調(diào)用autorun時要在Dep中注冊訂閱者,同時調(diào)用dep.notify()時要重新執(zhí)行update函數(shù),所以Dep中必須持有update引用,這里使用變量activeUpdate表示包裹update的函數(shù)。
實現(xiàn)代碼如下。
let activeUpdate = null function autorun (update) { const wrappedUpdate = () => { activeUpdate = wrappedUpdate // 引用賦值給activeUpdate update() // 調(diào)用update,即調(diào)用內(nèi)部的dep.depend activeUpdate = null // 綁定成功之后清除引用 } wrappedUpdate() // 調(diào)用 }
wrappedUpdate本質(zhì)是一個閉包,update函數(shù)內(nèi)部可以獲取到activeUpdate變量,同理dep.depend()內(nèi)部也可以獲取到activeUpdate變量,所以Dep的實現(xiàn)就很簡單了。
實現(xiàn)代碼如下。
class Dep { // 初始化 constructor () { this.subscribers = new Set() } // 訂閱update函數(shù)列表 depend () { if (activeUpdate) { this.subscribers.add(activeUpdate) } } // 所有update函數(shù)重新運行 notify () { this.subscribers.forEach(sub => sub()) } }
結(jié)合上面兩部分就是完整實現(xiàn)。
實例3:實現(xiàn)響應(yīng)式系統(tǒng)要求如下:
1、結(jié)合上述兩個實例,convert()重命名為觀察者observe()
2、observe()轉(zhuǎn)換對象的屬性使之響應(yīng)式,對于每個轉(zhuǎn)換后的屬性,它會被分配一個Dep實例,該實例跟蹤訂閱update函數(shù)列表,并在調(diào)用setter時觸發(fā)它們重新運行
3、autorun()接收update函數(shù)作為參數(shù),并在update函數(shù)訂閱的屬性發(fā)生變化時重新運行。
示例:
const state = { count: 0 } observe(state) autorun(() => { console.log(state.count) }) // 輸出 count is: 0 state.count++ // 輸出 count is: 1
結(jié)合實例1和實例2之后就可以實現(xiàn)上述要求,observe中修改obj屬性的同時分配Dep的實例,并在get中注冊訂閱者,在set中通知改變。autorun函數(shù)保存不變。
實現(xiàn)如下:
class Dep { // 初始化 constructor () { this.subscribers = new Set() } // 訂閱update函數(shù)列表 depend () { if (activeUpdate) { this.subscribers.add(activeUpdate) } } // 所有update函數(shù)重新運行 notify () { this.subscribers.forEach(sub => sub()) } } function observe (obj) { // 迭代對象的所有屬性 // 并使用Object.defineProperty()轉(zhuǎn)換成getter/setters Object.keys(obj).forEach(key => { let internalValue = obj[key] // 每個屬性分配一個Dep實例 const dep = new Dep() Object.defineProperty(obj, key, { // getter負(fù)責(zé)注冊訂閱者 get () { dep.depend() return internalValue }, // setter負(fù)責(zé)通知改變 set (newVal) { const changed = internalValue !== newVal internalValue = newVal // 觸發(fā)后重新計算 if (changed) { dep.notify() } } }) }) return obj } let activeUpdate = null function autorun (update) { // 包裹update函數(shù)到"wrappedUpdate"函數(shù)中, // "wrappedUpdate"函數(shù)執(zhí)行時注冊和注銷自身 const wrappedUpdate = () => { activeUpdate = wrappedUpdate update() activeUpdate = null } wrappedUpdate() }
結(jié)合Vue文檔里的流程圖就更加清晰了。
Job Done!!!
本文內(nèi)容參考自VUE作者尤大的付費視頻交流
本人Github鏈接如下,歡迎各位Star
http://github.com/yygmind/blog
我是木易楊,網(wǎng)易高級前端工程師,跟著我每周重點攻克一個前端面試重難點。接下來讓我?guī)阕哌M(jìn)高級前端的世界,在進(jìn)階的路上,共勉!
如果你想加群討論每期面試知識點,公眾號回復(fù)[加群]即可
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/98945.html
摘要:進(jìn)階系列一之響應(yīng)式原理及實現(xiàn)進(jìn)階系列二之插件原理及實現(xiàn)進(jìn)階系列三之函數(shù)原理及實現(xiàn)函數(shù)原理根據(jù)第一篇文章介紹的響應(yīng)式原理,如下圖所示。在初始化階段,本質(zhì)上發(fā)生在函數(shù)中,然后通過函數(shù)生成,根據(jù)生成。負(fù)責(zé)收集依賴,清除依賴和通知依賴。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo))showImg(https://segmentfa...
摘要:示例輸出第一步先不考慮插件,在已有的中是沒有這個公共方法的,如果要簡單實現(xiàn)的話可以通過鉤子函數(shù)來,即在里面驗證邏輯。按照插件的開發(fā)流程,應(yīng)該有一個公開方法,在里面使用全局的方法添加一些組件選項,方法包含一個鉤子函數(shù),在鉤子函數(shù)中驗證。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo))showImg(https://segmen...
摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
閱讀 2538·2021-11-24 10:20
閱讀 2385·2021-09-10 10:51
閱讀 3370·2021-09-06 15:02
閱讀 3105·2019-08-30 15:55
閱讀 2835·2019-08-29 18:34
閱讀 3070·2019-08-29 12:14
閱讀 1206·2019-08-26 13:53
閱讀 2917·2019-08-26 13:43