对于vue2 数据双向绑定的理解,手敲一遍后会有更好的认知~
一、页面数据部分内容
在实现数据双向绑定之前,我们首先 先准备到我们需要用到的数据,然后再实现代码部分
(一)页面结构
这里就要完成使用 v-model 绑定的数据 与页面结构插值表达式中的值 互相关联,达到vue 的数据双向绑定的效果
<body id="app">
<span>up主:{{ name }}</span>
<input type="text" v-model="name">
<span>更多:{{ more.like }}</span>
<input type="text" v-model="more.like">
</body>
(二)script 部分
通过script 标签部分 引入的vue 文件,并不是真实的 vue,也就是我们马上写的代码部分
el 中我们存储了html 部分的 <body/> 标签中的 id 便于我们以后方便使用
<script src="./vue.js"></script>
<script>
const vm = new Vue(
{
el: '#app',
data: {
name: 'kerwin',
more: {
like: '点赞'
}
}
}
)
console.log(vm);
</script>
二、vue.js 代码
(一)创建Vue 实例
因为我偶们考虑到代码量比较多,再者我们需要使用构造函数,所以我们选择class类来创建构造函数
obj_instance 为实例时 传递的对象,首先。我们先将他的data 属性,保存至实例中的 $data中,然而vue 中也是这样进行使用的
class Vue {
constructor(obj_instance) {
this.$data = obj_instance.data
}
}
(二)实现数据劫持
关键点:我们需要使用Object.defineProperty 来实现数据劫持,vue2中是这样,vue3 略微不一样,理解了vue2 再理解vue3 就不难了
1、创建数据劫持的函数
因为我们需要双向绑定的是 data中的数据 所以我们需要传递 data
class Vue {
constructor(obj_instance) {
// ....
// 在构造函数中将data 传入
Observer(this.$data)
}
}
// 实现数据劫持
function Observer(data_instance) {
}
2、遍历data对象并实现数据劫持
理解:Object.defineProperty
Object.defineProperty( 操作的对象,操作的属性,{ } )
{ } 中传入对象,并在对象中实现数据监听
这个方法可以修改对象中的现有属性
// 数据劫持
function Observer(data_instance) {
// 遍历对象中的每一个属性
Object.keys(data_instance).forEach(key => {
Object.defineProperty(data_instance, key, {
// 设置属性描述符可以改变
configurable: true,
// 设置是否可枚举
enumerable: true,
get() {
},
set(newValue) {
},
})
})
}
3、设置setter和getter
我们可以在设置之前在控制台查看一下数据
① getter
在observer 之前我们输出一下 vm。当我们在访问vm中的属性值的时候,发现数据为undefined,因为在访问的时候我们 get 并没有return,导致数据丢失
所以我们在遍历key 的时候,先将数据保存至 value 这个变量中,当我们访问的时候,会触发get,并将value 返回出来,这样数据就没有问题了
② setter
当我们设置vm 中的数据的时候,会触发set函数,他的形参(newValue) 就是我们设置的数据,所以我们直接将 value设置为newValue 就可以了,此时数据就可以正常的修改
// 数据劫持
function Observer(data_instance) {
// 如果没有值或不为对象那么直接返回出去
Object.keys(data_instance).forEach(key => {
++ let value = data_instance[key]
Object.defineProperty(data_instance, key, {
configurable: true,
enumerable: true,
get() {
++ console.log(`访问了${key}属性 --> 属性值为:${value}`);
++ return value // 将原本数据return出来
},
set(newValue) {
++ value = newValue
++ console.log(`修改了${key}属性值${value} --> 为${newValue}`);
},
})
})
}
4、处理data对象属性中的对象
因为我们设置的data属性值中 也是有对象的,我们使用 object.key 遍历的只有第一层,要想让data中所有的数据 都可以劫持到,那么我们需要使用递归
当我们遍历key的时候 首先先递归调用一下是否 有值或 数据类型为对象,如果有那么继续递归,就可以解决这个问题了
function Observer(data_instance) {
// 如果没有值或不为对象那么直接返回出去
++ if (!data_instance || typeof data_instance !== 'object') return
Object.keys(data_instance).forEach(key => {
let value = data_instance[key]
// 递归,内部属性
++ Observer(value)
Object.defineProperty(data_instance, key, {
// ....
})
})
}
5、处理给属性赋值一个新对象
当我们给一个属性重新赋值为一个对象时,他并不会递归,因为observe 已经运行完了,所以我们需要在 setter 中再进行调用一下observe
Object.defineProperty(data_instance, key, {
// ...
set(newValue) {
// 递归
++ Observer(newValue)
value = newValue
return console.log(`修改了${key}属性值${value} --> 为${newValue}`);
},
})
太多了,不做解释了,以后再补充
详情可见gitee 克隆下来就可以,地址如下