简易的Vue实现

121 阅读1分钟

对于理解Vue的工作机制还是有些帮助的,如果需要深入理解Vue还得自己上GitHub看源码的。

class Vue {
    /**
    * 构造方法
    * 观察数据,编译模板
    * /
    constructor (options) {
      this.$el = document.querySelector(options.el)
      this.$methods = options.methods
      this._binding = {}
      this._observe(options.data)
      this._compile(this.$el)
    }
    /**
    * 将观察数据的key与watcher建立映射关系,便于数据更新时调用watcher.update方法
    * watcher.key是一个数组,是因为观察数据(data)可能不止用于一个DOM属性,要达到更新多处就需要push多个watcher
    * /
    _pushWatcher (watcher) {
      if (!this._binding[watcher.key]) this._binding[watcher.key] = []
      this._binding[watcher.key].push(watcher)
    }
    
    /**
    * Vue2.x用的是Object.defineProperty方法代理数据的更新操作
    * Vue3.x是用Proxy,究其原理差不多
    * 这里的作用是代理set方法,数据发生变化时更新视图UI
    * /
    _observe (data) {
      var that = this
      this.$data = new Proxy(data, {
        set(target,key,value){
          let res =  Reflect.set(target,key,value)
          that._binding[key].map(item => {item.update()})
          return res
        }
      })
    }
    /**
    * 这个compile方法比较简单,递归找DOM节点,取属性/方法,push到watcher
    * /
    _compile (root) {
      const nodes = Array.prototype.slice.call(root.children)
      let data = this.$data
      nodes.map(node => {
        if(node.children.length > 0) this._complie(node)
        if(node.hasAttribute('v-bind')) {
          let key = node.getAttribute('v-bind')
          this._pushWatcher(new Watcher(node,'innerHTML',data,key))
        }
        if(node.hasAttribute('v-model')) {
          let key = node.getAttribute('v-model')
          this._pushWatcher(new Watcher(node,'value',data,key))
          node.addEventListener('input',() => {data[key] = node.value})
        }
        if(node.hasAttribute('v-click')) {
          let methodName = node.getAttribute('v-click')
          let mothod = this.$methods[methodName].bind(data)
          node.addEventListener('click',mothod)
        }
      })
    }
}

class Watcher {
    constructor (node,attr,data,key) {
      this.node = node
      this.attr = attr
      this.data = data
      this.key = key
    }
    update () {
        this.node[this.attr] = this.data[this.key]
    }
}

// 实例化一个Vue 测试用

<div id="app">
  <input type="text" v-model="num">
  <input type="button" value="增加" v-click="add">
  <input type="button" value="减去" v-click="sub">
  <div v-bind="num"></div>
</div>
<script>
window.app = new Vue({
  el: '#app',
  data:{
      num:0
  },
  methods:{
    add(){
      this.num++
    },
    sub(){
      this.num--
    }
  }
})
</script>