vue2.x響應式原理,vue與react響應式簡單對比
配合ppt食用更佳ppt
實現的最終目標
const demo = new Vue({
data: {
text: "before",
},
// 對應的template 為 <div><span>{{text}}</span></div>
render(h){
return h('div', {}, [
h('span', {}, [this.__toString__(this.text)])
])
}
})
setTimeout(function(){
demo.text = "after"
}, 3000)
對應的虛擬DOM會從
<div><span>before</span></div>
變成
<div><span>after</span></div>
第一步,監聽data下邊的所有屬性,轉換為響應式
思路
- 當data下的某個屬性變化時,如何觸發相應的函式?
方案:ES5中新添加了一個方法:Object.defineProperty,通過這個方法,可以自定義getter
和setter
函式,那麼在獲取物件屬性或者設定物件屬性時就能夠執行相應的回撥函式
程式碼如下:
class Vue {
constructor(options) {
this.$options = options
this._data = options.data
observer(options.data, this._update.bind(this))
this._update()
}
_update(){
this.$options.render()
}
}
function observer(obj, cb) {
Object.keys(obj).forEach((key) => {
defineReactive(obj, key, obj[key], cb)
})
}
function defineReactive(obj, key, val, cb) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: () => {
console.log('你訪問了' + key)
return val
},
set: newVal => {
if (newVal === val)
return
console.log('你設定了' + key)
console.log('新的' + key + ' = ' + newVal)
val = newVal
cb()
}
})
}
var demo1 = new Vue({
el: '#demo',
data: {
text: "before"
},
render(){
console.log("我要render了")
}
})
- 引發了第二個問題,如果
data
中的屬性是一個物件還能觸發我們的回掉函式麼?比如說下邊的demo
var demo2 = new Vue({
el: '#demo',
data: {
text: "before",
o: {
text: "o-before"
}
},
render(){
console.log("我要render了")
}
})
方案:用遞迴完善上邊的響應式,需要在它開始對屬性進行響應式轉換的時候,前邊加個判斷,即如下
function observer(obj) {
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === 'object') {
new observer(obj[key], cb)
}
defineReactive(obj, key, obj[key])
})
}
- 實際寫的過程中發現呼叫data的屬性時需要這樣寫
demo._data.text
,肯定是沒有demo.text
這樣寫來的方便,所以就需要加一層代理進行轉換
程式碼如下:
_proxy(key) {
const self = this
Object.defineProperty(self, key, {
configurable: true,
enumerable: true,
get: function proxyGetter() {
return self._data[key]
},
set: function proxySetter(val) {
self._data[key] = val
}
})
}
然後在建構函式中加上這麼一句話
Object.keys(options.data).forEach(key => this._proxy(key))
到此,我們的data
屬性已經變為響應式的了,只要data
的屬性發生變化,那麼就會觸發render
函式。這也是為什麼只有vue元件中的data
屬性才是響應式的,其他地方宣告的值均不是響應式的原因。但是這裡有個問題,即觸發render
函式的準確度問題!
第二步,解決準確度問題,引出虛擬dom
比如下邊的demo
new Vue({
template: `
<div>
<span>name:</span> {{name}}
<div>`,
data: {
name: 'js',
age: 24
}
})
setTimeout(function(){
demo.age = 25
}, 3000)
template
中只用到了data
中的name
屬性,但是當修改age
屬性的時候,會不會觸發渲染呢?答案是:會。但實際是不需要觸發渲染機制的
解決這個問題,先要簡單說下虛擬dom。vue有兩種寫法:
// template模板寫法(最常用的)
new Vue({
data: {
text: "before",
},
template: `
<div>
<span>text:</span> {{text}}
</div>`
})
// render函式寫法,類似react的jsx寫法
new Vue({
data: {
text: "before",
},
render (h) {
return (
<div>
<span>text:</span> {{text}}
</div>
)
}
})
由於vue2.x引入了虛擬dom的原因,這兩種寫法最終都會被解析成虛擬dom,但在這之前,他們會先被解析函式轉換成同一種表達方式,即如下:
new Vue({
data: {
text: "before",
},
render(){
return this.__h__('div', {}, [
this.__h__('span', {}, [this.__toString__(this.text)])
])
}
})
透過上邊的render
函式中的this.__h__
方法,可以簡單瞭解下虛擬dom
function VNode(tag, data, children, text) {
return {
tag: tag, // html標籤名
data: data, // 包含諸如 class 和 style 這些標籤上的屬性
children: children, // 子節點
text: text // 文字節點
}
}
寫一個簡單的虛擬dom:
function VNode(tag, data, children, text) {
return {
tag: tag,
data: data,
children: children,
text: text
}
}
class Vue {
constructor(options) {
this.$options = options
const vdom = this._update()
console.log(vdom)
}
_update() {
return this._render.call(this)
}
_render() {
const vnode = this.$options.render.call(this)
return vnode
}
__h__(tag, attr, children) {
return VNode(tag, attr, children.map((child)=>{
if(typeof child === 'string'){
return VNode(undefined, undefined, undefined, child)
}else{
return child
}
}))
}
__toString__(val) {
return val == null ? '' : typeof val === 'object' ? JSON.stringify(val, null, 2) : String(val);
}
}
var demo = new Vue({
el: '#demo',
data: {
text: "before",
},
render(){
return this.__h__('div', {}, [
this.__h__('span', {}, [this.__toString__(this.text)])
])
}
})
回頭看問題,也就是說,我需要知道render
函式中依賴了data
中的哪些屬性,只有這些屬性變化,才需要去觸發render
函式
第三步,依賴收集,準確渲染
思路:在這之前,我們已經把data
中的屬性改成響應式了,當去獲取或者修改這些變數時便能夠觸發相應函式。那這裡就可以利用這個相應的函式做些手腳了。當宣告一個vue物件時,在執行render
函式獲取虛擬dom的這個過程中,已經對render
中依賴的data
屬性進行了一次獲取操作,這次獲取操作便可以拿到所有依賴。
其實不僅是render
,任何一個變數的改別,是因為別的變數改變引起,都可以用上述方法,也就是computed
和watch
的原理
首先需要寫一個依賴收集的類,每一個data
中的屬性都有可能被依賴,因此每個屬性在響應式轉化(defineReactive
)的時候,就初始化它。程式碼如下:
class Dep {
constructor() {
this.subs = []
}
add(cb) {
this.subs.push(cb)
}
notify() {
console.log(this.subs)
this.subs.forEach((cb) => cb())
}
}
function defineReactive(obj, key, val, cb) {
const dep = new Dep()
Object.defineProperty(obj, key, {
// 省略
})
}
那麼執行過程就是:
- 當執行render
函式的時候,依賴到的變數的get
就會被執行,然後就把這個 render
函式加到subs
裡面去。
- 當set
的時候,就執行notify
,將所有的subs
數組裡的函式執行,其中就包含render
的執行。
注:程式碼中有一個
Dep.target
值,這個值時用來區分是普通的get
還是收集依賴時的get
最後完整程式碼如下:
function VNode(tag, data, children, text) {
return {
tag: tag,
data: data,
children: children,
text: text
}
}
class Vue {
constructor(options) {
this.$options = options
this._data = options.data
Object.keys(options.data).forEach(key => this._proxy(key))
observer(options.data)
const vdom = watch(this, this._render.bind(this), this._update.bind(this))
console.log(vdom)
}
_proxy(key) {
const self = this
Object.defineProperty(self, key, {
configurable: true,
enumerable: true,
get: function proxyGetter() {
return self._data[key]
},
set: function proxySetter(val) {
self._data[key] = val
}
})
}
_update() {
console.log("我需要更新");
const vdom = this._render.call(this)
console.log(vdom);
}
_render() {
return this.$options.render.call(this)
}
__h__(tag, attr, children) {
return VNode(tag, attr, children.map((child) => {
if (typeof child === 'string') {
return VNode(undefined, undefined, undefined, child)
} else {
return child
}
}))
}
__toString__(val) {
return val == null ? '' : typeof val === 'object' ? JSON.stringify(val, null, 2) : String(val);
}
}
function observer(obj) {
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === 'object') {
new observer(obj[key])
}
defineReactive(obj, key, obj[key])
})
}
function defineReactive(obj, key, val) {
const dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: () => {
if (Dep.target) {
dep.add(Dep.target)
Dep.target = null
}
console.log('你訪問了' + key)
return val
},
set: newVal => {
if (newVal === val)
return
console.log('你設定了' + key)
console.log('新的' + key + ' = ' + newVal)
val = newVal
dep.notify()
}
})
}
function watch(vm, exp, cb) {
Dep.target = cb
return exp()
}
class Dep {
constructor() {
this.subs = []
}
add(cb) {
this.subs.push(cb)
}
notify() {
this.subs.forEach((cb) => cb())
}
}
Dep.target = null
var demo = new Vue({
el: '#demo',
data: {
text: "before",
test: {
a: '1'
},
t: 1
},
render() {
return this.__h__('div', {}, [
this.__h__('span', {}, [this.__toString__(this.text)]),
this.__h__('span', {}, [this.__toString__(this.test.a)])
])
}
})
vue react響應式簡單對比
綜上發現,利用Object.defineProperty
這個特性可以精確的寫出訂閱釋出模式,從這點來說,vue
是優於react
的,在沒經過優化之前,vue
的渲染機制一定是比react
更加準確的,為了驗證這一說法,我用兩個框架同時寫了兩個相同的簡單專案進行對比。
沒有對比就沒有傷害:
通過對比發現,react
在正常使用的過程中產生了多餘的渲染,在移動端或者元件巢狀非常深的情況下會產生非常大的效能消耗,因此在使用react
的過程中,寫好react
生命週期中的shouldComponentUpdate
是非常重要的!
參考
相關推薦
vue2.x響應式原理,vue與react響應式簡單對比
配合ppt食用更佳ppt 實現的最終目標 const demo = new Vue({ data: { text: "before", }, // 對應的template 為 <div><span>{{text
當面試官問你Vue響應式原理,你可以這麼回答他
const Observer = function(data) { for (let key in data) { defineReactive(data, key); } } const defineReactive = function(obj, key) { const dep =
Vue2.x源碼學習筆記-Vue靜態方法和靜態屬性整理
temp next 技術 spa delet 結構 又是 靜態 https Vue靜態方法和靜態屬性,其實直接在瀏覽器中可以查看到的,如下 圈起來的是其靜態屬性,但是有的屬性對象中的屬性的值又是函數。未圈起來的則是函數。 其實它來自如下各個目錄下的js文件 // src
AD轉換原理,器件與引數
1 內容簡介 對AD晶片基本原理,分類與關鍵技術引數指標進行整理介紹。 2 模數轉換 2.1 轉換原理類別 AD轉換就是模數轉換。顧名思義,就是把模擬訊號轉換成數字訊號。主要包括積分型、逐次逼近型、並行比較型/串並行型、Σ-Δ調製型、壓頻變換型。A/D轉換器是用來通過一定的電路將模擬量轉變
http server原理,nginx與php之間是如何工作的
Nginx (“engine x”) 是一個高效能的 HTTP 和 反向代理 伺服器,也是一個 IMAP/POP3/SMTP 代理伺服器。 Nginx 是由 Igor Sysoev 為俄羅斯訪問量第二的 Rambler.ru 站點開發的,第一個公開版本0.1.0釋出於2004年10月4日。其將原始碼
Redis: 2、Redis高可用原理,搭建與驗證
Redis高可用原理,搭建與驗證 一、redis-ha原理 1 原理 redis高可用採用的是哨兵(sentinel),多個redis-slave配備了多個哨兵程序,哨兵監控redis-master,一旦出現故障,將一臺slave提升為master。客戶端通過連線哨
vue與react對比,相同之處與不同之處。
兩者都為當下主流框架 相同之處在於: 一、使用 Virtual DOM 二、提供了響應式 (Reactive) 和元件化 (Composable) 的檢視元件。 三、將注意力集中保持在核心庫,而將其他功能如路由和全域性狀態管理交給相關的庫。 不同之處: 一、在 Rea
uva 12093 Protecting Zonk(在某個節點X使用A裝置,此時與節點X相連的邊都被覆蓋)
題意: 有一個n(n<=10000)個節點的無根樹。有兩種裝置A,B,每種都有無限多個。 1.在某個節點X使用A裝置需要C1(C1<=1000)的花費,並且此時與節點X相連
vue2.X基礎知識八之vue-router路由
前端路由是直接找到與地址匹配的一個元件或物件並將其渲染出來。改變瀏覽器地址而不向伺服器發出請求有兩種做法,一是在地址中加入#以欺騙瀏覽器,地址的改變是由於正在進行頁內導航;二是使用HTML5的window.history功能,使用URL的Hash來模擬一個完整的URL。將
線段樹詳解 (原理,實現與應用)
比如,以左側的的藍色為例,若該節點是其父節點的右子節點,就證明它右側的那個紫色節點不會留下,會被其父替代,所以沒必要在這一步計算,若該節點是其父節點的左子節點,就證明它右側的那個紫色節點會留在這一層,所以必須在此刻計算,否則以後都不會再計算這個節點了。這樣逐層上去,容易發現,對於左側的藍色節點來說,只要它是左
淘寶、網易移動端 px 轉換 rem 原理,Vue-cli 實現 px 轉換rem
在過去的一段時間裡面一直在使用Vue配合 lib-flexible和px2rem-loader配合做移動端的網頁適配。秉著求知的思想,今天決定對他的原理進行分析。目前網上比較主流使用的就是淘寶方案和網易的解決方案,所以今天我就從這兩方面入手深度瞭解這兩個方案。本著網際網路分享的精神我會將我所理解的內容分
octavia的實現與分析(二)·原理,架構與基本流程
【瞭解】 其實說白了,Octavia就是將使用者的API請求經過邏輯處理,轉換成Haproxy或者Nginx的配置引數,下發到amphora虛機中。 Octavia的內部實現中,邏輯流程的處理主要使用TaskFlow庫。 【基本概念】 ·LBaas
C# 關鍵字explicit(顯示),implicit(隱式),類型的隱式和顯式轉換
tar oid bsp color col 必須 code 類型 顯示 class Program { static void Main(string[] args) { Adaptee ada = ne
Vue2實踐揭秘 - 書,讀後作了一個簡單摘要
邏輯 sel blog slides 執行者 emp 文件名 pla 最好 jd上買了本實踐相關的, 看過後,的確是實踐項目後的一些分享,有些網上的一些vue2教程沒怎麽提及 ----------- 看完了,有些啟發,作了個簡單摘要作記錄, 對vue2感興趣的,可以自己網
vue與react的區別
reac setter 表達式 react 輕量級框架 轉換 簡單 用戶 ets 相同點:都是基於組件化的輕量級框架,都專註於構建用戶界面的視圖層 vue,react都會構建一個虛擬的DOM並同步帶真是的DOM中 vue數據綁定表達式使用過的雙大括號語法,而指令是用於向
Day4 閉包、裝飾器decorator、叠代器與生成器、面向過程編程、三元表達式、列表解析與生成器表達式、序列化與反序列化
反序 bsp pic nbsp tor 序列 space 列表解析 列表 http://pic.cnhubei.com/space.php?uid=1774&do=album&id=1362489http://pic.cnhubei.com/space.ph
[轉]什麽是分布式系統,如何學習分布式系統
簡單 配置文件 延遲 去中心化 入門 應該 body base 技術分享 什麽是分布式系統 分布式系統挑戰 分布式系統特性與衡量標準 組件、理論、協議 用一個請求串起來 一個簡化的架構圖 概念與實現 總結 references
什麽是分布式系統,如何學習分布式系統
zook 並發 運營 app oop 錯誤 都是 衡量標準 god 目錄 什麽是分布式系統 分布式系統挑戰 分布式系統特性與衡量標準 組件、理論、協議 用一個請求串起來 一個簡化的架構圖 概念與實現 總結 references 正文 雖然本人在前面也
Vue與React的異同 -生命週期
vue的生命週期 建立前 beforeCreate 建立 create 掛載前 beforeMount 掛載 mounted 更新前 beforeUpdate 更新 updated 銷燬前 beforeDestroyed 銷燬 destoryed met