data为什么是函数,MVVM模型,数据代理

本文探讨了Vue中为何将data设为函数,通过实例对比对象和函数在创建实例时数据独立性的差异,以及MVVM模型(Model-View-ViewModel)在Vue中的应用。重点讲解了数据代理(如`Object.defineProperty`)在实现响应式数据更新中的作用,以及如何使用`Vue.set`处理复杂数据结构的变更。

1.data为什么要是函数?

首先可以通过下列代码来展示部分,其中区别在于,构造函数定义时,会在内部的data设置一个值,这个值是对象类型,在栈中时存储着一个指向内存中该对象的堆的地址,新建一个实列对象时,要获取函数中的data,也就是获取那个堆中的地址,再次创建实例对象时,获取的也是这个地址,但是这个地址指向的数据是同一个,也就是{count:0},所以想要改变其中一个的data时,就要对存储地址进行变动,但是所有的实列都是按照地址寻找值,改变一个其他的也会改变。

所以data属性声明值,必须使用函数类型,这样每次创建实例对象时都能获得他们自己的一个对象值,对应堆的地址不相同,所以互不影响。

该部分代码展示了创建实例对象后的寻找堆的地址时的不同情况。对象类型的每次都会同时改变,但是函数类型的互不影响。

<body>
		先用vue来区别data是函数的原因
		<div id="app">
			<cpn1></cpn1>
			<cpn1></cpn1>
			<cpn1></cpn1>
			<hr>
			<cpn2></cpn2>
			<cpn2></cpn2>
			<cpn2></cpn2>
		</div>
		
		<script src="vue.js"></script>
		<template id="cpn1"> <!-- template中只能暴露一个div -->
			<div>
				<button @click="count++">+</button>
					<h3>{{count}}</h3>
				<button @click="count--">-</button>
			</div>

			
		</template>
		<template id="cpn2">
			<div>
				<button @click="count++">+</button>
					<h3>{{count}}</h3>
				<button @click="count--">-</button>
			</div>
		</template>
		
		<script type="text/javascript">
			var obj = {count:0};
			const app = new Vue({
				el:'#app',
				data:{
				
				},
				methods:{
					
				},
				components:{
					cpn1:{
						template:'#cpn1',//挂载
						data(){
							return obj;
						}
					},
					cpn2:{
						template:'#cpn2',
						data(){
							return{
								count:0
							}
						}
					}
				}//简单的说  这里涉及的问题是于data存储的数据的地址有关,若是一个对象,他指向的一直都是该地址,是函数的情况是会不断的新建立一个地址	
			})
			
			
		</script>
		
		
	</body>

下面的代码同样是展示堆地址的不同。

在使用复用型组件时,声明data属性的值时,必须要使用函数类型   每次创建实例对象时,他们都是获取属于他们自己的一个对象值,并且对应的堆中的地址都不相同,所以互不影响。

创建实例对象data是对象时,改变一个所有结果都会改变。

创建实例对象data是函数时,改变其中一个不会影响其他的。

<body>
		进一步确定data的函数性质
		<div></div>
	<script type="text/javascript">
		
		var obj = {//此处是堆,用来存放data的数据地址,不会应为实例对象的创建二改变给它的地址信息。
				name:'张三',
				age:20
		}
		function box1(){
			return obj;
		}
		function box2(){
			return{
				name:'张三',
				age:20
			}
		}
		var a1 = box1();
		var b1 = box1();
		var c1 = box1();
		a1.name = '李四'
		console.log(a1);//{name: '李四', age: 20}
		console.log(b1);//{name: '李四', age: 20}
		console.log(c1);//{name: '李四', age: 20}
		//都发生改变
		var a2 = box2();
		var b2 = box2();
		var c2 = box2();
		a2.name = '李四'
		console.log(a2);//{name: '李四', age: 20}
		console.log(b2);//{name: '张三', age: 20}
		console.log(c2);//{name: '张三', age: 20}
		//三个结果共出现三个地址位置,从而互不影响
		//在使用复用型组件时,声明data属性的值时,必须要使用函数类型   每次创建实例对象时,他们都是获取属于他们自己的一个对象值,并且对应的堆中的地址都不相同,所以互不影响。
	</script>	
	
	</body>

2.MVVM模型

M:数据层(model)应用数据以及逻辑,主要指从后端获取的数据,指数据这里

代码中的数据data就是数据层。

V: 视图层(View):页面UI组件,主要由 HTML 和 CSS 来构建

div标签id是app的就是视图层 

VM:视图数据模型(ViewModel):数据与视图关联起来,数据和 DOM 已经建立了关联,是响应式的,使编程人员脱离复杂的界面操作

const vm = new Vue()即为试图数据模型。这里是VM层   ViewModel主要功能是实现数据双向绑定:
        数据变化后更新视图,既:model有数据更新时UI组件会响应变化
        视图变化后更新数据,界面上如果有input输入框,输入数据时,model中的数据也会更新

实际上vue并不是一个完整的MVVM的模型,是借用了这样的思维。

<body>
		
		<div id="app">
			<!-- // 视图层(View):页面UI组件,主要由 HTML 和 CSS 来构建 -->
			<h3>{{name}}</h3>
			<h3>{{age}}</h3>
		</div>
		
		<script src="vue.js"></script>
		<script type="text/javascript">
		//实际上vue并不是一个完整的MVVM的模型,是借用了这样的思维
		
		
		// 视图数据模型(ViewModel):数据与视图关联起来,数据和 DOM 已经建立了关联,是响应式的,使编程人员脱离复杂的界面操作
			const vm = new Vue({
		//这里是VM层   ViewModel主要功能是实现数据双向绑定:
		// 数据变化后更新视图,既:model有数据更新时UI组件会响应变化
		// 视图变化后更新数据,界面上如果有input输入框,输入数据时,model中的数据也会更新
				el:'#app',
				// 数据层(Model):应用数据以及逻辑,主要指从后端获取的数据,指数据这里
				data(){
					return{
						name:"zhangsan",
						age:20
					}
				}
				
			})
			
			
		</script>
		
		
	</body>

3.数据代理object.defineProperty

vue2 利用object.defineProperty劫持对象或对象属性的访问器,在属性值发生变化时获取属性值变化,从而进行后续操作,这就是 vue的底层运行逻辑   响应式原理。

object.defineProperty方法直接在一个对象上定义一个新属性,或者修改一个已存在的属性,并返回这个对象

value设置属性的值 

wtitable:值是否可以重写。true/flase

enumerable:目标属性是否可以被枚举。true/flase 

configurable:目标属性是否可以被删除,或是否可以再次修改特性

代码中有相应注释

<body>
		<!-- vue2 利用object.defineProperty劫持对象或对象属性的访问器,在属性值发生变化时获取属性值变化,从而进行后续操作 -->
		<!-- vue的底层运行逻辑   响应式原理-->
		<!-- object.defineProperty方法直接在一个对象上定义一个新属性,或者修改一个已存在的属性,并返回这个对象 -->
		<!-- value设置属性的值 -->
		<!-- wtitable:值是否可以重写。true/flase -->
		<!-- enumerable:目标属性是否可以被枚举。true/flase -->
		<!-- configurable:目标属性是否可以被删除,或是否可以再次修改特性 -->
		<div id="app">web</div>
		
		<script type="text/javascript">
			// var a = {name:'zhangsan'};
			// var b = {age:25};
			// Object.defineProperty(a,'age',{
			// 	get(){
			// 		return b.age
			// 	},
			// 	set(val){
			// 		b.age = val
			// 	}
			// })
			// a.age = 30
			// console.log(b);//{age: 30},通过a来修改b
			// ------------------------------------------------------
			// vue中的格式
			// const vm = new Vue({
			// 	el:'#app',
			// 	data(){
			// 		return{
			// 		}
			// 	},
			// 	methods:{
			// 	}
			// })
			// -------------响应式原理写法----------------------
			// let data = {
			// 	msg: '张三'
			// }//这个代表上面的数据data
			// let vm = {}   //这个代表上面的vm,试图数据模型
			// Object.defineProperty(vm,'msg',{
			// 	// enumerable:true,//可枚举的,这里可以不加但是后续的需要加上
			// 	// configurable:true,//可以被删除或再改一次
				
			// 	get(){
			// 		return data.msg
			// 	},
			// 	set(newval){
			// 		data.msg = newval;
			// 		document.querySelector("#app").textContent = newval
			// 	}
			// })
			
			// vm.msg = "你好,响应式原理"
			//改变了页面的渲染效果了
			
			let data = {
				msg:'Hello Vue',
				count:0
			}
			
			let vm = {}
			function defineProperty(){
				// Object.keys()//对象变成数组,值为对象的键值
				// Object.value()//对象的值变为数组
				 Object.keys(data).forEach(key=>{//遍历对象的写法,对象不是一个值,多个的新情况需要加上【】来指定
					 Object.defineProperty(vm,key,{
						enumerable:true,//可枚举的
						configurable:true,//可以被删除或修改一次
						get(){
							return data[key];//这里的中括号是指别的,js中动态属性以中括号的形式包起来,这里的key可能是msg有可能是count
						},
						set(newval){
							data[key] = newval;
							document.querySelector("#app").textContent = newval
						}
					 })
				 })
			}
			defineProperty(data)
			// vm.msg = "你好,响应式原理"
			vm.count = 11111111
			//这就是底层原理,vue2的响应式原理,主要通过object.de.....进行数据代理来操作vm去改变data的值。
			// 优势:无需显示调用,只要数据发生变化直接通知变化并驱动试图更新
			// 2.在set函数中精确得知变化数据,而不用逐个遍历属性获取变化值,减少性能消耗
			// Object.defineProperty()可以监测到属性的获取,修改,但是新增,删除监测不到。
		</script>
	</body>

Object.defineProperty的缺点,改变数值不能发生响应式变化。这里和数组的操作是一致的。通过几个修饰词改变。

<body>
		<div id="app">
		<!-- 	<div @click="qq">
				{{isAgree.msg}}
			</div>
			<div>
				{{isAgree.ww}}
			</div>
			<div>
				{{isAgree.qq}}
			</div> -->
			
			<h2 v-for="(item,index) in isAgree.web" :key="index">{{item}}</h2>
			<button @click="updateweb">点击</button>
			
		</div>
		<script src="vue.js"></script>
		<script type="text/javascript">
			const vm = new Vue({
				el:'#app',
				data(){
					return{
						isAgree:{
							msg:'zhangsan',
							web:['html','js']
						}
					}
				},
				methods:{
					qq(){
						// this.isAgree.ww = 'list';//直接添加无效
						// console.log(this.isAgree.ww);//list  改变了,但是没有响应式反应
						// vue.set(目标,属性名)向响应式对象中添加一个propert,并确保这个新的property同样是响应式的,且触发试图更新
						Vue.set(this.isAgree,'ww','list');
						//this.$set(this.isAgree,'qq','4');//两个都行
						Vue.delete(this.isAgree,'msg');
						// this.$delete(this.isAgree,'ww')
						
						// delete this.isAgree.msg;//直接删除无效
						// console.log(this.isAgree.msg);//删除了
					},
					updateweb(){
						//Object.de....是操作数组的
						this.isAgree.web[0] = 'lili'
						console.log(this.isAgree.web[0]);
						
					}
				},
				computed:{
					
				},
				watch:{
					
				}
				
			})
			
			//在组件的标签上加上native才能点击监听组件事件
			
		</script>
		
	</body>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值