vue 响应式原理模拟1 - Vue

78 阅读1分钟

模拟实例

我们就按照以下基础使用,其中包含了差值表达式、v-textv-model,实现一个简单的 Vue

<div id="app">
<h1>差值表达式</h1>
  <h3>{{ msg }}</h3>
  <h3>{{ count }}</h3>
<h1>v-text</h1>
  <div v-text="msg"></div>
<h1>v-model</h1>
  <input type="text" v-model="msg">
  <input type="text" v-model="count">
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
  el: '#app',
  data: {
    msg: 'Hello Vue',
    count: 20,
  }
})
</script>

先在浏览器的控制台上打印一下 vue 实例,看一下我们需要实现其中的哪些属性。 image.png

模拟属性

$options

$options 把构造函数的参数记录到了当中。

$data

data 选项中的成员记录到了 $data 中,并且转换成了 gettersetter$data 中的 setter 是真正监视数据变化的地方。_data$data 指向的是同一个对象,_ 开头的是私有成员,$ 开头的是公共成员。

image.png

$el

$el 对应选项中的 el,可以是选择器,也可以是一个 Dom 对象,如果是选择器则需要转换为 Dom 对象。

msg / count

  1. vm 中具有 countmsg 属性
  2. countmsg 都具有 get/set 方法

vue 构造函数内部需要将 data 中的成员转换为 gettersetter 注入到 vue 实例上。这样方便在其他地方使用的时候,可以直接使用 this.msgthis.count 来使用。

实现以上属性

  1. 通过属性保存选项数据到 $options$data$el
  2. 通过 _proxyData 方法把 data 中的成员转换成 gettersetter,注入到 vue 实例中
class Vue {
  constructor (options) {
    this.$options = options || {}
    this.$data = options.data || {}
    this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el
    this._proxyData(this.$data)
  }
  _proxyData (data) {
    // 这里要用箭头函数,因为箭头函数没有 this 绑定,必须通过查找作用域链决定其值,所以其值由外围最近一层的非箭头函数决定。
    // 如果使用 function 则会使其中的 this 指向 window
    Object.keys(data).forEach(key => {
      // 把 data 的属性注入到 vue 实例 this 中
      Object.defineProperty(this, key, {
        enumerable: true,
        configurable: true,
        get () {
          return data[key]
        },
        set (newValue) {
          if (newValue === data[key]) {
            return
          }
          data[key] = newValue
        }
      })
    })
  }
}

代码测试

将模拟实例中的 Vue 替换成我们刚写的这段 Vue 类:

<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
<script src="./js/vue.js"></script>

然后在控制台上打印出 vm,看看我们是否实现了需要的功能: image.png 可以看出来我们已经实现了这几个属性,并将 data 中的成员转换成 gettersetter,注入到 vue 实例中,但是 $data 中的属性还没有转换成 gettersetter,我们会在以后的代码中实现。