# Vue 中数据监视和数据代理浅识
# 实现原理:
# data
- 类型:Object | Function
- 限制:组件的定义只接受 function。
- 详细:
Vue 实例的数据对象。Vue 会递归地把 data 的 property 转换为 getter/setter,从而让 data 的 property 能够响应数据变化。对象必须是纯粹的对象 (含有零个或多个的 key/value 对):浏览器 API 创建的原生对象,原型上的 property 会被忽略。大概来说,data 应该只能是数据 - 不推荐观察拥有状态行为的对象。
一旦观察过,你就无法在根数据对象上添加响应式 property。因此推荐在创建实例之前,就声明所有的根级响应式 property。
实例创建之后,可以通过 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> |
当你把一个普通的 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,从而使它关联的组件重新渲染。
后面有时间再手写一次吧,最近好忙,嘻嘻