一.概念及使用
响应式:
* 当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的数据双向绑定.