1. 程式人生 > >Vue 更新機制及資料繫結

Vue 更新機制及資料繫結

一、更新機制。

參考帥氣的別人家博主:更新機制!

二、資料繫結

原理:
vue主要是藉助物件的訪問器屬性(Object.defineProperty)劫持資料,並結合訂閱者-釋出者模式來實現資料雙向繫結。

通過Object.defineProperty把data中的各資料屬性改為訪問器屬性,來劫持每個屬性的setter、getter;setter劫持到資料變化後,作為釋出者 釋出通知,訂閱者們接到通知後更新資料。

為了進一步說明vue原理,這裡我們藉助例項來進行講解。

例項:
有一段html程式碼(mvvm.html),需要保持 輸入框中資料和data中資料同步。需求有了,那我們如何實現呢?看程式碼:

mvvm.html程式碼:

<div id="app">
    <input type="text" id="a" v-model='name' />
    {{name}}
</div>

利用Object.defineProperty把data中的各資料屬性改為訪問器屬性

//把data中的所有資料屬性修改為訪問器屬性:
function observe(obj,vm){
    Object.keys(obj).forEach(function(key){
        defineReactive(obj,key,obj[key]);
    })
}
//資料屬性修改為訪問器屬性:
function defineReactive(obj,key,val){
    var dep = new Dep();//主題物件,存放key屬性的所有訂閱者
    Object.defineProperty(obj,key,{
        get: function(){
            if(Dep.target){
                dep.subs.push(Dep.target);//新增訂閱者
            }
            return val;
        },
        set: function(newVal){
            if(val==newVal)return;
            val = newVal;//資料劫持:當給key屬性賦值時,會觸發set方法
            dep.notify();//釋出通知
        }
    })
}

定義主題物件:新增訂閱者和釋出者方法

function Dep(){
    this.subs = [];
}
Dep.prototype = {
    addSub: function(sub){//新增訂閱者
        this.subs.push(sub);
    },
    nodify: function(){//釋出通知
        this.subs.forEach(function(sub){
            sub.update();//訂閱者更新資料
        })
    }
}

訂閱者:新增資料更新方法

function Watcher(vm, node, name){
    Dep.target = this;
    this.name = name;
    this.node = node;
    this.vm = vm;
    this.update();
    Dep.target = null;
}
Watcher.prototype = {
    update: function(){//更新資料
        this.get();
        this.node.nodeValue = this.value;
    },
    get: function(){//獲取資料
        this.value = this.vm.data[this.name];//觸發get
    }
}

利用documentFragment(文件片段)來處理節點,處理後再把文件片段插入掛載目標(注:操作documentFragment優於直接操作Dom節)

function nodeToFragment(node,vm){
    var fragment = document.createDocumentFragment();
    var child;
    while(child = node.firstChild){
        compile(child,vm);//編譯資料
        fragment.appendChild(child);
    }
    return fragment;
}

編譯資料:解析模板指令,將模板中的變數替換成資料

function compile(node,vm){
    var reg = /\{\{(.*)\}\}/;
    if(node.nodeType==1){
        var attrs = node.attributes;
        for(var i=0,ln=attrs.length;i<ln;i++){
            if(attrs[i].nodeName=='v-model'){
                var name = attrs[i].nodeValue;
                node.addEventListener("input",function(event){
                    //給相應的data賦值,觸發該屬性的set方法
                    vm.data[name]=event.target.value;
                })
                node.value = vm.data[name];//將data的值賦值給節點
                node.removeAttribute('v-model');
            }
        }
    }
    if(node.nodeType==3){
        if(reg.test(node.nodeValue)){
            var name = (node.nodeValue).match(reg)[1];
            name = name.trim();
            new Watcher(vm, node, name);//為節點新增訂閱者方法
        }
    }
}

建立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);
    document.getElementById(id).appendChild(dom);
}

建立Vue示例:呼叫Vue,實現 輸入框資料和data資料同步

var vm = new Vue({
    el: 'app',
    data: {
        name:'Lucy'
    }
})

轉載自:https://blog.csdn.net/yihanzhi/article/details/79898572