Vue响应式过程

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <input v-model="fanfan">{{fanfan}} 
        
    </div>
</body>
</html>
<script>
    function mycompile(el,vm) {
        //传入节点
        let node=el
        //进行子节点处理
        this.frag=this.nodefragment(node,vm)

        return this.frag

    }


    mycompile.prototype.nodefragment=function(node,vm){
        let flag=document.createDocumentFragment()
        let child

        while (child=node.firstChild) {
             

                //对每个子节点的input和{{}}进行识别
                this.compileElement(child,vm)
                //对于嵌套的进行递归
                if(child.firstChild){
               
                   flag.append(this.nodefragment(child,vm)) 
                }

                flag.append(child)
        }

        return flag
    }

    mycompile.prototype.compileElement=function(node,vm){

        var reg = /\{\{(.*)\}\}/;

      
        if(node.nodeType == 1) {
                    var attr = node.attributes;
                    // 解析属性
                    for(var i = 0; i < attr.length; i++ ) {
                        if(attr[i].nodeName == 'v-model') {
                            var name = attr[i].nodeValue; // 获取v-model绑定的属性名
                            
                            node.addEventListener('input', function(e) {
                                // 给相应的data属性赋值,进而触发该属性的set方法
                                vm[name]=e.target.value
                                console.log(e.target.value);
                              
                            });
                            // node.value = vm[name]; // 将data的值赋给该node
                            new Watch(vm, node, name, 'value');

                        }
                    }
                }

            if(node.nodeType==3) //文本节点
            {
                if(reg.test(node.nodeValue)){
                    //key值
                    let name=RegExp.$1
                    //识别出来后进行data值的替换
                    node.nodeValue=vm[name]
                    //要进行双向绑定的联系 让vm里面值改变的时候也通知{{}}改变 通过watch进行收集 dep进行订阅 到时候改变就会dep进行发布
                    new Watch(vm,node,name,'nodeValue')
                }      
            }
    }
   
    //对于vue对象 需要进行data的响应式绑定和dom树的模板解析
   function myvue(options){
        this.data=options.data
        this.el=options.el
        observer(this.data,this)
        let dom= new mycompile(document.getElementById(this.el),this)
        document.getElementById(this.el).appendChild(dom);
   }

   function observer(obj,vm){
        Object.keys(obj).forEach((v)=>{
            
            defineReactive(vm,v,obj[v])

        })
   }

   function defineReactive(obj,key,value){
        var dep = new Dep();

        //如果是对象的话 还要进行递归响应式 并且将其挂载在对应的对象上
        if(  Object.prototype.toString.call(value)=='[object Object]'){
                observer(value,value)
        }

        Object.defineProperty(obj,key,{
             get: function() {
                 console.log('get');
                    //添加订阅者watcher到主题对象Dep
                    if(Dep.target) {
                        console.log('推入');
                        console.log(Dep.target);
                        // JS的浏览器单线程特性,保证这个全局变量在同一时间内,只会有同一个监听器使用
                        dep.depend(Dep.target);
                    }
                    //返回后作为vue实例上的属性
                   return value; 
                },

            set:function(newvalue){
                console.log('set');
               
                if(newvalue === value) return;
                    value = newvalue;
                        // 作为发布者发出通知
                        dep.notify();
                }  
        })
   }


   function Watch(vm,node,name,type){

            Dep.target=this
            this.vm=vm
            this.node=node
            this.name=name
            this.type=type
            //触发更新
            this.update()
            Dep.target=null

   }
   Watch.prototype.update=function(){
    //触发vm的get函数 把这个watch推入观察者 !!!
    this.node[this.type]=this.vm[this.name]
   }

   function Dep(){

    this.subscribes=[]

   }
   
    Dep.prototype.depend=function(dep){
        this.subscribes.push(dep)
    }

    Dep.prototype.notify=function(dep){
        this.subscribes.forEach((sub)=>{
            sub.update()
        })
    }

 

   let vm=new myvue({
       el:'app',
       data:{
            fanfan:'挂载fan',
            xixi:'我',
            myobj:{
                one:'1',
                two:'2'
            },
       },
      
   })

</script>

通过监听DOM里面的input的键入操作,如果有v-model的属性就会改变vm对象中对应的属性值。触发set操作。

然后通知Dep去更改视图。