<!DOCTYPE html>
<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>MVVM</title>
</head>

<body>
    <div id="app">
        <div>{{name}}</div>
        <div>
            <p>{{person.age}}</p>
        </div>
    </div>
</body>
<script>
    class Vue {
        constructor(options) {
            this.el = document.querySelector(options.el)
            this.data = options.data
            //使得data响应式
            new Observer(this.data)
            //编译模板
            new Compile(this.el, this.data)
        }
    }
    class Observer {
        constructor(data) {
            if (typeof data !== 'object') {
                return
            }
            this.observe(data)
        }
        observe(data) {
            //循环遍历data属性
            Object.keys(data).forEach(key => {
                if (typeof data[key] === 'object') {
                    new Observer(data[key])
                } else {
                    this.defineReactive(data, key)
                }
            })
        }
        defineReactive(data, key) {
            //dep收集的是watcher 在vue中watcher有三种,分别是渲染、计算、watch属性,三者对应的回调函数不同
            //应由一个统一的变量收集,即dep。
            //在渲染watcher中,何处收集是我一开始觉得最绕的地方,其实是在编译模板的时候
            let dep = new Dep()
            let value = data[key]
            Object.defineProperty(data, key, {
                get() {
                    //收集watcher  那Dep.target何时为true呢 往下看
                    if (Dep.target && dep.subs.indexOf(Dep.target) == -1) dep.subs.push(Dep.target)
                    return value
                },
                set(newVal) {
                    if (value === newVal) {
                        return
                    }
                    console.log('set')
                    value = newVal
                    new Observer(value)
                    //调用更新模板的函数
                    dep.notify()
                }
            })
        }
    }
    class Compile {
        constructor(el, data) {
            this.data = data
            this.compile(el)
        }
        compile(el) {
            Array.from(el.children).forEach(node => {
                if (node.children.length !== 0) {
                    this.compile(node)
                }
                if (node.children.length === 0) {
                    //此处实例化了watcher 先看看watcher的逻辑
                    new Watcher(node, this.data)
                }
            })
        }
    }
    class Watcher {
        constructor(node, data) {
            this.node = node
            this.data = data
            this.key
            //实例化的时候就会调用一个get方法
            this.get()
        }
        update(newVal) {
            this.get()
        }
        get() {
            //这里就把Dep的静态属性target设为this,即当前watcher实例
            Dep.target = this
            let html = this.node.innerHTML
            let reg = /\{\{(.*)\}\}/
            if (reg.test(html)) {
                this.key = reg.exec(this.node.innerHTML)[1]
            }
            let value = this.data
            this.key.split('.').forEach(k => {
                value = value[k]
            })
            //上述获取this.data的某个属性操作中,会触发这个属性的get方法,即把watcher收集到了dep中
            //此时完成了依赖收集
            this.node.innerHTML = value
            Dep.target = null
        }
    }
    class Dep {
        constructor() {
            this.subs = []
        }
        notify() {
            this.subs.forEach(sub => sub.update())
        }
    }
    Dep.target = null
    let app = new Vue({
        el: '#app',
        data: {
            name: 'zj',
            person: {
                age: 18,
                sex: 1
            }
        }
    })
    setTimeout(() => {
        app.data.name = '123'
        setTimeout(() => {
            app.data.person.age = 20
        }, 500);
        console.log(app.data.name)
    }, 1000);
</script>

</html>