手写vue(深入理解mvvm)

153 阅读1分钟
  1. new Vue() 初始化 完成对data数据 的劫持监听 Compile解析指令渲染模板 初始化视图 同时定义一个更新函数和Watcher 将来对应的数据发生变化时 Watcher会调用更新函数 从而更新视图
  2. data的key可能再视图中出现多次 所以每一个key都需要一个管家Dep来管理Watcher 只要data中数据一旦变化 会首先找到对应的Dep 通知所有的Watcher 执行更新函数 然后更新视图

微信截图_20211005142748.png

    // 递归
    observe(val);
  
    // Dep在这创建
    const dep = new Dep()
    
    Object.defineProperty(obj, key, {
      get() {
        // 依赖收集
        Dep.target && dep.addDep(Dep.target)
        return val;
      },
      set(newVal) {
        if (val !== newVal) {
          observe(newVal);
          val = newVal;
  
          dep.notify()
        }
      },
    });
  }
  

function observe(obj){
    if(typeof obj !== "object" || obj == null){
        return obj
    }

    new Observer(obj)
}

class Observer{
    constructor(obj){
        this.value = obj
        
        if(Array.isArray(obj)){

        }else{
            this.walk(obj)
        }
    }
    walk(obj){
        Object.keys(obj).forEach((key)=>{
            defineReactive(obj,key,obj[key])
        })
    }
}

// 给data数据做代理 
function proxy(vm) {
    Object.keys(vm.$data).forEach((key) => {
      Object.defineProperty(vm, key, {
        get() {
          return vm.$data[key];
        },
        set(v) {
          vm.$data[key] = v;
        },
      });
    });
  }

class GVue{
    constructor(options){
        this.$options = options
        this.$data = options.data

        // 数据响应式处理
        observe(this.$data)

        // 代理
        proxy(this)

        new Compile(options.el ,this)
    }
}

class Compile {
    // el-宿主,vm-KVue实例
    constructor(el, vm) {
      this.$vm = vm;
      this.$el = document.querySelector(el);
  
      this.compile(this.$el);
    }
  
    compile(el) {
      // 遍历el dom树
      el.childNodes.forEach((node) => {
        if (this.isElement(node)) {
          // element
          // 需要处理属性和子节点
          // console.log("编译元素", node.nodeName);
          this.compileElement(node);
  
          // 递归子节点
          if (node.childNodes && node.childNodes.length > 0) {
            this.compile(node);
          }
        } else if (this.isInter(node)) {
          // console.log("编译插值表达式", node.textContent);
          // 获取表达式的值并赋值给node
          this.compileText(node);
        }
      });
    }
  
    isElement(node) {
      return node.nodeType === 1;
    }
  
    // {{xxx}}
    isInter(node) {
      return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent);
    }
  
    isDir(attr) {
      return attr.startsWith("k-");
    }
  
    // 更新函数,
    update(node, exp, dir) {
      // init
      const fn = this[dir + 'Updater']
      fn && fn(node, this.$vm[exp])
  
      // update: 创建Watcher
      new Watcher(this.$vm, exp, function(val) {
        fn && fn(node, val)
      })
    }
    
    // 编译文本,将{{ooxx}}
    compileText(node) {
      this.update(node, RegExp.$1, 'text')
    }
  
    textUpdater(node, val) {
      node.textContent = val
    }
    
    // 处理元素所有动态属性
    compileElement(node) {
      Array.from(node.attributes).forEach((attr) => {
        const attrName = attr.name;
        const exp = attr.value;
  
        // 判断是否是一个指令
        if (this.isDir(attrName)) {
          // 执行指令处理函数
          // k-text, 关心text
          const dir = attrName.substring(2);
          this[dir] && this[dir](node, exp)
        }
      });
    }
  
    // k-text处理函数
    text(node, exp) {
      this.update(node, exp, 'text')
    }
  
    // k-html
    html(node, exp) {
      this.update(node, exp, 'html')    
    }
  
    htmlUpdater(node, val) {
      node.innerHTML = val
    }
  }
  class Watcher {
    constructor(vm, key, updateFn) {
      this.vm = vm
      this.key = key
      this.updateFn = updateFn
  
      // 读取一下key的值,触发其get,从而收集依赖
      Dep.target = this
      this.vm[this.key]
      Dep.target = null
    }
  
    update() {
      this.updateFn.call(this.vm, this.vm[this.key])
    }
  }
  

class Dep{
    constructor(){
        this.deps = []
    }

    addDep(dep){
        this.deps.push(dep)
    }

    notify(){
        console.log('notify')
        this.deps.forEach(dep => dep.update())
    }
}