# VUE 原理解析

# dom-diff

  1. dom-diff(进行同层比较)
    1. 数据更改触发set函数
    2. 执行对应的 Dep.notify函数
    3. 通过patch 对应新旧虚拟节点
    4. 判断两个节点是否一样
      1. 不一样:用新的虚拟节点整个替换老节点
      2. 一样:根据子节点情况接着进行比较分四种情况
        1. 老节点有子节点,新节点没有(删除老节点)
        2. 老节点没有子节点,新节点有子节点(把新节点的子节点 append 到 老节点中)
        3. 都有文本节点(以新换旧)
        4. 都有子节点
    5. 进入 patchVnode 函数 做的事情
      1. 新旧虚拟dom若是同一个地址,则无操作
      2. 找到当前节点对应的真实dom elm 获取老节点的子节点和新节点的子节点
      3. 判断 新节点的文本节点是否是undefined
        1. 若是
          1. 比较新旧节点的子节点是否都存在,都存在但不一样
            1. 调用 updateChildren 方法更新
          2. 新节点有子节点 老节点没有
            1. 调用 addVnodes将新节点中的子节点添加到 elm中
          3. 新节点没有子节点 老节点有
            1. 调用 removeVnodes 移除老节点中的子节点
          4. 老节点的文本存在的话
            1. 调用setTextContent 把老节点对应的 elm 的文本设置为空
        2. 若新节点的文本不是空
          1. 调用 setTextContent 把元素 elm 中的文本设置成新的内容

# 数据双向绑定原理

function observe(data) {
    // 判断data是否是对象
    if (Object.property.toSting.call(data) !== '[object Object]') return;
    let keys = Object.keys(data);
    keys.forEach((key) => {
        definedReactive$$1(data, key, data[key]);
    })
}
function definedReactive$$1(target, key, val) {
    var dep = new Dep();
    Object.defineProperty(target, key, {
        enumerable: true,
        get() {
            if (Dep.target) {
             	dep.addSub(Dep.target);   
            }
            return val;
        },
        set(newV) {
            if (newV !== val) {
                val = newV;
                dep.notify();
            }
        }
    })
}
function nodeToFragment(el, vm){
    let fragment = document.createDocumentFragment();
    let child;
    while(child = el.firstChild) {
        complie(child, vm);
        fragment.appendChild(child);
    }
    el.appendChild(fragment);
}
function complie(node, vm){
    // 编译node 节点类型 元素节点考虑行内属性,文本节点直接替换
    if (node.nodeType === 1) {
        let attrs = node.attributes;
        [...attrs].forEach((item) => {
            if (/^v-/.test(item.nodeName)) {
                let vName = item.nodeValue;
                let val = vm.$data[vName];
                new Watcher(node, vName, vm);
                node.value = val;
                node.addEvemtListener('input', (e)=>{
                    vm.$data[vName] = e.target.value;
                })
            }
        })
        [...node.childNodes].forEach((item) => {
            // 递归编译子节点
            complie(item, vm);
        })
    } else {
        let str = node.textContent;
        if (/\{\{(\w+)\}\}/.test(str)) {
           str = str.replace(/\{\{(\w+)\}\}/, (a,b) => {
               new Watcher(node, b, vm);
               return vm.$data[b];
           });
           node.textContent = str; 
        }
    } 
}
class Watcher {
    constructor(node, key, vm){
        Dep.target = this; // this就是 Watcher实例
        this.node = node;
        this.key = key;
        this.vm = vm;
        this.getValue(); // 把当前的Watcher放在事件池里
        Dep.target = null;
    }
    getValue(){
        this.value = this.vm.$data[this.key]
    }
    update(){ // 负责更新dom
        this.getValue();
        if (this.node.nodeType === 1) {
            this.node.value = this.value;
        } else {
            this.node.textContent = this.value;
        }
    }
}
class Dep {
    constructor(){
        this.subs = [];
    }
    addSub(sub){
        this.subs.push(sub)
    }
    notity(){
        this.subs.forEach((sub) => {
            // subs 存储的都是一些Watcher实例
            sub.update();
        })
    }
}
function Vue(option) {
    this.$el = document.querySelector(option.el);
    this.$data = option.data;
    observe(this.$data);
    nodeToFragment(this.$el, this);
}

# 异步更新

$nextTick 原理