前言
本文從一個(gè)簡(jiǎn)單的雙向綁定開始,逐步升級(jí)到由defineProperty和Proxy分別實(shí)現(xiàn)的響應(yīng)式系統(tǒng),注重入手思路,抓住關(guān)鍵細(xì)節(jié),希望能對(duì)你有所幫助。
一、極簡(jiǎn)雙向綁定
首先從最簡(jiǎn)單的雙向綁定入手:
// html<input type="text" id="input"><span id="span"></span>
// jslet input = document.getElementById('input')let span = document.getElementById('span')input.addEventListener('keyup', function(e) { span.innerHTML = e.target.value})以上似乎運(yùn)行起來也沒毛病,但我們要的是數(shù)據(jù)驅(qū)動(dòng),而不是直接操作dom:
// 操作obj數(shù)據(jù)來驅(qū)動(dòng)更新let obj = {}let input = document.getElementById('input')let span = document.getElementById('span')Object.defineProperty(obj, 'text', { configurable: true, enumerable: true, get() { console.log('獲取數(shù)據(jù)了') return obj.text }, set(newVal) { console.log('數(shù)據(jù)更新了') input.value = newVal span.innerHTML = newVal }})input.addEventListener('keyup', function(e) { obj.text = e.target.value})以上就是一個(gè)簡(jiǎn)單的雙向數(shù)據(jù)綁定,但顯然是不足的,下面繼續(xù)升級(jí)。
二、以defineProperty實(shí)現(xiàn)響應(yīng)系統(tǒng)
在Vue3版本來臨前以defineProperty實(shí)現(xiàn)的數(shù)據(jù)響應(yīng),基于發(fā)布訂閱模式,其主要包含三部分:Observer、Dep、Watcher。
1. 一個(gè)思路例子
// 需要劫持的數(shù)據(jù)let data = { a: 1, b: { c: 3 }}// 劫持?jǐn)?shù)據(jù)dataobserver(data)// 監(jiān)聽訂閱數(shù)據(jù)data的屬性new Watch('a', () => { alert(1)})new Watch('a', () => { alert(2)})new Watch('b.c', () => { alert(3)})以上就是一個(gè)簡(jiǎn)單的劫持和監(jiān)聽流程,那對(duì)應(yīng)的observer和Watch該如何實(shí)現(xiàn)?
2. Observer
observer的作用就是劫持?jǐn)?shù)據(jù),將數(shù)據(jù)屬性轉(zhuǎn)換為訪問器屬性,理一下實(shí)現(xiàn)思路:
①Observer需要將數(shù)據(jù)轉(zhuǎn)化為響應(yīng)式的,那它就應(yīng)該是一個(gè)函數(shù)(類),能接收參數(shù)。
②為了將數(shù)據(jù)變成響應(yīng)式,那需要使用Object.defineProperty。
③數(shù)據(jù)不止一種類型,這就需要遞歸遍歷來判斷。
// 定義一個(gè)類供傳入監(jiān)聽數(shù)據(jù)class Observer { constructor(data) { let keys = Object.keys(data) for (let i = 0; i < keys.length; i++) { defineReactive(data, keys[i], data[keys[i]]) } }}// 使用Object.definePropertyfunction defineReactive (data, key, val) { // 每次設(shè)置訪問器前都先驗(yàn)證值是否為對(duì)象,實(shí)現(xiàn)遞歸每個(gè)屬性 observer(val) // 劫持?jǐn)?shù)據(jù)屬性 Object.defineProperty(data, key, { configurable: true, enumerable: true, get () { return val }, set (newVal) { if (newVal === val) { return } else { data[key] = newVal // 新值也要劫持 observer(newVal) } } })}// 遞歸判斷function observer (data) { if (Object.prototype.toString.call(data) === '[object, Object]') { new Observer(data) } else { return }}// 監(jiān)聽objobserver(data)
新聞熱點(diǎn)
疑難解答
圖片精選