部分方法手写

class Vue {
  constructor(options) {
    // 生命周期
    if (typeof options.beforeCreate === 'function') {
      options.beforeCreate.bind(this)()
    }
    this.$data = options.data()
    // 数据响应式
    this.dataReact()
    this.thisData(this.$data)
    if (typeof options.created === 'function') {
      options.created.bind(this)()
    }
    if (typeof options.beforeMount === 'function') {
      options.beforeMount.bind(this)()
    }
    this.$el = document.querySelector(options.el)
    this.methods = options.methods
    if (typeof options.mounted === 'function') {
      options.mounted.bind(this)()
    }
    // 解析节点
    this.compliedNode(this.$el)
  }

  // 给this拷贝一份数据 使得在script中可以直接使用this.***获取数据
  thisData(data) {
    for (let key in data) {
      this[key] = data[key]
    }
  }

  // 数据响应式
  dataReact() {
    Object.keys(this.$data).forEach(key => {
      // 顺便要改变this指向 如果不改变this指向,在Observe里this就是this.$data
      Observe.bind(this)(this.$data, key, this.$data[key])
    })

    // 绑定setter和getter
    function Observe(data, key, value, that = this) { //that指向当前实例
      Object.defineProperty(data, key, {
        get() {
          return value
        },
        set(newVal) {
          value = newVal
          // 触发其他订阅者
          that[key] = newVal
          that.Dep.trigger(key)
        }
      })
    }
  }

  // 使用订阅发布模式实现精准更新
  Dep = {
    map: {},
    // 添加订阅者
    collect(eventName, fn) {
      if (!this.map[eventName]) {
        this.map[eventName] = []
      }
      this.map[eventName].push(fn)
    },
    // 触发事件
    trigger(eventName) {
      this.map[eventName].forEach(fn => fn())
    }
  }

  // 获取标签元素 1.添加事件 2.插值语法
  compliedNode(node) {
    // 获取根节点下的所有子节点
    let childNodes = node.childNodes
    // 取出标签节点
    let tagNodes = Array.from(childNodes).filter(item => {
      return item.nodeType === 1
    })
    // 遍历标签节点
    this.recursiveNode(tagNodes)
  }

  // 递归遍历子节点
  recursiveNode(nodeParent) {
    nodeParent.forEach(node => {
      if (node.childNodes.length > 0) {
        this.recursiveNode(node.childNodes)
      }
      if (node.nodeType === 1) {
        // 获取属性值名称列表
        let attrs = node.attributes
        // 遍历属性 处理指令
        Array.from(attrs).forEach((item) => {
          if (item.nodeName === '@click') {
            // 给标签添加原生点击事件 并且使this指向当前实例
            node.addEventListener('click', this.methods[item.nodeValue].bind(this))
          }
          if (item.nodeName === 'v-model') {
            // 初始赋值
            node.value = this.$data[item.nodeValue]
            this.Dep.collect(item.nodeValue, () => node.value = this.$data[item.nodeValue])
            // 给标签绑定input事件
            node.addEventListener('input', (e) => {
              node.value = this.$data[item.nodeValue] = e.target.value
            })
          }
        })
        // 插值语法 处理插值表达式
        if (node.innerHTML.includes('{{')) {
          // 获取多个插值语法
          let reg = /{{(.*?)}}/g
          let match = node.innerHTML.match(reg)
          // 遍历插值语法
          match.forEach((item) => {
            // 获取插值语法中的值
            let key = item.slice(2, -2).trim()
            if (this.$data[key]) {
              // 替换插值语法
              node.innerHTML = node.innerHTML.replace(item, this.$data[key])
              // 记录上次替换的值
              let lastValue = JSON.parse(JSON.stringify(this.$data[key]))
              // 订阅数据变化
              this.Dep.collect(key, () => {
                if (lastValue !== this.$data[key]) {
                  // 新值替换旧值
                  node.innerHTML = node.innerHTML.replace(lastValue, this.$data[key])
                  lastValue = JSON.parse(JSON.stringify(this.$data[key]))
                }
              })
            }
          })
        }
      }
    })
  }
}
Last Updated:
Contributors: 黄定鑫