Vue渲染原理及其双向数据绑定详解

来源:互联网 发布:网易企业邮箱域名格式 编辑:程序博客网 时间:2024/04/25 15:53

双向数据绑定原理

1. 新建vue实例

var data = {        text:'hello world!'    };    var vm = new Vue({        el:'#app',        data    });

2. 将vue实例中的el HTML模板渲染进DOM

 function nodeToFragment(node,vm) {        var frag = document.createDocumentFragment(),//创建DocumentFragment片段            child;        while(child = node.firstChild){//依次取node的子节点将其添加到frag中,            compile(child,vm);//添加到DOM之前需对child和Vue实例进行绑定以监听彼此的变化            frag.appendChild(child);//此方法会同时删除node中的child节点        }        return frag;    }    var dom = nodeToFragment(document.getElementById(id),this);document.getElementById(id).appendChild(dom);//将上面返回的节点添加进DOM

3. 将vue实例中的el HTML模板渲染进DOM,compile详解

function compile(node,vm) {//node为el的子节点        var reg = /\{\{(.*)\}\}/,// .为匹配除\r\n之外的任意单个字符,*为匹配前面字符任意多次            name;        //元素节点        if(node.nodeType == 1){            var attr = node.attributes;            for(var i = 0; i<attr.length; i++){//遍历el的子节点的属性,若其有v-model属性,变从vm.data中取v-model属性对应的值value,将value作为el的子节点的value|innerHTML值,因为v-model一般对应input元素                if(attr[i].nodeName == 'v-model'){                VModelEle.push(node);                    name = attr[i].nodeValue;                    node.value = vm.data[name];                    node.innerHTML= vm.data[name];                    node.addEventListener('input',function (e) {                        vm.data[name] = e.target.value;                    });//此处已假定node为input元素,监听此input,若其value有变化则将其反映到vm.data的name中,name为'v-model'属性对应的nodeValue。到这里,已经实现了DOM到vm.data的绑定。                    new Watcher(vm,node,name);//这一步实现vm.data到DOM的绑定                    node.removeAttribute('v-model');//从node中删除v-model属性。                    break;                }            }        }        //若node为文本节点,判断文本节点的值是不是{{}}这种形式。        if(node.nodeType == 3){            if(reg.test(node.nodeValue)){                name = RegExp.$1;//RegExp为全局对象,$1-$9为最新匹配到的9个正则表达式中带括号的项,只能存储最新9个                name = name.trim();//取reg = /\{\{(.*)\}\}/中括号内匹配到的值,name在vm.data中也有                new Watcher(vm,node,name);//为vm.name添加监听器,实现从vm到DOM的绑定            }        }    }

4. 为vm.name添加监听器,new Watcher(vm,node,name)

  • Dep方法详情
 function Dep() {        this.subs = [];    }    Dep.prototype = {        addSub:function (sub) {            this.subs.push(sub);//sub为Watcher实例        },        notify:function () {            this.subs.forEach(function (sub) {                sub.update();//通知所有Watcher更新DOM            })        }    };
  • Watcher方法详情
 function Watcher(vm,node,name) {        Dep.target = this;        this.name = name;        this.node = node;        this.vm = vm;        this.update();//每次new Watcher时都要用vm.data[this.name]数据更新this.node.nodeValue,即实现vm.data到DOM的绑定。        Dep.target = null;    }    Watcher.prototype = {        update:function () {            this.node.nodeValue = this.vm.data[this.name];        }    };
  • vm.data中的每个数据重写其Object.defineProperty中的setget方法
 function observe(obj) {        Object.keys(obj).forEach(function (key) {            defineReactive(obj,key,obj[key]);        })    }  function defineReactive(obj,key,val){//defineReactive即为每个data属性添加setter和getter,当在外部重新为data添加新属性时,还会调用此方法,接口为Vue.$set(obj,kay,value);。        var dep = new Dep();        Object.defineProperty(obj,key,{        //因为读取vm.data的地方比较多,比如compile方法中的node.value =vm.data[name];node.innerHTML= vm.data[name];而只有第一次为vm.data[name]添加Watcher时才需将此Watcher添加进Dep的subs中,因为new Watcher会执行Watcher的update方法,其中会读取this.vm.data[this.name]。            get:function () {                if(Dep.target){                    dep.addSub(Dep.target);                }                return val;            },            //当重写vm.data中的属性时,就重置vModelEles中元素的value,vModelEles为所有有v-model属性的元素            set:function (value) {                for(var ele in vModelEles){                    vModelEles[ele].value = value;                }                if(value == val) return;                //若两次值不同,将调用dep.notify方法,启动所有Watcher,更改nodeValue                val = value;                dep.notify();                console.log(val);            }        });    }

双向数据绑定原理总结

  • 从DOM到vm.data很简单,即addEventListener监听DOM的变化,将其值写到vm.data中即可。
  • 从vm.data到DOM:
    • 为出现在DOM中的vm.data中的每个元素添加Watcher,所有的Watcher都存放在Dep的subs中
    • 每当vm.data有变化就调用Dep的notify方法,即取出所有subs中的Watcher,调用Watcher的updata方法,更新DOM
原创粉丝点击