Vue双向绑定原理

2019-10-08  本文已影响0人  来了啊小老弟
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div id="app"></div>
</body>
</html>
<script type="text/javascript">
    var temObserable;
    // 观察者
    function Observer (){
        this.observables = []
    }
    Observer.prototype.notify = function(){
        for(var i=0;i<this.observables.length;i++){
            this.observables[i].update()
        }
    }
    Observer.prototype.subscribe = function(){
        this.observables.push(temObserable)
    }
    //可观察对象 具备事件触发的能力
    function Observable(node,propName,data){
        this.$node = node;
        this.$propName= propName;
        this.$data = data;
    }
    Observable.prototype.update = function(){
        if(this.$node.nodeType === 1){
            this.$node.value = this.$data[this.$propName]
        } else if(this.$node.nodeType === 3){
            this.$node.nodeValue = this.$data[this.$propName]
        }
    }
    //  MVVM 是一个构造函数,接受一个options对象,存起来
    function MVVM(options){
        this.$el = options.el;
        this.$options = options;
        this.$data = options.data;
        this.$template = options.template;
        this.$$data = this.$data();//数据对象

        //监视属性,遍历对象
        this.init();
    }

    MVVM.prototype.init = function(){
        this.defineReactive(this.$$data,this.$$data.text); //{text:'abc'}
        this.compiler(this.$template,this.$$data)
    };
    MVVM.prototype.compiler = function(tempstr,data){
        //将tempstr插入到app中
        var box = document.querySelector(this.$el);
        box.innerHTML = tempstr
        //分析box 获取到其中的标记
        var nodes = box.children[0].childNodes;
        //正则匹配
        var regexText = /.*\{\{(.*)\}\}.*/;
        var regexV = /^v-(.*)$/
        // var i=0;i<nodes.length;i++
        for(var i = nodes.length-1;i>=0;i--){
            //判断Nodetype ===3 文本标签,===1 input标签
            var node= nodes[i]
            if(node.nodeType === 3){
                var result = regexText.exec(node.nodeValue);
                if(result){
                    //获取结果与this.$$data匹配 this.$$data[XXX]
                    //触发获取
                    this.textMatch(result[1].trim(), node);  //text
                }
            }else if(node.nodeType === 1){
                //获取元素的属性名称
                var nodeAttrs = node.attributes;
                for(var j=0;j<nodeAttrs.length;j++){
                    console.log(nodeAttrs[j])
                    var attr = nodeAttrs[j];
                    var result = regexV.exec(attr.name)
                    if(result){
                        console.log(attr.name,attr.value,"被匹配了",result)
                        this.redirective[result[1]](attr.value, node, this.$$data)
                    }
                }
            }
        }
    }
    MVVM.prototype.redirective = {
        model:function(propName, node, data){
            temObserable = new Observable(node,propName,data)
            //给元素添加事件和给一个初始值
            node.value = data[propName];
            node.addEventListener('input',function(e){
                data[propName] = e.target.value
            })
        }
    }
    MVVM.prototype.textMatch = function(propName, node){
        //1创建存储信息的行为  可观察对象
        //2将其挂载全局
        //3触发get函数,并从全局中取出1
        temObserable = new Observable(node,propName,this.$$data)
        node.nodeValue = this.$$data[propName]
    }
    MVVM.prototype.defineReactive = function(obj,value){
        for(var key in obj){
            //创建观察者
            var Observers = new Observer()
            Object.defineProperty(obj,key,{
                set:function(v){
                    //更新观察者中的所有可观察对象中的节点数据
                    value = v;
                    console.log('set触发')
                    Observers.notify()
                },
                get:function(){
                    //为节点初始化的时候触发,将当前的节点关联上可观察对象,并加入到观察者中
                    console.log('get触发')
                    if(temObserable){
                        Observers.subscribe();
                        temObserable = null
                    }
                    return value
                }
            })
        }
    }
    var vm = new MVVM({
        el:"#app",
        data(){
            return {
                text:"abc"
            }
        },
        template:"<div><input type='text' v-model='text'/>{{text}}</div>"
    })
</script>
上一篇 下一篇

猜你喜欢

热点阅读