雙向數(shù)據(jù)綁定是什么

![](https://static.vue-js.com/cef7dcc0-3ac9-11eb-85f6-6fac77c0c9b3.png)

## 一饺蚊、什么是雙向綁定

我們先從單向綁定切入單向綁定非常簡(jiǎn)單砾淌,就是把`Model`綁定到`View`,當(dāng)我們用`JavaScript`代碼更新`Model`時(shí)顾稀,`View`就會(huì)自動(dòng)更新雙向綁定就很容易聯(lián)想到了,在單向綁定的基礎(chǔ)上坝撑,用戶更新了`View`静秆,`Model`的數(shù)據(jù)也自動(dòng)被更新了,這種情況就是雙向綁定舉個(gè)栗子

?![](https://static.vue-js.com/d65738d0-3ac9-11eb-ab90-d9ae814b240d.png)

當(dāng)用戶填寫表單時(shí)巡李,`View`的狀態(tài)就被更新了抚笔,如果此時(shí)可以自動(dòng)更新`Model`的狀態(tài),那就相當(dāng)于我們把`Model`和`View`做了雙向綁定關(guān)系圖如下

?![](https://static.vue-js.com/dcc1d4a0-3ac9-11eb-ab90-d9ae814b240d.png)

## 二侨拦、雙向綁定的原理是什么

我們都知道 `Vue` 是數(shù)據(jù)雙向綁定的框架殊橙,雙向綁定由三個(gè)重要部分構(gòu)成

- 數(shù)據(jù)層(Model):應(yīng)用的數(shù)據(jù)及業(yè)務(wù)邏輯

- 視圖層(View):應(yīng)用的展示效果,各類UI組件

- 業(yè)務(wù)邏輯層(ViewModel):框架封裝的核心狱从,它負(fù)責(zé)將數(shù)據(jù)與視圖關(guān)聯(lián)起來

而上面的這個(gè)分層的架構(gòu)方案膨蛮,可以用一個(gè)專業(yè)術(shù)語(yǔ)進(jìn)行稱呼:`MVVM`這里的控制層的核心功能便是 “數(shù)據(jù)雙向綁定” 。自然季研,我們只需弄懂它是什么敞葛,便可以進(jìn)一步了解數(shù)據(jù)綁定的原理

### 理解ViewModel

它的主要職責(zé)就是:

- 數(shù)據(jù)變化后更新視圖

- 視圖變化后更新數(shù)據(jù)

當(dāng)然,它還有兩個(gè)主要部分組成

- 監(jiān)聽器(Observer):對(duì)所有數(shù)據(jù)的屬性進(jìn)行監(jiān)聽

- 解析器(Compiler):對(duì)每個(gè)元素節(jié)點(diǎn)的指令進(jìn)行掃描跟解析,根據(jù)指令模板替換數(shù)據(jù),以及綁定相應(yīng)的更新函數(shù)

### 三训貌、實(shí)現(xiàn)雙向綁定

我們還是以`Vue`為例制肮,先來看看`Vue`中的雙向綁定流程是什么的

1. ?`new Vue()`首先執(zhí)行初始化冒窍,對(duì)`data`執(zhí)行響應(yīng)化處理,這個(gè)過程發(fā)生`Observe`中

2. ?同時(shí)對(duì)模板執(zhí)行編譯豺鼻,找到其中動(dòng)態(tài)綁定的數(shù)據(jù)综液,從`data`中獲取并初始化視圖,這個(gè)過程發(fā)生在`Compile`中

3. ?同時(shí)定義?個(gè)更新函數(shù)和`Watcher`儒飒,將來對(duì)應(yīng)數(shù)據(jù)變化時(shí)`Watcher`會(huì)調(diào)用更新函數(shù)

4. ?由于`data`的某個(gè)`key`在?個(gè)視圖中可能出現(xiàn)多次谬莹,所以每個(gè)`key`都需要?個(gè)管家`Dep`來管理多個(gè)`Watcher`

5. ?將來data中數(shù)據(jù)?旦發(fā)生變化,會(huì)首先找到對(duì)應(yīng)的`Dep`桩了,通知所有`Watcher`執(zhí)行更新函數(shù)

流程圖如下:

?![](https://static.vue-js.com/e5369850-3ac9-11eb-85f6-6fac77c0c9b3.png)

### 實(shí)現(xiàn)

先來一個(gè)構(gòu)造函數(shù):執(zhí)行初始化附帽,對(duì)`data`執(zhí)行響應(yīng)化處理

```js

class?Vue?{ ?

??constructor(options)?{ ?

????this.$options?=?options; ?

????this.$data?=?options.data; ?


????//?對(duì)data選項(xiàng)做響應(yīng)式處理 ?

????observe(this.$data); ?


????//?代理data到vm上 ?

????proxy(this); ?


????//?執(zhí)行編譯 ?

????new?Compile(options.el,?this); ?

??} ?

} ?

```

對(duì)`data`選項(xiàng)執(zhí)行響應(yīng)化具體操作

```js

function?observe(obj)?{ ?

??if?(typeof?obj?!==?"object"?||?obj?==?null)?{ ?

????return; ?

??} ?

??new?Observer(obj); ?

} ?


class?Observer?{ ?

??constructor(value)?{ ?

????this.value?=?value; ?

????this.walk(value); ?

??} ?

??walk(obj)?{ ?

????Object.keys(obj).forEach((key)?=>?{ ?

??????defineReactive(obj,?key,?obj[key]); ?

????}); ?

??} ?

} ?

```

#### 編譯`Compile`

對(duì)每個(gè)元素節(jié)點(diǎn)的指令進(jìn)行掃描跟解析,根據(jù)指令模板替換數(shù)據(jù),以及綁定相應(yīng)的更新函數(shù)

?![](https://static.vue-js.com/f27e19c0-3ac9-11eb-85f6-6fac77c0c9b3.png)

```js

class?Compile?{ ?

??constructor(el,?vm)?{ ?

????this.$vm?=?vm; ?

????this.$el?=?document.querySelector(el);??//?獲取dom ?

????if?(this.$el)?{ ?

??????this.compile(this.$el); ?

????} ?

??} ?

??compile(el)?{ ?

????const?childNodes?=?el.childNodes;? ?

????Array.from(childNodes).forEach((node)?=>?{?//?遍歷子元素 ?

??????if?(this.isElement(node))?{???//?判斷是否為節(jié)點(diǎn) ?

????????console.log("編譯元素"?+?node.nodeName); ?

??????}?else?if?(this.isInterpolation(node))?{ ?

????????console.log("編譯插值?本"?+?node.textContent);??//?判斷是否為插值文本?{{}} ?

??????} ?

??????if?(node.childNodes?&&?node.childNodes.length?>?0)?{??//?判斷是否有子元素 ?

????????this.compile(node);??//?對(duì)子元素進(jìn)行遞歸遍歷 ?

??????} ?

????}); ?

??} ?

??isElement(node)?{ ?

????return?node.nodeType?==?1; ?

??} ?

??isInterpolation(node)?{ ?

????return?node.nodeType?==?3?&&?/\{\{(.*)\}\}/.test(node.textContent); ?

??} ?

} ?


```

#### 依賴收集

視圖中會(huì)用到`data`中某`key`,這稱為依賴井誉。同?個(gè)`key`可能出現(xiàn)多次蕉扮,每次都需要收集出來用?個(gè)`Watcher`來維護(hù)它們,此過程稱為依賴收集多個(gè)`Watcher`需要?個(gè)`Dep`來管理颗圣,需要更新時(shí)由`Dep`統(tǒng)?通知

?![](https://static.vue-js.com/fa191f40-3ac9-11eb-ab90-d9ae814b240d.png)

實(shí)現(xiàn)思路

?1. `defineReactive`時(shí)為每?個(gè)`key`創(chuàng)建?個(gè)`Dep`實(shí)例

?2. 初始化視圖時(shí)讀取某個(gè)`key`喳钟,例如`name1`,創(chuàng)建?個(gè)`watcher1`

?3. 由于觸發(fā)`name1`的`getter`方法在岂,便將`watcher1`添加到`name1`對(duì)應(yīng)的Dep中

?4. 當(dāng)`name1`更新奔则,`setter`觸發(fā)時(shí),便可通過對(duì)應(yīng)`Dep`通知其管理所有`Watcher`更新

```js

//?負(fù)責(zé)更新視圖 ?

class?Watcher?{ ?

??constructor(vm,?key,?updater)?{ ?

????this.vm?=?vm ?

????this.key?=?key ?

????this.updaterFn?=?updater ?


????//?創(chuàng)建實(shí)例時(shí)蔽午,把當(dāng)前實(shí)例指定到Dep.target靜態(tài)屬性上 ?

????Dep.target?=?this ?

????//?讀一下key易茬,觸發(fā)get ?

????vm[key] ?

????//?置空 ?

????Dep.target?=?null ?

??} ?


??//?未來執(zhí)行dom更新函數(shù),由dep調(diào)用的 ?

??update()?{ ?

????this.updaterFn.call(this.vm,?this.vm[this.key]) ?

??} ?

} ?

```

聲明`Dep`

```js

class?Dep?{ ?

??constructor()?{ ?

????this.deps?=?[];??//?依賴管理 ?

??} ?

??addDep(dep)?{ ?

????this.deps.push(dep); ?

??} ?

??notify()?{? ?

????this.deps.forEach((dep)?=>?dep.update()); ?

??} ?

} ?

```

創(chuàng)建`watcher`時(shí)觸發(fā)`getter`

```js

class?Watcher?{ ?

??constructor(vm,?key,?updateFn)?{ ?

????Dep.target?=?this; ?

????this.vm[this.key]; ?

????Dep.target?=?null; ?

??} ?

} ?


```

依賴收集及老,創(chuàng)建`Dep`實(shí)例

```js

function?defineReactive(obj,?key,?val)?{ ?

??this.observe(val); ?

??const?dep?=?new?Dep(); ?

??Object.defineProperty(obj,?key,?{ ?

????get()?{ ?

??????Dep.target?&&?dep.addDep(Dep.target);//?Dep.target也就是Watcher實(shí)例 ?

??????return?val; ?

????}, ?

????set(newVal)?{ ?

??????if?(newVal?===?val)?return; ?

??????dep.notify();?//?通知dep執(zhí)行更新方法 ?

????}, ?

??}); ?

} ?

```

## 參考文獻(xiàn)

- https://www.liaoxuefeng.com/wiki/1022910821149312/1109527162256416

- https://juejin.cn/post/6844903942254510087#heading-9


面試官VUE系列總進(jìn)度:3/33

[面試官:說說你對(duì)vue的理解\?](http://mp.weixin.qq.com/s?__biz=MzU1OTgxNDQ1Nw==&mid=2247484101&idx=1&sn=83b0983f0fca7d7c556e4cb0bff8c9b8&chksm=fc10c093cb674985ef3bd2966f66fc28c5eb70b0037e4be1af4bf54fb6fa9571985abd31d52f&scene=21#wechat_redirect) ?

[面試官:說說你對(duì)SPA(單頁(yè)應(yīng)用)的理解\?](http://mp.weixin.qq.com/s?__biz=MzU1OTgxNDQ1Nw==&mid=2247484119&idx=1&sn=d171b28a00d42549d279498944a98519&chksm=fc10c081cb6749976814aaeda6a6433db418223cec57edda7e15b9e5a0ca69ad549655639c61&scene=21#wechat_redirect)

![](https://static.vue-js.com/821b87b0-3ac6-11eb-ab90-d9ae814b240d.png)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末抽莱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子写半,更是在濱河造成了極大的恐慌岸蜗,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叠蝇,死亡現(xiàn)場(chǎng)離奇詭異璃岳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)铃慷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蹋盆,“玉大人栖雾,你說我怎么就攤上這事噪径。” “怎么了数初?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)梗顺。 經(jīng)常有香客問我泡孩,道長(zhǎng),這世上最難降的妖魔是什么寺谤? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任仑鸥,我火速辦了婚禮,結(jié)果婚禮上变屁,老公的妹妹穿的比我還像新娘眼俊。我一直安慰自己,他們只是感情好粟关,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布疮胖。 她就那樣靜靜地躺著,像睡著了一般闷板。 火紅的嫁衣襯著肌膚如雪澎灸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天遮晚,我揣著相機(jī)與錄音性昭,去河邊找鬼。 笑死县遣,一個(gè)胖子當(dāng)著我的面吹牛糜颠,可吹牛的內(nèi)容都是我干的汹族。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼其兴,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼顶瞒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起忌警,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤搁拙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后法绵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體箕速,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年朋譬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了盐茎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡徙赢,死狀恐怖字柠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情狡赐,我是刑警寧澤窑业,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站枕屉,受9級(jí)特大地震影響常柄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜搀擂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一西潘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧哨颂,春花似錦喷市、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至沃测,卻和暖如春缭黔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蒂破。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工馏谨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人附迷。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓惧互,卻偏偏與公主長(zhǎng)得像哎媚,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子喊儡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 一拨与、概述 JavaScript 語(yǔ)言采用的是單線程模型,也就是說艾猜,所有任務(wù)只能在一個(gè)線程上完成买喧,一次只能做一件事。...
    零星小雨_c84a閱讀 2,441評(píng)論 0 2
  • 作者:阮一峰www.ruanyifeng.com/blog/2018/07/web-worker.html 概述 ...
    grain先森閱讀 1,075評(píng)論 0 1
  • 簡(jiǎn)介 web worker 是運(yùn)行在后臺(tái)的 JavaScript匆赃,不會(huì)影響頁(yè)面的性能淤毛。例如處理類似 高斯函數(shù)處理圖...
    劉翾閱讀 703評(píng)論 0 0
  • 一、概述 JavaScript 語(yǔ)言采用的是單線程模型算柳,也就是說低淡,所有任務(wù)只能在一個(gè)線程上完成,一次只能做一件事瞬项。...
    你瞅瞅你寫的bug閱讀 503評(píng)論 0 1
  • Web Worker 概述 使用場(chǎng)景 js采用的是單線程模型蔗蹋,所有任務(wù)只能在一個(gè)線程上完成,但是計(jì)算機(jī)目前大部分都...
    小小了墨閱讀 742評(píng)論 0 0