對於深入響應式原理的深刻理解

2022-09-11 14:06:21 字數 3789 閱讀 1402

先說我對深入響應式原理的理解:

vue 最顯著的特性之一便是不太引人注意的響應式系統(reactivity system),

它是乙個典型的 mvvm 框架。模型層(model)只是普通 j**ascript 物件,修改它則更新檢視(view)。這使得狀態管理非常簡單直接,但是如果你理解其工作原理,這樣你就可以迴避一些常見的問題。

下面我們來深入探索一下 vue 響應式系統的底層的細節:

當你把乙個普通的 j**ascript 物件傳入 vue 例項作為data選項,vue 將遍歷此物件所有的屬性,並使用object.defineproperty把這些屬性全部轉為 getter/setter。object.defineproperty是 es5 中乙個無法 shim 的特性,這也就是 vue 不支援 ie8 以及更低版本瀏覽器的原因。

這些 getter/setter 對使用者來說是不可見的,但是在內部它們讓 vue 能夠追蹤依賴,在屬性被訪問和修改時通知變更。這裡需要注意的是不同瀏覽器在控制台列印資料物件時對 getter/setter 的格式化並不同,所以建議安裝 vue-devtools 來獲取對檢查資料更加友好的使用者介面。

每個元件例項都對應乙個 watcher 例項,它會在元件渲染的過程中把「接觸」過的資料屬性記錄為依賴。之後當依賴項的 setter 觸發時,會通知 watcher,從而使它關聯的元件重新渲染。

先就關於如何追蹤變化舉個簡單的例子:

id="main">

執行後,我們可以從頁面中看到,count 後面的 times 每隔 1s 遞增 1,檢視一直在更新。在**中僅僅是通過 setinterval 方法每隔 1s 來修改 vm.times 的值,並沒有任何 dom 操作。

那麼 vue.js 是如何實現這個過程的呢?我們可以通過一張圖來看一下,如下圖所示:

圖中的模型(model)就是 data 方法返回的,檢視(view)是最終在瀏覽器中顯示的dom。模型通過observer、dep、watcher、directive等一系列物件的關聯,最終和檢視建立起關係。歸納起來,vue.js在這裡主要做了三件事:

受現代 j**ascript 的限制 (而且object.observe也已經被廢棄),vue 無法檢測到物件屬性的新增或刪除。由於 vue 會在初始化例項時對屬性執行 getter/setter 轉化,所以屬性必須在data物件上存在才能讓 vue 將它轉換為響應式的。例如:

var vm = new vue(

})// `vm.a` 是響應式的

vm.b = 2

// `vm.b` 是非響應式的

對於已經建立的例項,vue 不允許動態新增根級別的響應式屬性。但是,可以使用vue.set(object, propertyname, value)方法向巢狀物件新增響應式屬性。例如,對於:

vue.set(vm.someobject, 'b', 2)

您還可以使用vm.$set例項方法,這也是全域性vue.set方法的別名:

this.$set(this.someobject,'b',2)

有時你可能需要為已有物件賦值多個新屬性,比如使用object.assign()_.extend()。但是,這樣新增到物件上的新屬性不會觸發更新。在這種情況下,你應該用原物件與要混合進去的物件的屬性一起建立乙個新的物件。

// 代替 `object.assign(this.someobject, )`

this.someobject = object.assign({}, this.someobject, )

也有一些陣列相關的注意事項,之前已經在列表渲染中講過。

宣告響應式屬性

由於 vue 不允許動態新增根級響應式屬性,所以你必須在初始化例項前宣告所有根級響應式屬性,哪怕只是乙個空值:

var vm = new vue(,

template: '}'})

// 之後設定 `message`

vm.message = 'hello!'

如果你未在data選項中宣告message,vue 將警告你渲染函式正在試圖訪問不存在的屬性。

這樣的限制在背後是有其技術原因的,它消除了在依賴項跟蹤系統中的一類邊界情況,也使 vue 例項能更好地配合型別檢查系統工作。但與此同時在**可維護性方面也有一點重要的考慮:data物件就像元件狀態的結構 (schema)。提前宣告所有的響應式屬性,可以讓元件**在未來修改或給其他開發人員閱讀時更易於理解。

可能你還沒有注意到,vue 在更新 dom 時是非同步執行的。只要偵聽到資料變化,vue 將開啟乙個佇列,並緩衝在同一事件迴圈中發生的所有資料變更。如果同乙個 watcher 被多次觸發,只會被推入到佇列中一次。這種在緩衝時去除重複資料對於避免不必要的計算和 dom 操作是非常重要的。然後,在下乙個的事件迴圈「tick」中,vue 重新整理佇列並執行實際 (已去重的) 工作。vue 在內部對非同步佇列嘗試使用原生的promise.thenmutationobserversetimmediate,如果執行環境不支援,則會採用settimeout(fn, 0)代替。

例如,當你設定vm.somedata = 'new value',該元件不會立即重新渲染。當重新整理佇列時,元件會在下乙個事件迴圈「tick」中更新。多數情況我們不需要關心這個過程,但是如果你想基於更新後的 dom 狀態來做點什麼,這就可能會有些棘手。雖然 vue.js 通常鼓勵開發人員使用「資料驅動」的方式思考,避免直接接觸 dom,但是有時我們必須要這麼做。為了在資料變化之後等待 vue 完成更新 dom,可以在資料變化之後立即使用vue.nexttick(callback)。這樣**函式將在 dom 更新完成後被呼叫。例如:

}div>

var vm = new vue(

})vm.message = 'new message' // 更改資料

vm.$el.textcontent === 'new message' // false

vue.nexttick(function () )

在元件內使用vm.$nexttick()例項方法特別方便,因為它不需要全域性vue,並且**函式中的this將自動繫結到當前的 vue 例項上:

vue.component('example', }',

data: function ()

},methods: )

}}})

因為$nexttick()返回乙個promise物件,所以你可以使用新的es2016asynd/await語法完成相同的事情:

methods: 

}

對MapReduce Yarn的深刻理解

1.mapreduce詳細工作流程之map階段 2.mapreduce流程之reduce階段巨集觀上看 reducetask分為四個階段 1.copy 2.merge 3.sort 4.reduce 1.copy reducetask將遠端從maptask上覆制過來要處理的資料,針對某一片資料,如果...

深刻理解Vue中的元件

今天看了下vue官網上關於元件的教程,感覺內容還挺多,現在把元件中基本的知識梳理一下。註冊元件就是利用vue.component 方法,先傳入乙個自定義元件的名字,然後傳入這個元件的配置。vue.component mycomponent 如上方式,就已經建立了乙個自定義元件,然後就可以在vue例項...

深刻理解MyBatis中 和 的區別

首先看一下下面這兩個sql語句的區別 select id,username,password,role from user where username and password select id,username,password,role from user where username an...