【Vue全解3】Vue的data代理和数据响应式

975 阅读2分钟

目录

  • 系列文章
  • 数据响应式
  • 小实验
  • getter/setter
  • Object.defineProperty
  • 代理和监听
  • 小结:vue对data做了什么
  • this.$set和Vue.set
  • 数组的变异方法
  • 小结:数组的变异方法

议题:Vue对option的data做了什么?


一、系列文章

【Vue 全解 0】Vue 实例

【Vue 全解 1】构造选项 options 之 data

【Vue 全解 2】Vue 模板语法摘要

【Vue 全解 3】Vue 的 data 代理和数据响应式

【Vue 全解 4】options 之生命周期钩子(created、mounted、updated、destroyed)

【Vue 全解 5】options 之 DOM(el、template、render)

【Vue 全解 6】options 之资源(directive、filter、components)和修饰符

【Vue 全解 7】options 之组合(mixin、extends、provide/inject)

【Vue 全解 8】Vue 表单输入绑定 v-model

二、数据响应式

响应式就是做某种刺激作出反应,而数据响应式就是对数据的改变作出响应。在Vue中,数据的响应就是意味着数据变化,Vue就会进行一次render()渲染。以此来更新UI界面。-- 个人理解

  • Vue的数组响应式
  • 我如果修改vm.n,那么UI里面的n就会响应我,Vue2通过Object.defineProperty来实现数据响应式

const vm=new Vue({
	/* 对n进行改变 */
	data:{
		n:0
	}
})

三、小实验

  • data变了?
/* 引入完整Vue */
import Vue from "vue/dist/vue.js";
const myData={
	n:0,
}
console.log(myData);	//第一次打印myData
const vm=new Vue({
	data:myData,
	template:`
		<div>
			{{n}}
			<button @click='add'>+20</button>
		</div>
	`,
	methods:{
		add(){
			this.n+=20;
		}
	}
}).$mount('#app');
/* 设置定时器,功能是让n值+10 */
setTimeout(() => {
	myData.n+=10;
	console.log(myData);	//第二次打印myData
	console.log(vm);
}, 3000);
  1. 程序运行结果截图--展示myData的变化
  • 第一次打印myData
  • 第二次打印myData

  1. 结论:一开始是{n:0},传给new Vue之后立马变成{n:(...)}。可以看见,两次打印出来的myData不一样!其中的原因是什么呢?接下来让我们了解一下ES6的新语法getter/setter,通过这个新语法,能让我们有进一步的认识。

四、getter/setter

  • getter
let obj={
	姓:'高',
	名:'圆圆',
	age:18,
	get 姓名(){
		return this.姓+this.名;
	}
}
console.log('姓名是:'+obj.姓名);	//不用括号也能打印出姓名,这就是getter
  • setter
let obj={
	姓:'高',
	名:'圆圆',
	age:18,
	get 姓名(){
		return this.姓+this.名;
	},
	set 姓名(name){
		this.姓=name[0];	//只考虑第一个字为姓
		this.名=name.substring(1);
	}
};
obj.姓名='袁姗姗';
console.log(`姓名为:${obj.姓名}`);

理解data为什么不一样了吗?并没有!我也是这样认为的,让我们接下来再研究一个东西吧!


五、Object.defineProperty

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象,即在对象已经定义完成后,后面手动添加getter/setter时使用。

  • 作用
    1. 可以给对象添加属性
    2. 可以给对象添加getter/setter
    3. getter/setter用于对属性的读写和监控
  • 语法

    Object.defineProperty(obj,prop,descriptor)

    1. obj是要修改的对象
    2. prop是定义或修改的属性名或symbol
    3. descriptor是要定义或修改的描述符
  • 代码示例
const object1 = {};
let value=0;
Object.defineProperty(object1, 'property1', {
  get (){
	  return value;
  };
  set (num){
	  value=num;
  }
});

坑预警:get/set函数不需要写属性名,get返回时不能写this.property1,这样会造成死循环。一般是使用外部变量来做中间过渡


六、代理和监听

  • 代理

    例如对myData的对象的属性读写,全权由另外一个对象vm对象负责(Vue构造出来的),那么vm就是myData的代理。

  • 监听

    在Vue创建之后,data里面的值就会被Vue构造函数进行监听。只要监听的数据改变了。Vue就知道数据发生了改变,将会通过一系列操作去更新UI界面

  • 使用代理的代码示例
let data={proxy({data:{n:0}})};
/* 参数使用的为解构赋值 */
function proxy({data}) {
	let value=data.n;
	Object.defeinProperty(data,'n',{
		get (){
			return value;
		};
		set (newValue){
			if(newValue<0)return;	//实现一个门槛,n值小于0就不能设置n值
			value=newValue;
		}
	})
	/* 上面的代码就会监听data */
	const obj={};
	Object.defeinProperty(obj,'n',{
		get (){
			return data.n;
		}
		set (){
			data.n=value;
		}
	})
}
console.log("data.n:"+data.n);
data.n=-1;
console.log("data.n:"+data.n+'设置-1失败!');
data.n=1;
console.log("data.n:"+data.n+"设置1成功!");
  • 下面的代码看着眼熟吗?
let data5=proxy({data:{
	n:0
}})
const vm=new Vue({
	data:{
		n:0
	}
})

七、小结:vue对data做了什么

终于将刚开始的议题解释了,接下来是我的个人小结

  • const vm=new Vue({data:myData)
    1. 会让vm成为mydata的代理(proxy)
    2. 会对myData的所有属性进行监控
    3. 为什么要监控? 这不废话吗,Vue只有知道了那些数据变化才能及时的更新页面啊!就是render(data)啊

八、this.$set和Vue.set

Vue 里面肯定是简化了原生的Object.defineProperty的。他们就是两个API:this.$set和Vue.set

  • this.$set和Vue.set是等价的
  • 作用
    1. 新增key
    2. 自动创建代理和监听(前提是没有创建过)
    3. 触发UI更新(但并不会立即更新)
new Vue({
	data:{
		obj:{
			n:0
		}
	},
	methods:{
		setVal(){
			Vue.set(this.obj,'b',1);	//添加obj里面一个为'b'的属性,值为1
			this.$set(this.obj,'c',2);	//添加obj里面一个为'c'的属性,值为2
		}
	}
})

九、数组的变异方法

那么问题来了,如果data里面有数组怎么办?数组里面直接定义好所有key后,你对数组作出增加key的操作Vue是不会知道的。

  • 代码示例
import Vue from 'vue/dist/vue.js';
new Vue({
	data:{
		arr:['a','b','c'],
	},
	template:`
		<div>
			{{arr}}
			<button>set d</button>
		</div>
	`,
	methods:{
		setD(){
			// this.arr[3]='d';	//页面中并不会显示d
			this.$set(arr,3,"d");	//这样设置是可以的。
			this.arr.push('e');	//这样也可以设置。
		}
	}
})

注意:Vue里面数组的push、pop等操作并不是数组原生自带的。而是被Vue的作者尤雨溪篡改了的。就是在原型链上自己封装新的push、pop等常用函数API

  • 图示证明


十、小结:数组的变异方法

  • 对象中新增的key
    1. Vue 没有办法事先监听和代理
    2. 使用this.$set或Vue.set来新增key,创建监听和代理,更新UI
    3. 最好提前将属性都写出来,不要新增key
  • 数组中新增的key
    1. 可以用this.$set和Vue.set来新增key
    2. Vue的作者对数组的常用API进行了篡改,我们可以直接进行操作。并且这些API会自动处理监听和代理。
    3. 数组新增key最好通过7个篡改的API