# Vue 中数据监视和数据代理浅识

# 实现原理:

# data

  • 类型:Object | Function
  • 限制:组件的定义只接受 function。
  • 详细

Vue 实例的数据对象。Vue 会递归地把 data 的 property 转换为 getter/setter,从而让 data 的 property 能够响应数据变化。对象必须是纯粹的对象 (含有零个或多个的 key/value 对):浏览器 API 创建的原生对象,原型上的 property 会被忽略。大概来说,data 应该只能是数据 - 不推荐观察拥有状态行为的对象。

一旦观察过,你就无法在根数据对象上添加响应式 property。因此推荐在创建实例之前,就声明所有的根级响应式 property。

实例创建之后,可以通过 vm.data访问原始数据对象。Vue实例也代理了data对象上所有的property,因此访问vm.a等价于访问vm.data 访问原始数据对象。Vue 实例也代理了 data 对象上所有的 property,因此访问 vm.a 等价于访问 vm.data.a。

以 _ 或 $ 开头的 property 不会被 Vue 实例代理,因为它们可能和 Vue 内置的 property、API 方法冲突。你可以使用例如 vm.$data._property 的方式访问这些 property。

当一个组件被定义,data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。

如果需要,可以通过将 vm.$data 传入 JSON.parse (JSON.stringify (...)) 得到深拷贝的原始数据对象。

var data = { a: 1 } 
// 直接创建一个实例 
var vm = new Vue({data: data }) 
vm.a // => 1 vm.$data === data // => true
// Vue.extend () 中 data 必须是函数 
var Component = Vue.extend({
  data: function () {
    return { a: 1 }
  } 
})

注意,如果你为 data property 使用了箭头函数,则 this 不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问。data: vm => ({a: vm.myProp})

# 理解:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>姓名案例_计算属性实现</title>
		<!-- 引入Vue -->
		<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js""></script>
	</head>
	<body>
		<!-- 
			计算属性:
					1.定义:要用的属性不存在,要通过已有属性计算得来。
					2.原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
					3.get函数什么时候执行?
								(1).初次读取时会执行一次。
								(2).当依赖的数据发生改变时会被再次调用。
					4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
					5.备注:
							1.计算属性最终会出现在vm上,直接读取使用即可。
							2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
		 -->
		<!-- 准备好一个容器-->
		<div id="root">
			姓:<input type="text" v-model="firstName"> <br/><br/>
			名:<input type="text" v-model="lastName"> <br/><br/>
			测试:<input type="text" v-model="x"> <br/><br/>
			全名:<span><!--swig0--></span> <br/><br/>
			<!-- 全名:<span><!--swig1--></span> <br/><br/>
			全名:<span><!--swig2--></span> <br/><br/>
			全名:<span><!--swig3--></span> -->
		</div>
	</body>
	<script type="text/javascript">
		Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
		const vm = new Vue({
			el:'#root',
			data:{
				firstName:'张',
				lastName:'三',
				x:'你好'
			},// 属性
			methods: {
				demo(){
					
				}
			},
			computed:{
				fullName:{
					//get 有什么作用?当有人读取 fullName 时,get 就会被调用,且返回值就作为 fullName 的值
					//get 什么时候调用?1. 初次读取 fullName 时。2. 所依赖的数据发生变化时。
					get(){
						console.log('get被调用了')
						//console.log (this) // 此处的 this 是 vm
						return this.firstName + '-' + this.lastName// 他需要这两个依赖数据
					},
					//set 什么时候调用?当 fullName 被修改时。
					set(value){
						console.log('set',value)
						const arr = value.split('-')
						this.firstName = arr[0]
						this.lastName = arr[1]
					}
				}
			}
		})
	</script>
</html>

img

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。这里需要注意的是不同浏览器在控制台打印数据对象时对 getter/setter 的格式化并不同,所以建议安装 vue-devtools 来获取对检查数据更加友好的用户界面。

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把 “接触” 过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

后面有时间再手写一次吧,最近好忙,嘻嘻