前言
所謂的前端響應(yīng)系統(tǒng),簡(jiǎn)單的理解可以認(rèn)為,瀏覽器根據(jù)特定變量顯示的數(shù)據(jù),在這個(gè)變量的值發(fā)生變化后,能夠自動(dòng)的刷新頁(yè)面從而展現(xiàn)出變化后的新值。這也是響應(yīng)前端框架實(shí)現(xiàn)雙向綁定的前提。
示例
如下示例:通過(guò)定時(shí)器在2秒后更改變量的值,從而實(shí)現(xiàn)界面在2秒后能夠自動(dòng)從“Hello world”變化為“你好,世界”
原理分析
提起響應(yīng)系統(tǒng),耳熟能詳?shù)木褪菙?shù)據(jù)劫持(數(shù)據(jù)代理),所謂的數(shù)據(jù)劫持就是指flash修改器老是無(wú)響應(yīng),在訪問(wèn)或者修改對(duì)象某個(gè)屬性時(shí),通過(guò)一段代碼攔截這個(gè)行為,并進(jìn)行相應(yīng)的操作返回結(jié)果。而前端響應(yīng)系統(tǒng),主要就是根據(jù)數(shù)據(jù)劫持這個(gè)原理實(shí)現(xiàn)的,具體的邏輯可以拆分為以下幾步
初始化讀取對(duì)象屬性值
let obj1 = { text: "Hello world" }
function effect() {
document.getElementById("toshow").innerHTML = obj1.text
}
劫持讀取行為添加相應(yīng)操作(將該讀取行為暫存起來(lái))
const buckset = new Set()
buckset.add(effect)
設(shè)置對(duì)象屬性的值
obj1.text = '你好 世界'
劫持設(shè)置行為添加相應(yīng)操作(設(shè)置完成后并調(diào)用之前暫存的讀取操作)
buckset.forEach(fn => fn())
上述步驟就是響應(yīng)系統(tǒng)最簡(jiǎn)易的基礎(chǔ)邏輯
代碼示例
在 以前主要是通過(guò).()方法修改對(duì)象屬性的get()方法和set()方法實(shí)現(xiàn)劫持讀取對(duì)象屬性值和設(shè)置屬性值的行為,進(jìn)而添加額外的邏輯實(shí)現(xiàn)主動(dòng)響應(yīng),這也是VUE2的實(shí)現(xiàn)方式。在 版本及以后新增了Proxy代理對(duì)象,可以在代理的對(duì)象種新增屬性的讀取和設(shè)置行為,從而實(shí)現(xiàn)主動(dòng)響應(yīng)flash修改器老是無(wú)響應(yīng),VUE3采取的正式這種方式。.()和Proxy的實(shí)現(xiàn)方式分別如下,也可以認(rèn)為下面兩個(gè)是兩個(gè)簡(jiǎn)易的響應(yīng)系統(tǒng):
數(shù)據(jù)劫持實(shí)現(xiàn)方式
在線代碼
<!DOCTYPE html>
<html>
<body>
<h3>響應(yīng)系統(tǒng)基礎(chǔ)原理</h3>
<br />
<span>
2秒后自動(dòng)更換:
<h4 id="toshow"></h4>
</span>
<script>
// 初始化數(shù)據(jù)
let obj1 = { text: 'Hello world' }
// 讀取操作
function effect() {
document.getElementById('toshow').innerHTML = data.text
}
// 創(chuàng)建“桶” 用于暫存讀取操作
const buckset = new Set()
// 劫持讀取和操作行為
function newObj(obj = {}) {
// 遍歷對(duì)象鍵, 根據(jù)鍵劫持對(duì)象get和set方法
const keys = Object.keys(obj)
keys.forEach(k => {
// 獲取初始值
const val = obj[k]
Object.defineProperty(obj, k, {
get: function() {
// 暫存讀取行為
buckset.add(effect)
// 讀取行為正常返回
return this.k ? this.k : val
},
set: function(value) {
// 設(shè)置行為正常設(shè)置
this.k = value
// 執(zhí)行讀取行為
buckset.forEach(fn => fn())
}
})
})
return obj
}
// 執(zhí)行劫持行為
let data = new newObj(obj1)
// 初始化讀取對(duì)象
effect()
// 修改對(duì)象值
setTimeout(() => {
data.text = '你好,世界'
}, 2000)
</script>
</body>
</html>
數(shù)據(jù)代理實(shí)現(xiàn)方式
在線代碼
<!DOCTYPE html>
<html>
<body>
<h3>響應(yīng)系統(tǒng)基礎(chǔ)原理</h3>
<br />
<span>
2秒后自動(dòng)更換:
<h4 id="toshow"></h4>
</span>
<script>
// 初始化數(shù)據(jù)
let obj = { text: 'Hello world' }
// 讀取操作
function effect() {
document.getElementById('toshow').innerHTML = data.text
}
// 創(chuàng)建“桶” 用于暫存讀取操作
const buckset = new Set()
// 代理讀取和操作行為
const data = new Proxy(obj, {
get(target, key) {
// 暫存讀取行為
buckset.add(effect)
// 讀取行為正常返回
return target[key]
},
set(target, key, newVal) {
// 設(shè)置行為正常設(shè)置
target[key] = newVal
// 執(zhí)行讀取行為
buckset.forEach(fn => fn())
return true
}
})
// 初始化讀取對(duì)象
effect()
// 修改對(duì)象值
setTimeout(() => {
data.text = '你好,世界'
}, 2000)
</script>
</body>
</html>
最后
當(dāng)然,這僅僅只是最基礎(chǔ)的響應(yīng)系統(tǒng)原理,對(duì)于前端框架來(lái)說(shuō),其功能還是遠(yuǎn)遠(yuǎn)不夠的,比如如何避免陷入無(wú)限遞歸、如何避免重復(fù)調(diào)用、如何實(shí)現(xiàn)嵌套響應(yīng)等等,要優(yōu)化的空間還是非常大。但是通過(guò)本文,我們至少有了一個(gè)清晰的印象,知道了所謂響應(yīng)系統(tǒng)的大概實(shí)現(xiàn)邏輯,不至于以后在面對(duì)響應(yīng)、數(shù)據(jù)劫持和數(shù)據(jù)代理時(shí)一頭霧水。
如果想對(duì)VUE的響應(yīng)系統(tǒng)有跟深的理解和學(xué)習(xí)推薦閱讀霍春陽(yáng)編寫的《Vue.js設(shè)計(jì)與實(shí)現(xiàn)》第四章響應(yīng)系統(tǒng)的作用與實(shí)現(xiàn)。
如果本文對(duì)你有幫助,請(qǐng)動(dòng)動(dòng)鼠標(biāo)點(diǎn)贊加收藏,謝謝!