<html lang="en">
            <head>
                <meta charset="UTF-8">
                 <meta name="viewport" content="width=device-width, initial-scale=1.0">
                 <meta http-equiv="X-UA-Compatible" content="ie=edge">
                <title>vue双向数据绑定原理</title>
            </head>
            <body>
                <div id="app">
                    <input type="text" v-model="text" />
                    输入的值为:
                    <div>
                    <input type="text" v-model="text">
                    </div>
                </div>
                <script>
                    function nodeToFragment (node, vm) {
                        var flag = document.createDocumentFragment();
                        var child;
                        while (child = node.firstChild) {
                            compile(child, vm);
                            if (child.firstChild) {
                                var dom = nodeToFragment(child, vm);
                                child.appendChild(dom);
                            }
                            flag.appendChild(child);
                        }
                        return flag;
                    }
                    function compile (node, vm) {
                        let reg = /\{\{(.*)\}\}/;
                        // 元素节点
                        if (node.nodeType === 1) {
                            var attrs = node.attributes;
                            for (let attr of attrs) {
                                if (attr.nodeName === 'v-model') {
                                    // 获取v-model指令绑定的data属性
                                    var name = attr.nodeValue;
                                    // 绑定事件
                                    node.addEventListener('input', function(e) {
                                        vm.$data[name] = e.target.value;
                                    })
                                    // 初始化数据绑定
                                    //  node.value = vm.$data[name];
                                    new Watcher(vm, node, name);
                                    // 移除v-model 属性
                                    node.removeAttribute('v-model')
                                }
                            }
                        }
                        
                        // 文本节点
                        if (node.nodeType === 3) {
                            if (reg.test(node.nodeValue)) {
                                var name = RegExp.$1 && (RegExp.$1.trim());
                                // 绑定数据到文本节点中
                                // node.nodeValue = node.nodeValue.replace(new RegExp('\\{\\{\\s*(' + name + ')\\s*\\}\\}'), vm.$data[name]);
                                new Watcher(vm, node, name);
                            }
                        }
                    }
            
                    function Dep () {
                        this.subs = [];
                    }
                    Dep.prototype = {
                        addSub (sub) {
                            this.subs.push(sub);
                        },
                        notify () {
                            this.subs.forEach(sub => {
                                // 执行订阅者的update方法
                                sub.update();
                            })
                        }
                    }
                    function Watcher (vm, node, name) {
                        // 全局的、唯一
                        Dep.target = this;
                        this.node = node;
                        this.name = name;
                        this.vm = vm;
                        this.index = 1;
                        this.update();
                        Dep.target = null;
                    }
            
                    Watcher.prototype = {
                        update () {
                            this.get();
                            let _name;
                            if (this.index === 1) {
                                _name = this.name;
                            } else {
                                _name = this.value;
                            }
                            // this.node.nodeValue = this.value;
                            if (this.node.nodeName === 'INPUT') {
                                // 可以添加TEXTAREA、SELECT等
                                this.node.value = this.value;
                            } else {
                              //  this.node.nodeValue = this.value;
                              this.node.nodeValue = this.node.nodeValue.replace(new RegExp('\\{?\\{?\\s*(' + _name + ')\\s*\\}?\\}?'), this.value);
                            }
                            ++this.index;
                        },
                        get () {
                            this.value = this.vm.$data[this.name]
                        }
                    }
                    function MVue (options) {
                        this.$el = options.el;
                        this.$data = options.data;
            
                        // 数据监听
                        obverser(this.$data);
                        
                        // 模板编译
                        let elem = document.querySelector(this.$el);
                        elem.appendChild(nodeToFragment(elem, this))
                    }
            
                    function obverser (obj) {
                        Object.keys(obj).forEach(key => {
                            if (obj.hasOwnProperty(key)) {
                                if (Object.prototype.toString.call(obj[key]) === '[object Object]') {
                                    obverser(obj[key])
                                }
                                defineReactive(obj, key);
                            }
                        })
                    }
            
                    function defineReactive (obj, key) {
                        var _value = obj[key];
                        // new一个主题对象
                        var dep = new Dep();
                        Object.defineProperty(obj, key, {
                            enumerable: true,
                            configurable: true,
                            set (newVal) {
                                if (_value === newVal) {
                                    return;
                                }
                                _value = newVal;
                                // 作为发布者发出通知给主题对象
                                dep.notify();
                            },
                            get () {
                                // 如果订阅者存在,添加到主题对象中
                                if (Dep.target) {
                                    dep.addSub(Dep.target);
                                }
                                return _value
                            }
                        })
                    }
                </script>
                <script>
                // 初始化
                var vm = new MVue({
                    el: '#app',
                    data: {
                        text: 'hello world'
                    }
                })
                </script>
            </body>
        </html>