一.概念及使用
响应式:
- 当Vue组件的实例初始化的时候已有的数据就是响应式数据
- 通过Object.defineProperty代理实例this身上的
- 响应式属性的值发生改变会触发视图更新
非响应式:
- 当Vue组件的实例初始化的时候没有,后期添加的属性
- 没有通过Object.defineProperty代理实例this身上的
- 非响应式属性的值发生改变不会触发视图更新
- 非响应式情况
1.直接在vm实例对象上添加属性
<template>
<div>
<button @click="addAttr">为vm实例添加属性</button>
<button @click="getNoRespon">获取vm实例对象上的非响应式数据</button>
</div>
</template>
<script>
export default {
name:'',
methods:{
addAttr(){
this.obj={
name:'张三' //给实例对象添加属性
}
},
getNoRespon(){
console.log(this.obj); //虽然给实例对象添加了属性,但是数据是非响应式的
}
}
}
</script>
2.列表渲染通过下标更改列表中的值
需求:点击按钮更改列表中的第0号元素
<template>
<div>
<ul>
<li v-for="(item,index) in arr" :key="index">{{item}}</li>
</ul>
<button @click="changeArr">更改0号元素</button>
</div>
</template>
<script>
export default {
name:'',
data(){
return {
arr:[1,2,3,4]
}
},
methods:{
//改变数组中0号元素的值
changeArr(){
this.arr[0]='a'
}
}
}
</script>
<style scoped>
</style>
这种情况数据其实已经改变了,但是视图没变
解决方法: Vue.set || this.$set
this.arr[0]='a'
改为
this.$set(this.arr,0,'a')
或者import Vue from 'vue'
Vue.set(this.arr,0,'a')
那么Vue数据响应式是如何实现的呢?
二.响应式底层原理
实现数据的双向绑定有几个重要的部分:
- 数据代理
- 数据劫持
- 模板解析
1.数据代理 =>简化对组件对象中data中属性的操作(读/写)
vm(Vue的实例对象)如果想要访问data的数据需要this._data.xxx,但是如果每个数据都这样访问太麻烦了,而Vue中是可以通过{{xxx}}或者this.xxx的形式直接访问到data中的数据,这里就使用到了数据代理
this.data.xxx => this.xxxx
数据代理原理:
<script>
//原先的数据
let vm_data = {
x: 100
}
//代理者->实现数据代理后直接访问代理者就能直接访问到vm_data中的数据
let vm_self = {}
Object.defineProperty(vm_self, 'x', {
//读取调用
get() {
return vm_data.x
},
//修改调用
set(value) {
vm_data.x = value
}
})
console.log(vm_self.x); //100
console.log(vm_self.x = 200); //200
</script>
2.数据劫持 => 实现数据双向绑定
M:data
VM:在observer(一个监视函数)中,通过object.defineProperty为data中每个层次的属性重写get,set方法,并且为每个属性创建dep对象
get:建立dep与watcher的联系
set:数据发生改变去更新界面
V:在模板解析时,为每个节点创建watcher对象
- this.msg='abc
- 由于数据代理 data.msg变成了'abc'
- 由于数据劫持 创建dep和watcher的关系 ,通过对应的dep去通知所有的watcher去更新节点
3.模板解析
在实例化Vue时会传入一个配置对象,配置对象中包括el,data....其中el就是将对应节点传入Vue中,Vue底层进行遍历所有节点,应为节点内还会有子节点,这其中需要递归调用,遍历所有的节点属性,如果节点属性中有v-开头,那就是Vue中的指令语法,再查看节点属性有无on开头,有就是原生的指令,通过调用方法对截取的指令通过Dom2的addeventListener('事件名',{})实现是事件的绑定
<div id='app'>
<div v-on:click='test' />
<div onClick='test' />
</div>
//实例化Vue
new Vue({
el:'#app',
data(){
return{}
},
methods:{test(){}}
})
三.扩展
v-model实现数据双向绑定的本质
将data中的数据渲染到input的value上,通过input标签上的onchange方法实现data中的值改变,从而实现v-model的数据双向绑定.