vue動態(tài)綁定原理

參考網(wǎng)址:
1.http://www.reibang.com/p/f194619f6f26?from=singlemessage&isappinstalled=0
2.http://www.cnblogs.com/kidney/p/6052935.html

四大步驟

==1.監(jiān)聽器Observer==:用來劫持監(jiān)聽所有的屬性趣惠,主要運用Object.Proprety屬性來監(jiān)聽數(shù)據(jù)错维。如果有改變時意推,就通知訂閱器列肢,由訂閱器來通知所有的訂閱者迹恐。

==2.訂閱器Dep==: 用來管理所有的訂閱者逗载,主要是一個數(shù)組,保存所有的訂閱者

==3.訂閱者Watcher==: 每一個watcher都有一個更新函數(shù)Update农曲,當接收到訂閱器的通知改變時社搅,就會執(zhí)行update函數(shù),從而更新視圖

==4.解析器Compile==: 掃描和解析每個節(jié)點的相關指令(v-model, v-on等v-指令)乳规,如果存在v-model形葬,v-on等指令,解析器compile就會初始化這些模板數(shù)據(jù)暮的,使之可以頁面首次打開時把vue實例的數(shù)據(jù)顯示在視圖上笙以,然后初始化相應的訂閱者Wather

完整流程

下面代碼的實現(xiàn)流程:
可以從這個Vue函數(shù)開始入手

        function Vue(options){
            console.log(this); //this是指向實例對象
            this.data = options.data;
            var data = this.data;
            observe(data,this);  //第一步
            var id = options.el;
            var dom = nodeToFragment(document.getElementById(id),this); //第二部
            document.getElementById(id).appendChild(dom);
        }
        var vm = new Vue({
            el: 'app',
            data:{
                text:'hello world'
            }
        })

第一步:創(chuàng)建一個observe去監(jiān)聽vue的實例vm中data的所有屬性

第二步:調用nodeToFragment()
①==劫持==id=‘app’的元素下的所有子節(jié)點。(后面對于DOM的操作都在這里進行操作冻辩,最后更新完成再更新到我們真正的DOM樹上源织,避免對DOM的反復操作)
②在這里調用解析器compile

第三步:compile()
①遍歷dom中節(jié)點是否有v指令屬性,有的話就對視圖進行==初始化==(如:把上面的text:‘hello world’初始化到<input type="text" v-model="text">value中)
②為所有有v指令的節(jié)點==創(chuàng)建一個watcher==微猖,并把對應的動態(tài)綁定屬性值傳過去
compile中與Observer的聯(lián)系:
③在初始化的過程中谈息,就必定會調用到我們observe的get方法,因此==在Observe這里把watcher添加到訂閱器Dep中==
④在compile中我們還會設置對事件的監(jiān)聽(如input事件),執(zhí)行了對視圖中的數(shù)據(jù)修改->反映到model數(shù)據(jù)中(調用observer的set方法)

第四步:Observer通知訂閱器Dep凛剥,數(shù)據(jù)值已修改侠仇。Dep.notify()通知所有的watcher去更新視圖

動態(tài)綁定測試代碼

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
     <div id="app">
        <input type="text" v-model="text">
        {{ text }}
    </div>

    <script type="text/javascript">


        //監(jiān)視者
        /**
         * [observe description]
         * @param  {[type]} obj [vue的data]
         * @param  {[type]} vm  [vue實例]
         * @return {[type]}     [description]
         */
        function observe(obj,vm){
             Object.keys(obj).forEach(function (key) {
                defineReactive(vm, key, obj[key]);
             })
        };

        /**
         * [defineReactive description]
         * @param  {[type]} obj [vue實例]
         * @param  {[type]} key [屬性名]
         * @param  {[type]} val [屬性值]
         * @return {[type]}     [description]
         */
        function defineReactive(obj, key, val){
            console.log('observe:defineReactive');
            var dep = new Dep();
            console.log(dep);
            //obj:vue實例vm(被操作的目標對象),key要定義or要修改的屬性名
            Object.defineProperty(obj,key,{
                get : function (){
                    console.log('gettttttttt!');
                    console.log(Dep.target);
                    if(Dep.target)
                        dep.addSub(Dep.target)
                    return val;
                },
                set : function (newVal){
                    console.log('set!!!!!!!!!!!!!!!!');
                    if(newVal === val) return;
                    val = newVal;
                    console.log(val);
                    dep.notify();
                }
            });
        };

        //解析器
        //node:劫持的dom犁珠, vm:vue實例
        function compile (node, vm) {
            console.log('compile-----start');
            var reg = /\{\{(.*)\}\}/;
            if(node.nodeType === 1)  //元素逻炊,查找v-modal綁定的屬性值
            {
                var attr = node.attributes;
                for(var i=0;i<attr.length;i++){
                    if(attr[i].nodeName == 'v-model'){
                        var name = attr[i].nodeValue; //v-model的屬性名
                        node.addEventListener('input',function(e){
                            console.log('檢測到值改變:'+e.target.value);
                            vm[name] = e.target.value; //給相應的 data 屬性賦值,進而觸發(fā)該屬性的 set 方法
                        });
                        node.value = vm[name];  //把vue對應屬性名的值賦給節(jié)點,觸發(fā)get方法,vm.data[name]不會觸發(fā)get方法犁享,但是vm[name]會觸發(fā)get方法S嗨亍!
                        node.removeAttribute('v-model');  //為什么要刪除v-model炊昆?
                    }
                }
                new Watcher(vm, node, name, 'input'); //通知watcher去修改桨吊?
            }
            if(node.nodeType === 3){
                 if (reg.test(node.nodeValue)) {
                  var name = RegExp.$1; // 獲取匹配到的字符串
                  name = name.trim();

                  new Watcher(vm, node, name, 'text');
                }
            }
            console.log('compile-----end');
        }

        //訂閱器
        function Dep (){
            this.subs = [];
            console.log('dep');
        }

        Dep.prototype =  {
            addSub: function(sub){
                console.log('addToDep')
                this.subs.push(sub);
            },
            notify: function(){
                this.subs.forEach(function(sub){
                    sub.update();  //通知watch去update
                });
            }

        }
        
        
        //var vdom = nodeToFragment(document.getElementById('app'));
        //console.log(vdom);

        function nodeToFragment(node,vm){
            console.log('nodeToFragment---start');
            var flag = document.createDocumentFragment();
            var child;
            //console.log(node.childNodes); // length : 5
            while (child = node.firstChild){
                compile(child,vm); //把劫持的child給compile解析
                flag.appendChild(child);
            }
            //console.log(node.childNodes);  // length : 0 
            console.log('nodeToFragment------end')
            return flag;
        }


    /**
     * [Watcher 訂閱者]
     * @param {[type]} vm       [vue實例對象]
     * @param {[type]} node     [含有v指令的節(jié)點]
     * @param {[type]} name     [description]
     * @param {[type]} nodeType [description]
     */
    function Watcher (vm, node, name, nodeType) {
      console.log('watcher');
      console.log(this);
      //Dep target是Dep和watcher關聯(lián)的唯一橋梁
      Dep.target = this;
      /**
       * 關于Dep.target: 是一個全局變量威根,保存了當前的watcher
       * wathcer構造函數(shù)中,為Dep.target賦值后會通過watcher.update()方法去調用observe.get()
       * 在observer.get()方法中就是根據(jù)Dep.target來把當前watcher加入到訂閱器Dep中
       * 所以在把wathcer加入完Dep后,Dep.target就沒有用了视乐,所以在this.update()后面要把Dep.watcher設為null
       */
      this.name = name;
      this.node = node;
      this.vm = vm;
      this.nodeType = nodeType;
      this.update();
      Dep.target = null;
    }

    Watcher.prototype = {
      //更新視圖上的值洛搀,{{ value }}
      update: function () {
        this.get();
        if (this.nodeType == 'text') {
          this.node.nodeValue = this.value;
        }
        if (this.nodeType == 'input') {
          this.node.value = this.value;
        }
      },
      // 獲取 data 中的屬性值
      get: function () {
        this.value = this.vm[this.name]; // 觸發(fā)相應屬性的 get
      }
    }

        function Vue(options){
            console.log(this); //this是指向實例對象
            this.data = options.data;
            var data = this.data;
            observe(data,this);
            var id = options.el;
            var dom = nodeToFragment(document.getElementById(id),this);
            document.getElementById(id).appendChild(dom);
        }

        var vm = new Vue({
            el: 'app',
            data:{
                text:'hello world'
            }
        })
    </script>
</body>
</html>
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市佑淀,隨后出現(xiàn)的幾起案子留美,更是在濱河造成了極大的恐慌,老刑警劉巖伸刃,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谎砾,死亡現(xiàn)場離奇詭異,居然都是意外死亡捧颅,警方通過查閱死者的電腦和手機景图,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隘道,“玉大人症歇,你說我怎么就攤上這事郎笆√饭#” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵宛蚓,是天一觀的道長激捏。 經(jīng)常有香客問我,道長凄吏,這世上最難降的妖魔是什么远舅? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮痕钢,結果婚禮上图柏,老公的妹妹穿的比我還像新娘。我一直安慰自己任连,他們只是感情好蚤吹,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著随抠,像睡著了一般裁着。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拱她,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天二驰,我揣著相機與錄音,去河邊找鬼秉沼。 笑死桶雀,一個胖子當著我的面吹牛矿酵,可吹牛的內容都是我干的。 我是一名探鬼主播背犯,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼坏瘩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了漠魏?” 一聲冷哼從身側響起倔矾,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎柱锹,沒想到半個月后哪自,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡禁熏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年壤巷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞧毙。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡胧华,死狀恐怖,靈堂內的尸體忽然破棺而出宙彪,到底是詐尸還是另有隱情矩动,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布释漆,位于F島的核電站悲没,受9級特大地震影響,放射性物質發(fā)生泄漏男图。R本人自食惡果不足惜示姿,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望逊笆。 院中可真熱鬧栈戳,春花似錦、人聲如沸难裆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽差牛。三九已至命锄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間偏化,已是汗流浹背脐恩。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留侦讨,地道東北人驶冒。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓苟翻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親骗污。 傳聞我的和親對象是個殘疾皇子崇猫,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內容

  • vue理解淺談 一 理解vue的核心理念 使用vue會讓人感到身心愉悅,它同時具備angular和react的優(yōu)點...
    ambeer閱讀 24,133評論 2 18
  • 我的github: vue雙向綁定原理 MVC模式 以往的MVC模式是單向綁定,即Model綁定到View需忿,當我們...
    KlausXu閱讀 44,906評論 7 91
  • 再次看了上次寫的博客關于Vue的MVVM诅炉,發(fā)現(xiàn)雖然介紹了MVVM的原理,但是感覺還不夠詳細屋厘,現(xiàn)在就再次根據(jù)這篇博客...
    大春春閱讀 1,935評論 0 5
  • “推磨涕烧,搖磨/推粑粑,請噶噶(外婆)/推豆腐汗洒,請舅母/舅母不吃菜豆腐/要吃隔壁的老雞母/燉又燉不耙(軟)议纯,煮又煮不...
    渝夫2016閱讀 1,174評論 0 2
  • 什么是喪文化? 舉幾個例子感受一下溢谤。 2016年的夏天瞻凤,以葛優(yōu)躺為先鋒的一場比廢大戰(zhàn)拉開了序幕。在“葛優(yōu)癱躺”的帶...
    愛學習的小王子閱讀 3,868評論 0 4