美文网首页
数据双向绑定的原理分析

数据双向绑定的原理分析

作者: 祝家庄打烊 | 来源:发表于2020-02-18 12:54 被阅读0次

    模板编译

    1、所有的DOM操作,都放到内存fragment中执行(创建fragment内存空间,把所有的子元素都移动到fragment中包裹,操作完DOM元素后,在把fragment移动到根节点上)。

    function compile(el,data){
        var fragment = document.createDocumentFragment();
        var first = null;
        while(first=el.firstChild){
            fragment.appendChild(first);
        }
        renderDom(fragment);
        el.appendChild(fragment);
    }
    

    2、HTML中存在v-html和{{}}内容编译成对应的数据(遍历fragment子元素,判断是元素节点还是文本节点,文本节点去掉{{}}找到对应的数据来源,在给相应的元素赋值)
    区分DOM节点

    function renderDom(fragment){
        var _that = this;
        Array.from(fragment.childNodes).forEach(function(item){
            if(item.nodeType==1){
                //元素节点
                renderElement(item);
                if(item.childNodes.length>0) renderDom(item);
            }else{
                //文本节点
                renderText(item)
            };
        })
    }
    

    遍历元素节点的属性值,找到对应的v-

    function renderElement(dom){
        Array.from(dom.attributes).forEach(function(attr){
            if(attr.name=="v-html"||attr.name=="v-text"||attr.name=="v-model"){
                eleRender(attr.value,dom);
            }
        })
    }
    

    元素节点赋值

    function eleRender(value,dom){
        if(dom.tagName.toLocaleLowerCase()=="input"){
            dom.value = compileData(value);
        }else{
            dom.innerHTML = compileData(value);
        }
        
    }
    

    文本节点赋值

    function renderText(text){
        var reg = /\{\{([^}]+)\}\}/;
        text.textContent.replace(reg,function(){
            new Watch(arguments[1],function(newValue){
                text.textContent = newValue;
            })
            text.textContent = compileData(arguments[1]);
        })
    }
    

    数据劫持

    当数据data发生改变的时候,重新编译模板。(利用的是Object.defineProperty监听数据变化,watch方法对每个元素进行监听,发布订阅者模式订阅watch对象,发布watch对象中update方法)
    1、Object.defineProperty监听数据变化(是对象上的每个属性进行监听,而且要监听到数据的最底层)

    function observer($data){
        if(!$data || typeof($data)!="object"){
            return false;
        }
        for(var key in $data){
            defineWatch($data,key,$data[key])
        }
    }
    function defineWatch($data,key,value){
        observer(value);
        //监听对象中某个属性的变化
        var subScript = new Subscribe();
        Object.defineProperty($data, key, {
            enumerable: true,
            configurable: true,
            get:function(){
                subScript.order(Subscribe.dep);
                return value;
            },
            set:function(newValue){
                value=newValue;
                subScript.push();
            }
        })
    }
    

    2、定义watch方法,对每个元素进行绑定,当监听到的数据改变时,触发watch方法,对元素重新赋值

    function Watch(explor,callback){
        Subscribe.dep = this;
        this.oldValue = compileData(explor);
        Subscribe.dep = null;
        this.update = function(){
            var newValue = compileData(explor);
            console.log("newValue",newValue)
            if(this.oldValue!=newValue){
                callback(newValue)
            }
        }
    }
    
    image.png

    3、定义发布订阅者模式,当获取数据的时候,表明元素正在进行赋值操作,这时,需要订阅watch对象,当改变数据的时候,表明赋值完成,这时,需要发布watch对象上的update方法,对监听到元素进行赋值操作。

    function Subscribe(){
        this.store = [];
        this.order = function(watch){
            this.store.push(watch);
        }
        this.push = function(){
            if(this.store.length==0) return false;
            this.store.forEach(function(item){
                item && item.update()
            })
        }
    }
    

    项目案例

    相关文章

      网友评论

          本文标题:数据双向绑定的原理分析

          本文链接:https://www.haomeiwen.com/subject/ogxgfhtx.html