vue数据绑定原理

简单概括

vue 的数据绑定基于 ES5 的 Object.defineProperty() 这个方法对数据做劫持。Object.defineProperty的 setter 与 getter用户看不到 ,但是在内部它们让 Vue追踪依赖,在属性被访问和修改时通知变化。然后结合发布订阅模式在数据发生数据,通知页面进行更新。(由于 ES5 的 Object.defineProperty() 这个方法不兼容 IE8 。所以 vue 的兼容性也是不兼容 IE8 既以下版本)

简单实现一个 Vue 源代码

<div id="app">
  <p>{{ sex }}</p>
  <p>{{ name }}</p>
  <p>{{ age }}</p>
</div>

<script>
// 提供一个 Event 类  发布订阅
class Event {
  constructor() {
    this.dep = {}
  }
  $on (eventName, callback) {
    if (!this.dep[eventName]) {
      this.dep[eventName] = []
    }
    this.dep[eventName].push(callback)
  }
  $emit (eventName, payload) {
    if (this.dep[eventName]) {
      this.dep[eventName].forEach(cb => {
        cb(payload)
      })
    }
  }
}
// 提供一个 Vue 的构造函数,或者 Vue 类
class Vue {
  constructor(options) {
    // 设置一些 实例属性
    this.$el = document.querySelector(options.el)
    this.$data = options.data;
    // 实现 代理属性
    // this.name === this.$data.name
    this.observer(this.$data)
    // 生成一个 Event 的实例
    this._ev = new Event()
    // 解析
    new Compile(this.$el, this)
  }
  /**
   * 将传递过来的对象中的属性直接绑定到实例对象上
   * observer(data)
   */
  observer(data) {
    // data {name: '张三', age: 18}
    // 遍历 data 这个对象
 	//console.log(Object.keys(data)) // ['name', 'age']
    // console.log(Object.values(data)) // ['张三', 18]
    Object.keys(data).forEach(key => {
      Object.defineProperty(this, key, {
        get () {
          return data[key]
        },
        set (value) {
          data[key] = value
          // 数据更新了, 触发事件
          this._ev.$emit(key)
        }
      })
    })
  }
}
// 提供一个 Compile 类。来做页面解析的工作
class Compile {
  /**
   * {DocumentFragment} el DOM 对象
   * {Vue} vm Vue 实例对象
   */
  constructor(el, vm) {
    // 将 vm 绑定到 当前 Compile 的实例对象上
    this.vm = vm

    this.compile(el)
  }

  compile (el) {
    // 遍历 el 这个 DOM 对象 子节点
    // console.log(el.childNodes)
    el.childNodes.forEach(node => {
      // 得到所有的文本节点,节点的文本内容类似 {{ xxx }} 这种格式的
      if (node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent)) {
        console.log(node.textContent)
        // 直接将当前的 node.textContent 给修改了
        // {{ name }} => 张三
        // {{ age }}  => 18
        // 1. 得到 {{ }} 里面的表达式
        let exp = RegExp.$1.trim()
        // 2. 将对应 exp 的数据设置到 当前节点的 textContent 上。
        node.textContent = this.vm[exp]
        // 3. 监听 this.vm === new Vue 的实例
        this.vm._ev.$on(exp, () => {
          node.textContent = this.vm[exp]
        })
      }
      // 递归遍历子节点
      if (node.childNodes) {
        this.compile(node)
      }
    })
  }
}
const vm = new Vue({
  el: '#app',
  data: {
    name: '豪哥',
    age: 18,
    sex: '男'
  }
})
</script>

在这里插入图片描述
使用控制台修改数据
在这里插入图片描述

原创文章 11 获赞 259 访问量 1万+

猜你喜欢

转载自blog.csdn.net/yh604005215/article/details/106178711