實(shí)現(xiàn)雙向數(shù)據(jù)綁定

MVVM框架主要包含3個部分:model褒翰、viewviewmodel盈包。

  1. Model:指的是數(shù)據(jù)部分,對應(yīng)到前端就是javascript對象
  2. View:指的是視圖部分旬牲,對應(yīng)前端就是dom
  3. Viewmodel:就是連接視圖與數(shù)據(jù)的中間件
1.雙向數(shù)據(jù)綁定的實(shí)現(xiàn)方式

簡單的來說仿粹,就是框架的控制器層(這里的控制器層是一個泛指,可以理解為控制view行為和聯(lián)系model層的中間件)和UI展示層(view層)建立一個雙向的數(shù)據(jù)通道原茅。當(dāng)這兩層中的任何一方發(fā)生變化時吭历,另一層將會自動作出相應(yīng)的變化。

vue-MVVM

一般來說要實(shí)現(xiàn)這種雙向數(shù)據(jù)綁定擂橘,在前端我目前了解的有三種形式:

  • 基于臟檢查 angular,regular(網(wǎng)易開發(fā))
  • 觀察機(jī)制
  • 封裝屬性訪問器

2.基于臟檢查

目前angular,regular的實(shí)現(xiàn)都是基于臟檢查晌区。當(dāng)發(fā)生某些特定的事情的時候,框架會調(diào)用相關(guān)的digest方法通贞。內(nèi)部邏輯就是遍歷所有的watcher,對監(jiān)控的屬性做對比朗若。如果值發(fā)生了變化,則執(zhí)行相應(yīng)的handler昌罩。

2.1基于regular詳細(xì)介紹一下:
watcher對象看起來是這樣的:
{
  get: function(context){...}  //獲得表達(dá)式當(dāng)前求值哭懈,此函數(shù)在解析時,已經(jīng)生成
  set: function(){} // 有些表達(dá)式可以生成set函數(shù)茎用,用于處理賦  值遣总,這個一般用于雙向綁定的場景
  once: false // 此監(jiān)聽器是否只生效一次
  last: undefined// 上一次表達(dá)式的求值結(jié)果
  fn: function(newvalue, oldvalue){} // 即你傳入$watch的第二個參數(shù),當(dāng)值改變時轨功,會調(diào)用此函數(shù)
  // ...
}

當(dāng)系統(tǒng)進(jìn)入臟檢查階段旭斥,遍歷所有的$watch綁定的watcher,然后對比watcher.get()watcher.last,如果不同則運(yùn)行對應(yīng)的watcher.fn(newvalue, oldvalue)。然后再進(jìn)入下一個watcher的檢查古涧。

何時進(jìn)行臟檢查垂券?

在Regular中,digest階段是由$update
方法觸發(fā)的羡滑。
具體源碼在regular/src/helper/watcher.js里面

由于regularjs是基于臟檢查菇爪,所以當(dāng)不是由regularjs本身控制的操作(如事件卒暂、指令)引起的數(shù)據(jù)操作,可能需要你手動的去同步data與view的數(shù)據(jù). $update方法即幫助將你的data同步到view層.

//手動同步
var component = new Regular()娄帖;
component.data.name = 'leeluolee'
// you need call $update to Synchronize data and view 
component.$update();  

//自動進(jìn)入
<div on-click={blog.title='Hello'}>{blog.title}</div>
regular-源碼片段,基于select的r-model指令

總結(jié):但是很顯然,臟檢查是低效的昙楚,它的效率基本上取決于你綁定的觀察者數(shù)量近速,在Regular中,你可以通過[@(Expression)

](https://regularjs.github.io/reference?syntax-zh#bind-once)元素來控制你的觀察者數(shù)量堪旧。

3.觀察者機(jī)制

使用ES7中的 Object.observe 方法對對象(或者其屬性)進(jìn)行監(jiān)控觀察削葱,一旦其發(fā)生變化時,將會執(zhí)行相應(yīng)的handler淳梦。這是目前監(jiān)控屬性數(shù)據(jù)變更最完美的一種方法析砸,語言(瀏覽器)原生支持,沒有什么比這個更好了爆袍。唯一的遺憾就是目前支持廣度還不行首繁,有待全面推廣。

4.封裝屬性訪問器(存取器get,set)

vue.js和avalon.js實(shí)現(xiàn)數(shù)據(jù)雙向綁定的原理就是屬性訪問器陨囊。
它使用了ES5中的定義標(biāo)準(zhǔn)屬性的Object.defineProperty 方法弦疮。

Object.defineProperty(obj, prop, descriptor)
//obj 待修改的對象
//prop 待修改的屬性名稱
//descriptor 待修改屬性的相關(guān)描述,要求傳入一個對象蜘醋。
//@{param} descriptor
1.configurable 胁塞,屬性是否可配置⊙褂铮可配置的含義包括:是否可以刪除屬性( delete )学辱,是否可以修改屬性的 writable 污桦、     enumerable 、 configurable 屬性。
2.enumerable 聪建,屬性是否可枚舉〗跸可枚舉的含義包括:是否可以通過 for...in 遍歷到参萄,是否可以通過 Object.keys() 方法獲取屬性名稱。
3.writable 酣倾,屬性是否可重寫舵揭。可重寫的含義包括:是否可以對屬性進(jìn)行重新賦值躁锡。
4.value 午绳,屬性的默認(rèn)值。
5.set 映之,屬性的重寫器拦焚。一旦屬性被重新賦值蜡坊,此方法被自動調(diào)用。
6.get 赎败,屬性的讀取器秕衙。一旦屬性被訪問讀取,此方法被自動調(diào)用僵刮。

Object.defineProperty使用示例:

var o = {};
Object.defineProperty(o, 'name', { value: 'erik'});
console.log(Object.getOwnPropertyDescriptor(o, 'name'));
   // {
        value: "erik", 
        writable: false, 
        enumerable:false,
        configurable: false
     }

Object.defineProperty(o, 'age', {
    value: 23,
    configurable: true,
    writable: true
});
console.log(o.age); // 23
o.age = 18;
console.log(o.age); // 18. 因為age屬性是可重寫的
console.log(Object.keys(o)); // []. name和age屬性都不是可枚舉的
Object.defineProperty(o, 'sex', {
    value: '女',
    writable: false
});
o.sex = '男'; // 這里的賦值其實(shí)是不起作用的
console.log(o.sex); // '女';
delete o.sex; // false, 屬性刪除的動作也是無效的

注意:

  • Object.defineProperty() 方法設(shè)置屬性時据忘,屬性不能同時聲明訪問器屬性( set 和 get )和 writable 或者 value 屬性。 意思就是搞糕,某個屬性設(shè)置了 writable 或者 value 屬性勇吊,那么這個屬性就不能聲明 get 和 set 了,反之亦然窍仰。
  • 因為 Object.defineProperty() 在聲明一個屬性時汉规,不允許同一個屬性出現(xiàn)兩種以上存取訪問控制。
4.1基于vue使用封裝屬性訪問器

首先驹吮,vuejs在實(shí)例化的過程中针史,會對遍歷傳給實(shí)例化對象選項中的data 選項,遍歷其所有屬性并使用 Object.defineProperty 把這些屬性全部轉(zhuǎn)為 getter/setter钥屈。

同時每一個實(shí)例對象都有一個watcher實(shí)例對象悟民,他會在模板編譯的過程中,用getter去訪問data的屬性,watcher此時就會把用到的data屬性記為依賴篷就,這樣就建立了視圖與數(shù)據(jù)之間的聯(lián)系射亏。當(dāng)之后我們渲染視圖的數(shù)據(jù)依賴發(fā)生改變(即數(shù)據(jù)的setter被調(diào)用)的時候,watcher會對比前后兩個的數(shù)值是否發(fā)生變化竭业,然后確定是否通知視圖進(jìn)行重新渲染智润。這樣就實(shí)現(xiàn)了所謂的雙向數(shù)據(jù)綁定。

defineProperty
效果
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末窟绷,一起剝皮案震驚了整個濱河市兼蜈,隨后出現(xiàn)的幾起案子遗契,更是在濱河造成了極大的恐慌漾根,老刑警劉巖逼蒙,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赁还,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門翩伪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凛剥,“玉大人犁享,你說我怎么就攤上這事。” “怎么了窑眯?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵屏积,是天一觀的道長。 經(jīng)常有香客問我磅甩,道長炊林,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任卷要,我火速辦了婚禮渣聚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘僧叉。我一直安慰自己奕枝,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布瓶堕。 她就那樣靜靜地躺著隘道,像睡著了一般。 火紅的嫁衣襯著肌膚如雪郎笆。 梳的紋絲不亂的頭發(fā)上谭梗,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機(jī)與錄音宛蚓,去河邊找鬼激捏。 笑死,一個胖子當(dāng)著我的面吹牛凄吏,可吹牛的內(nèi)容都是我干的远舅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼痕钢,長吁一口氣:“原來是場噩夢啊……” “哼表谊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起盖喷,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤爆办,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后课梳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體距辆,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年暮刃,在試婚紗的時候發(fā)現(xiàn)自己被綠了跨算。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡椭懊,死狀恐怖诸蚕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤背犯,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布坏瘩,位于F島的核電站,受9級特大地震影響漠魏,放射性物質(zhì)發(fā)生泄漏倔矾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一柱锹、第九天 我趴在偏房一處隱蔽的房頂上張望哪自。 院中可真熱鬧,春花似錦禁熏、人聲如沸壤巷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽隙笆。三九已至,卻和暖如春升筏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瘸爽。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工您访, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人剪决。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓灵汪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親柑潦。 傳聞我的和親對象是個殘疾皇子享言,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354

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