MVVM双向绑定简单实现


<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Documenttitle>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <style>
    style>
head>
<body>
    <div id='app'>
        <input type="text" v-model="hello"/>
        {{hello}}
        <div>
            <div>{{name}}div>
        div>
    div>
    <script>
        //发布者
        function Dep(){
            this.subs=[]
        }
        Dep.prototype.addSub = function(_watcher){
            this.subs.push(_watcher);
        }
        Dep.prototype.upDate = function(){
            // console.log(this.subs)
            this.subs.map(_watcher=>{
                console.log(_watcher)
                _watcher.upDate();
            })
        }
        //订阅者
        function watcher(vm,node,name){
            Dep.target = this;
            this.vm=vm;
            this.node=node;
            this.name=name;
            this.upDate();
            Dep.target=null;
        }
        watcher.prototype.upDate=function(){
            console.log('被调用啦???')
            this.node.nodeValue=this.vm[this.name];
        }

        function MVVM(opt){
            this.id = opt.el;
            this.data = opt.data;
            this.observe(this.data,this)
            this.dom = this.nodeToFragment(document.getElementById(this.id),this);
            document.getElementById(this.id).append(this.dom);
        }
        MVVM.prototype.nodeToFragment = function(dom,vm){
            //把#app下的全部保存到文档碎片中
            var frag = document.createDocumentFragment();
            var child;
            while(child = dom.firstChild){
                var _child = this.complie(child,vm)
                frag.append(_child);
            }
            return frag;
        }

        MVVM.prototype.complie = function(dom,vm){
            // 这里主要是把#app 下的dom节点全部解析 
            var reg = /\{\{(.*)\}\}/;
            switch(dom.nodeType){
                case 1://dom节点;
                    var atte = dom.attributes;
                    for(var i=0;i<atte.length;i++){
                        if(atte[i].nodeName == "v-model"){
                            var name = atte[i].nodeValue;
                            
                            dom.addEventListener('input',function(e){
                                vm[name]=e.target.value;
                            })
                            dom.value = vm[name];
                            dom.removeAttribute("v-model");
                        }
                    }
                    dom.append(this.nodeToFragment(dom,vm));
                    break;
                case 3://文本节点
                    var value = dom.nodeValue;
                    if(reg.test(value)){
                        var name = RegExp.$1;
                        name = name.trim();
                        new watcher(vm,dom,name);//对每个文本节点,当做是一个订阅者
                    }
                    break;
            }
            return dom;
        }
        MVVM.prototype.observe = function(obj,vm){
            //将data下的数据全部进行get set监听,然后放置到根对象下
            Object.keys(obj).map( key =>{
                this.defineProperty(vm,key,obj[key])
            })
        }
        MVVM.prototype.defineProperty=function(obj,key,val){
            var dep = new Dep();
            Object.defineProperty(obj,key,{
                get:function(){
                    //把该字段当做发布者
                    //把调用该字段的dom节点当做订阅者
                    if(Dep.target) dep.addSub(Dep.target);
                    console.log('需要取数据',key,'<>',val)
                    return val;
                },
                set:function(newVal){
                    if(val !== newVal){
                        console.log('需要更新数据',val,"--->",newVal)
                        val=newVal;
                        dep.upDate();
                        //发布者进行全局更新
                    }
                }
            })
        }
        var vue = new MVVM({
            el:'app',
            data:{
                hello:'hello world',
                name:'小明'
            }
        })
    script>
body>
html>

你可能感兴趣的:(前端,VUE,MVVM)