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

猜你喜欢

转载自blog.csdn.net/quhongqiang/article/details/83784448