模拟实例
我们就按照以下基础使用,其中包含了差值表达式、v-text 和 v-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 实例,看一下我们需要实现其中的哪些属性。
模拟属性
$options
$options 把构造函数的参数记录到了当中。
$data
data 选项中的成员记录到了 $data 中,并且转换成了 getter 和 setter。$data 中的 setter 是真正监视数据变化的地方。_data 和 $data 指向的是同一个对象,_ 开头的是私有成员,$ 开头的是公共成员。
$el
$el 对应选项中的 el,可以是选择器,也可以是一个 Dom 对象,如果是选择器则需要转换为 Dom 对象。
msg / count
vm中具有count和msg属性count和msg都具有get/set方法
vue 构造函数内部需要将 data 中的成员转换为 getter 和 setter 注入到 vue 实例上。这样方便在其他地方使用的时候,可以直接使用 this.msg 和 this.count 来使用。
实现以上属性
- 通过属性保存选项数据到
$options、$data、$el中 - 通过
_proxyData方法把data中的成员转换成getter和setter,注入到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,看看我们是否实现了需要的功能:
可以看出来我们已经实现了这几个属性,并将
data 中的成员转换成 getter 和 setter,注入到 vue 实例中,但是 $data 中的属性还没有转换成 getter 和 setter,我们会在以后的代码中实现。