摘要:在原理中,最重要的部分就是如何實(shí)現(xiàn)數(shù)據(jù)的觀測(cè),依賴的收集,視圖的更新。本文講的就是這三個(gè)的簡單實(shí)現(xiàn)。這樣做代碼似乎有點(diǎn)丑,我們?cè)谠O(shè)置屬性觸發(fā)會(huì)發(fā)生函數(shù),有沒有一種更加智能的方式來實(shí)現(xiàn)通知變化呢。
在vue原理中,最重要的部分就是如何實(shí)現(xiàn)數(shù)據(jù)的觀測(cè),依賴的收集,視圖的更新。本文講的就是Observer, Dep, Watcher這三個(gè)的簡單實(shí)現(xiàn)。
pub(publish)表示發(fā)布者,sub(subscribe)表示訂閱者, cb(callback)表示回調(diào)函數(shù)
如果你覺得這篇講的對(duì)你有所幫助,請(qǐng)幫我點(diǎn)個(gè)star
Observer的作用簡單來說就是讓object對(duì)象的屬性都用Object.defineProperty()來進(jìn)行定義,這樣當(dāng)獲取object的屬性,或者修改屬性的時(shí)候,就能夠觸發(fā)get,set達(dá)到數(shù)據(jù)的觀測(cè)的效果。
class Observer { constructor(value) { this.value = value this.walk(this.value) } walk (value) { // 遞歸遍歷value的屬性 Object.keys(value).forEach((key) = > { defineReactive(value, key, value[key]) }) } } function defineReactive(obj, key ,val) { let childOb = observe(val) Obeject.defineProperty(obj, key, { enumerable: true, configurable: true, get() { console.log("") return val }, set(newVal) { console.log("") val = newVal childOb = observe(val) } }) } function observe (value) { if (typeof value === "object" && !Array.isArray(value)) { value = new Observer(value) } }
defineReactive的作用就是給對(duì)象的屬性進(jìn)行簡單的數(shù)據(jù)觀測(cè),一旦值獲取或者設(shè)置就會(huì)觸發(fā)一些行為.因?yàn)橐粋€(gè)對(duì)象的屬性可能還是對(duì)象,所以在這里我們添加observe函數(shù)來遍歷值,讓一個(gè)對(duì)象的屬性的屬性還是可以進(jìn)行觀測(cè)的,簡單呢來說的意思就是讓所有屬性都可以進(jìn)行忽略。當(dāng)然在實(shí)際情況中,我們還需要考慮數(shù)組的情況,但都大同小異。
這樣做代碼似乎有點(diǎn)丑,我們?cè)谠O(shè)置屬性觸發(fā)set會(huì)發(fā)生console.log()函數(shù),有沒有一種更加智能的方式來實(shí)現(xiàn)通知變化呢。這里我們就需要用消息訂閱器來進(jìn)行實(shí)現(xiàn),這樣做我們就不需要通過觀察console.log()輸出的值來看進(jìn)行的情況,我們只需要在set方法里邊加一個(gè)通知,一旦值發(fā)生變化,就通知外邊值發(fā)生了改變
Dep的作用就是用來收集屬性值的變化,一旦set方法觸發(fā)的時(shí)候,就更新視圖。那就準(zhǔn)備一個(gè)數(shù)組來進(jìn)行收集吧!
下面是Dep的實(shí)現(xiàn):
class Dep { constructor() { this.subs = [] } addSub (sub) { this.subs.push(sub) } notify () { const subs = this.subs.slice() subs.forEach((sub) => { sub.update() // 視圖更新 }) } }
上面就是Dep的簡單實(shí)現(xiàn),addSub的作用是增加訂閱者,因?yàn)橛泻芏嘤嗛喺?,我們需要用一個(gè)數(shù)組將它進(jìn)行存儲(chǔ),notify()函數(shù)的作用就是當(dāng)set發(fā)生的時(shí)候,進(jìn)行通知,update()這個(gè)函數(shù)待會(huì)在watcher中會(huì)講到。實(shí)現(xiàn)了Dep我們是不是該更改了set()函數(shù)了呢,下面是defineReactive()修改后的代碼
function defineReactive(obj, key ,val) { let dep = new Dep() // 畢竟要使用Dep的方法 let childOb = observe(val) Obeject.defineProperty(obj, key, { enumerable: true, configurable: true, get() { return val }, set(newVal) { val = newVal childOb = observe(val) dep.notify() // 因?yàn)閿?shù)據(jù)改變了,我們就通知Dep } }) }
一旦觸發(fā)set,就調(diào)用dep.notify(),notify的作用就是針對(duì)訂閱者遍歷進(jìn)行更新。
Watcher的簡單實(shí)現(xiàn):watcher的作用,就是當(dāng)狀態(tài)發(fā)生改變的時(shí)候,更新視圖,我們可以假設(shè)
class Watcher { constructor (vm, cb, expOrFn) { this.vm = vm // 這表示一個(gè)Vue的實(shí)例 this.cb = cb // 這里需要考慮expOrFn是字符串或者函數(shù)的情況 // 這里做一個(gè)簡化,只考慮函數(shù)的情況 this.getter = expOrFn this.value = this.get() } get () { Dep.target = this const vm = this.vm value = this.getter.call(this.vm, vm) Dep.target = null return value } update () { this.run() } run () { const value = this.get() if (value !== this.value) { const oldValue = this.value this.value = value this.cb.call(this.vm, value, oldValue) } } }
Watcher的簡單實(shí)現(xiàn)就完成了,在Dep()構(gòu)造函數(shù)中,我們使用了sub.update()這行代碼,而update函數(shù)是Watcher里邊的方法,說明每一個(gè)sub都是Wathcer的實(shí)例,問題是我們應(yīng)該如何通過addSub()這個(gè)方法,將Watcher加入到subs這個(gè)數(shù)組中盡心存儲(chǔ)呢,答案還是在defineReactive()里邊進(jìn)行修改
function defineReactive(obj, key ,val) { let dep = new Dep() // 畢竟要使用Dep的方法 let childOb = observe(val) Obeject.defineProperty(obj, key, { enumerable: true, configurable: true, get() { if(Dep.target) { dep.addSub(Dep.target) } return val }, set(newVal) { val = newVal childOb = observe(val) dep.notify() // 因?yàn)閿?shù)據(jù)改變了,我們就通知Dep } }) }
這樣是不是就實(shí)現(xiàn)了往Dep里邊加Watcher了,vue源碼中比這個(gè)復(fù)雜的多,各種參數(shù),看著頭大。本文的宗旨就是通過簡化讓你了解內(nèi)部原理,如果需要更深入了解就需要閱讀源碼了。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/92151.html
摘要:后端路由簡介路由這個(gè)概念最先是后端出現(xiàn)的。前端路由模式隨著的流行,異步數(shù)據(jù)請(qǐng)求交互運(yùn)行在不刷新瀏覽器的情況下進(jìn)行。通過這些就能用另一種方式來實(shí)現(xiàn)前端路由了,但原理都是跟實(shí)現(xiàn)相同的。 后端路由簡介 路由這個(gè)概念最先是后端出現(xiàn)的。在以前用模板引擎開發(fā)頁面時(shí),經(jīng)常會(huì)看到這樣 http://www.xxx.com/login 大致流程可以看成這樣: 瀏覽器發(fā)出請(qǐng)求 服務(wù)器監(jiān)聽到80端口(或4...
答案自己谷歌或百度找。 一、來源背景 面試題是來自微博@??途W(wǎng)發(fā)布的真實(shí)大廠前端面經(jīng)題目,我一直在收集題目長期一個(gè)一個(gè)的記錄下來的,可能會(huì)有重復(fù),但基本前端的面試大綱和需要掌握的知識(shí)都在其中了,面試題僅做學(xué)習(xí)參考,學(xué)習(xí)者閱后也要用心鉆研其中的原理,重要知識(shí)需要系統(tǒng)學(xué)習(xí)、透徹學(xué)習(xí),形成自己的知識(shí)鏈。 二、532道前端真實(shí)大廠面試題 express和koa的對(duì)比,兩者中間件的原理,koa捕獲異常多種情...
答案自己谷歌或百度找。 一、來源背景 面試題是來自微博@牛客網(wǎng)發(fā)布的真實(shí)大廠前端面經(jīng)題目,我一直在收集題目長期一個(gè)一個(gè)的記錄下來的,可能會(huì)有重復(fù),但基本前端的面試大綱和需要掌握的知識(shí)都在其中了,面試題僅做學(xué)習(xí)參考,學(xué)習(xí)者閱后也要用心鉆研其中的原理,重要知識(shí)需要系統(tǒng)學(xué)習(xí)、透徹學(xué)習(xí),形成自己的知識(shí)鏈。 二、532道前端真實(shí)大廠面試題 express和koa的對(duì)比,兩者中間件的原理,koa捕獲異常多種情...
答案自己谷歌或百度找。 一、來源背景 面試題是來自微博@??途W(wǎng)發(fā)布的真實(shí)大廠前端面經(jīng)題目,我一直在收集題目長期一個(gè)一個(gè)的記錄下來的,可能會(huì)有重復(fù),但基本前端的面試大綱和需要掌握的知識(shí)都在其中了,面試題僅做學(xué)習(xí)參考,學(xué)習(xí)者閱后也要用心鉆研其中的原理,重要知識(shí)需要系統(tǒng)學(xué)習(xí)、透徹學(xué)習(xí),形成自己的知識(shí)鏈。 二、532道前端真實(shí)大廠面試題 express和koa的對(duì)比,兩者中間件的原理,koa捕獲異常多種情...
摘要:雙向數(shù)據(jù)綁定指的是,將對(duì)象屬性變化與視圖的變化相互綁定。數(shù)據(jù)雙向綁定已經(jīng)了解到是通過數(shù)據(jù)劫持的方式來做數(shù)據(jù)綁定的,其中最核心的方法便是通過來實(shí)現(xiàn)對(duì)屬性的劫持,達(dá)到監(jiān)聽數(shù)據(jù)變動(dòng)的目的。和允許觀察數(shù)據(jù)的更改并觸發(fā)更新。 1 MVVM 雙向數(shù)據(jù)綁定指的是,將對(duì)象屬性變化與視圖的變化相互綁定。換句話說,如果有一個(gè)擁有name屬性的user對(duì)象,與元素的內(nèi)容綁定,當(dāng)給user.name賦予一個(gè)新...
閱讀 1848·2021-09-22 15:23
閱讀 3289·2021-09-04 16:45
閱讀 1920·2021-07-29 14:49
閱讀 2783·2019-08-30 15:44
閱讀 1531·2019-08-29 16:36
閱讀 1055·2019-08-29 11:03
閱讀 1524·2019-08-26 13:53
閱讀 517·2019-08-26 11:57