原生js仿vue-mue

<!DOCTYPE html>
<html>
<head>
    <title>vue-mue</title>
</head>
<body>

<div id="app">
    <input type="text" v-model="text"> {{text}} <br/>
    <input type="text" v-model="name"> {{name}}
</div>

<script>

    /*
    订阅者
     */
    class Watcher{
        constructor(vm, node, name) {
            Dep.target = this;
            this.vm = vm;
            this.node = node;
            this.name = name;
            this.update();
            Dep.target = null;
        }
        update(){
            this.get();
            this.node.nodeValue = this.value;
        }
        get(){
            // get value
            this.value = this.vm[this.name];
        }
    }

    /*
    发布者仓库
     */
    class Dep{
        constructor(){
            this.subs = [];
        }
        addSub(sub){
            this.subs.push(sub);
        }
        notify(){
            this.subs.forEach(function(sub){
                sub.update();
            })
        }
    }

    /*
    构造Mue
     */
    class Mue{
        constructor(options){
            this.data = options.data;
            var data = this.data;
            var id = options.el;
            var el = document.getElementById(id);
            observe(data, this); //model 加工入口
            var dom = nodeToFragment(el, this);

            el.appendChild(dom);
        }
    }

    function observe(data, vm){ //data: {text: 'hello'}
        Object.keys(data).forEach(function(key){
            defineReactive(vm, key, data[key]);
        })
    }

    function defineReactive(vm, key, val){
        var dep = new Dep();
        Object.defineProperty(vm, key, {
            get: function(){
                console.log(`获取数据${val}`);
                if(Dep.target){
                    dep.addSub(Dep.target);
                }
                //set init value from data
                return val;
            },
            set: function(newValue){
                if(newValue === val) return;
                val = newValue;
                console.log(`数据更新${val}`);
                dep.notify();
            }
        })
    }

    /*
        node 容器dom节点  这里指id为app的div标签节点
        vm 实例化Mue对象
     */
    function nodeToFragment(node, vm){
        var frag = document.createDocumentFragment();
        var child;
        while(child = node.firstChild){
            compile(child, vm);
            frag.append(child);
        }
        return frag;
    }

    /*
        node 遍历后的节点(元素节点、文本节点等)
        vm 实例化Mue对象
     */
    function compile(node, vm){
        var reg = /\{\{(.*)\}\}/;
        //处理元素节点
        if(node.nodeType === 1){
            if(node.hasAttribute('v-model')){
                var name = node.getAttribute('v-model');
                node.addEventListener('input', function(e){
                    //set value
                    vm[name] = e.target.value;
                })
            }            
            //get value
            node.value = vm[name];
            node.removeAttribute('v-model');
        }
        //处理文本节点
        if(node.nodeType === 3){
            if(reg.test(node.nodeValue)){
                var name = RegExp.$1;
                name = name.trim();                
                //get value
                node.nodeValue = vm[name];
            }
            new Watcher(vm, node, name); //订阅入口
        }
    }

    //实例化Mue对象
    var app = new Mue({
        el: 'app',
        data: {
            text: 'Well',
            name: 'john'
        }
    })

</script>
</body>
</html>

 

posted @ 2018-03-30 10:34  sunbey80  阅读(513)  评论(0编辑  收藏  举报