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