1. 程式人生 > >vue 雙向資料繫結原理

vue 雙向資料繫結原理

Vue的雙向資料繫結原理是什麼?

vue.js是採用資料劫持結合釋出者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的setter, getter,在資料變動時釋出訊息給訂閱者,出發相應的監聽回撥。
具體步驟:
首先Vue會使用documentfragment劫持根元素裡包含的所有節點,這些節點不僅包括標籤元素,還包括文字,甚至換行的回車。
然後Vue會把data中所有的資料,用defindProperty()變成Vue的訪問器屬性,這樣每次修改這些資料的時候,就會觸發相應屬性的get,set方法。

接下來編譯處理劫持到的dom節點,遍歷所有節點,根據nodeType來判斷節點型別,根據節點本身的屬性(是否有v-model等屬性)或者文字節點的內容(是否符合{{文字插值}}的格式)來判斷節點是否需要編譯。對v-model,繫結事件當輸入的時候,改變Vue中的資料。對文字節點,將他作為一個觀察者watcher放入觀察者列表,當Vue資料改變的時候,會有一個主題物件,對列表中的觀察者們釋出改變的訊息,觀察者們再更新自己,改變節點中的顯示,從而達到雙向繫結的目的。


demo

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>雙向繫結demo</title>
<style>
    body{margin: 200px auto;width: 800px;}
</style>
</head>
<body>
    <div id="app">
        <input type="text" id="input" v-model="text">
        {{ text }}
    </div>



<script>
    // Vue建構函式
    function Vue(options){
        this.data = options.data;
        //釋出訂閱模式,資料繫結
        var data = this.data;
        observe(data,this);

        var id = options.el;
        var dom = nodeToFragment(document.getElementById(id),this);
        //編譯完成後返回到app中
        document.getElementById("app").appendChild(dom);
    }

    //遍歷傳入例項的data物件的屬性,將其設定為Vue物件的訪問器屬性
    function observe(obj,vm){
        Object.keys(obj).forEach(function(key){
            defineReactive(vm,key,obj[key]);
        });
    }
    //訪問器屬性是物件中的一種特殊屬性,它不能直接在物件中設定,而必須通過defineProperty()方法單獨定義。
    //訪問器屬性的"值"比較特殊,讀取或設定訪問器屬性的值,實際上是呼叫其內部特性:get和set函式。
    function defineReactive(obj,key,val){
        //這裡用到了觀察者(訂閱/釋出)模式,它定義了一種一對多的關係,讓多個觀察者監聽一個主題物件,這個主題物件的狀態發生改變時會通知所有觀察者物件,觀察者物件就可以更新自己的狀態。


        //例項化一個主題物件,物件中有空的觀察者列表
        var dep = new Dep();
        //將data的每一個屬性都設定為Vue物件的訪問器屬性,屬性名和data中相同
        //所以每次修改Vue.data的時候,都會呼叫下邊的get和set方法。然後會監聽v-model的input事件,當改變了input的值,就相應的改變Vue.data的資料,然後觸發這裡的set方法
        Object.defineProperty(obj,key,{
            get: function(){
                //Dep.target指標指向watcher,增加訂閱者watcher到主體物件Dep
                if(Dep.target){
                    dep.addSub(Dep.target);
                }
                return val;
            },
            set: function(newVal){
                if(newVal === val){
                    return
                }
                val = newVal;
                //console.log(val);
                //給訂閱者列表中的watchers發出通知
                dep.notify();
            }
        });
    }

    //主題物件Dep建構函式
    function Dep(){
        this.subs = [];
    }
    //Dep有兩個方法,增加觀察者  和  釋出訊息
    Dep.prototype = {
        addSub: function(sub){
            this.subs.push(sub);
        },
        notify: function(){
            this.subs.forEach(function(sub){
                sub.update();
            });
        }
    }

    //DocumentFragment(文件片段)可以看作節點容器,它可以包含多個子節點,當我們將它插入到DOM中時,只有它的子節點會插入目標節點,所以把它看作一組節點的容器。使用DocumentFragment處理節點,速度和效能遠遠優於直接操作DOM。Vue進行編譯時,就是將掛載目標的所有子節點劫持(真的是劫持)到DocumentFragment中,經過一番處理後,再將DocumentFragment整體返回插入掛載目標。
    //var dom = nodeToFragment(document.getElementById("app"));
    //console.log(dom);
    //返回到app中
    //document.getElementById("app").appendChild(dom);


    function nodeToFragment(node,vm){
        var flag = document.createDocumentFragment();
        var child;
        //劫持node的所有子節點(真的在dom樹中消失了,所以要在下邊重新返回搭到app中)
        while (child = node.firstChild){
            //先編譯所有的子節點,再劫持到文件片段中
            compile(child,vm);
            flag.appendChild(child);
        }
        return flag;
    }

    //編譯節點,初始化資料繫結
    function compile(node,vm){
        //該正則匹配的是 :{{任意內容}}
        var reg = /\{\{(.*)\}\}/;
        //節點型別為元素
        if(node.nodeType === 1){
            var attr = node.attributes;
            //解析屬性,不同的屬性不用的處理方式,這裡只寫了v-model屬性
            for(var i=0;i<attr.length;i++){
                if (attr[i].nodeName == "v-model") {
                    //獲取節點中v-model屬性的值,也就是繫結的屬性名
                    var name = attr[i].nodeValue;

                    node.addEventListener("input",function(e){
                        //當觸發input事件時改變vue.data中相應的屬性的值,進而觸發該屬性的set方法
                        vm[name] = e.target.value;
                    });
                    //改變之後,通過屬性名取得資料
                    node.value = vm.data[name];
                    //用完刪,所以瀏覽器中編譯之後的節點上沒有v-model屬性
                    node.removeAttribute("v-model");
                }
            }
        }
        //節點型別為text
        if(node.nodeType === 3){
            //text是否滿足文字插值的寫法:{{任意內容}}
            if(reg.test(node.nodeValue)){
                //獲取匹配到的字串:這裡的RegExp.$1是RegExp的一個屬性
                //該屬性表示正則表示式reg中,第一個()裡邊的內容,也就是
                //{{任意內容}} 中的  文字【任意內容】
                var name = RegExp.$1;
                //去掉前後空格,並將處理後的資料寫入節點
                name = name.trim();
                //node.nodeValue = vm.data[name];
                //例項化一個新的訂閱者watcher
                new Watcher(vm,node,name);
                return;
            }
        }

    }

    //觀察者建構函式。
    //上邊例項化新的觀察者的時候執行這個函式:通過get()取得Vue.data中對應的資料,然後通過update()方法把資料更新到節點中。
    function Watcher(vm,node,name){
        //讓全域性變數Dep的target屬性的指標指向該watcher例項
        Dep.target = this;
        this.vm = vm;
        this.node = node;
        this.name = name;
        //放入Dep.target才能update()?
        this.update();
        Dep.target = null;
    }
    // 觀察者使用update方法,實際上是
    Watcher.prototype = {
        update: function(){
            this.get();
            this.node.nodeValue = this.value;
        },
        //獲取data中的屬性值 
        get: function(){
            this.value = this.vm[this.name]; //觸發相應屬性的get
        }
    }



    var vm = new Vue({
        el: "app",
        data: {
            text: "Hello Vue"
        }
    })

</script>
</body>
</html>


相關推薦

vue雙向資料原理

Vue應用的是mvvm框架,view和model分離,然後通過vm雙向資料繫結,` <code class="hljs handlebars has-numbering" style="display: block; padding: 0px; color: inh

vue 雙向資料原理

Vue的雙向資料繫結原理是什麼?vue.js是採用資料劫持結合釋出者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的setter, getter,在資料變動時釋出訊息給訂閱者,出發相應的監聽回撥。具體步驟:首先Vue會使用document

Vue雙向資料原理解析

首先上原始碼,模擬vue的雙向資料繫結原理<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Two-way data-

vue中實現雙向資料原理,使用了Object.defineproperty()方法,方法簡單

在vue中雙向資料繫結原理,我們一般都是用v-model來實現的 ,但一般在面試話會問到其實現的原理, 方法比較簡單,就是利用了es5中的一個方法.Object.defineproperty(),它有三個引數, Object.defineproperty(obj,'val',attrObject), 引數

詳解 vue 雙向資料原理,並實現一組雙向資料

1:vue 雙向資料繫結的原理: Object.defineProperty是ES5新增的一個API,其作用是給物件的屬性增加更多的控制Object.defineProperty(obj, prop, descriptor)引數 obj: 需要定義屬性的物件(目標物件)prop: 需被定義或修改的屬性名(物

Vue.js雙向資料原理

vue雙向繫結就是指model層與view層的同步,兩者之間任意一個發生變化都會同步更新到另一者。 View為檢視層,Model為資料層,ViewModel為邏輯控制層。 vue.js採用資料劫持結合釋出者-訂閱者模式的方法,通過Object.defin

vue雙向資料原理

有關雙向資料繫結的原理 最近兩次面試的時候,被問到了vue中雙向資料繫結的原理,因為初學不精,只是使用而沒有深入研究,所以答不出來。之後就在網上查找了別人寫的部落格,學習一下。 下面是部落格園一篇部落格,以及MDN上講解Object.defineProper

深入vue原始碼,瞭解vue雙向資料原理

大家都知道vue是一種MVVM開發模式,資料驅動檢視的前端框架,並且內部已經實現了雙向資料繫結,那麼雙向資料繫結是怎麼實現的呢? 先手動擼一個最最最簡單的雙向資料繫結 1 <div> 2 <input type="text" name="" id="te

vue 雙向資料的實現學習(二)- 監聽器的實現

廢話:上一篇https://www.cnblogs.com/adouwt/p/9928278.html  提到了vue實現的基本實現原理:Object.defineProperty() -資料劫持 和 釋出訂閱者模式(觀察者),下面講的就是資料劫持在程式碼中的具體實現。 1.先看如何呼

vue雙向資料的實現

總所周知,偵測一個物件變化的方法,常用的兩種是Object.defineProperty,和es6的proxy,下面就基於Object.defineProperty實現簡單的雙向繫結 其實vue主要是通過Object.defineProperty實現的,至於vue3.0會不會重寫

模擬Vue雙向資料

事件物件 function EventEmit(){ // { // "message":['事件1','事件2'] // } this.callbacks={} } EventEmit.prototype.on=function(eventNa

Vue 雙向資料實現

  <!DOCTYPE html> <html> <head> <title>myVue</title> <style> #app{ text-align: center;

Angular雙向資料原理

Angular是通過髒檢測來進行雙向資料繫結 Angular比不是通過定時去進行檢測 Angular在$digest cycle流程裡面,會從rootscope開始遍歷,檢查所有的watcher。

angularjs雙向資料原理

angular並不存在定時髒檢測。angular對常用的dom事件,xhr事件等做了封裝, 在裡面觸發進入angular的digest流程。在digest流程裡面, 會從rootscope開始遍歷,

angularjs雙向資料原理解析

angularjs的雙向資料繫結 髒值(發生了變化的值)檢查不等於定時輪詢,而是特定事件觸發才會執行 只有指定事件觸發後才會進入髒值輪詢。 - DOM事件,譬如使用者輸入文字,點選按鈕等。(ng-click) - XHR(ajax)響應事件 (http)

JS框架雙向資料原理及思考

本文章資訊點 雙向資料繫結原理(vue) 如何設計搭建自己的框架 程式碼暫略,詳見,github [地址]https://github.com/yyccmmkk/Bi-directionalDataBindingDemo 什麼是單向什麼是雙向? 單向指是資料從mod

Vue雙向資料的理解

一、什麼是MVVM框架   所謂MVVM,是Model - View - ViewModel 的簡寫。   我的理解是頁面上所看到的就是View。 使用Vue.js或者是其他MVVM框架,是在操作ViewModel,實現View - ViewModel的雙向互動。 然後Mod

雙向資料原理

1. 釋出者-訂閱者模式(backbone.js) 一般通過sub, pub的方式實現資料和檢視的繫結監聽,更新資料方式通常做法是 vm.set(‘property’, value),雖然老套古板,這種方式的優點在於相容ie8以下版本。 2. 髒

vue實現雙向資料原理及實現篇 vue雙向原理及實現

轉自:canfoo#! vue的雙向繫結原理及實現 前言 先上個成果圖來吸引各位: 程式碼:                          &nb

vue.js和angular雙向資料的實現原理

一、vue雙向資料繫結 1、原理 資料劫持: vue.js 是採用資料劫持結合釋出者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的setter,getter,在資料變動時釋出訊息給訂閱者,觸發相應的監聽回撥。 2、實現步驟 要實現mv