简单回顾一下这个系列的前两节,前两节花了大篇幅讲了vue在初始化时进行的选项合并。选项配置是vue实例化的第一步,针对不同类型的选项,vue提供的丰富选项配置策略以保证用户可以使用不同丰富的配置选项。而在这一节中,我们会分析选项合并后的又两步重要的操作: 数据代理和关联子父组件关系,分别对应的处理过程为initProxy和initLifecycle。这章节的知识点也为后续的响应式系统介绍和模板渲染做铺垫。
2.1 Object.defineProperty和Proxy
在介绍这一章的源码分析之前,我们需要掌握一下贯穿整个vue数据代理,监控的技术核心:Object.defineProperty 和 Proxy
2.1.1 Object.defineProperty
官方定义:Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。 基本用法: Object.defineProperty(obj, prop, descriptor)
我们可以用来精确添加或修改对象的属性,只需要在descriptor中将属性特性描述清楚,descriptor的属性描述符有两种形式,一种是数据描述符,另一种是存取描述符。
数据描述符
- configurable:数据是否可删除
- enumerable:属性是否可枚举
- value:属性值,默认为undefined
- writable:属性是否可读写
存取描述符
- configurable:数据可改变
- enumerable:可枚举
- get:一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。
- set:一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。
注意: 数据描述符的value,writable 和 存取描述符的get, set属性不能同时存在,否则会抛出异常。 有了Object.defineProperty方法,我们可以方便的利用存取描述符中的getter/setter来进行数据监听,在get,set钩子中分别做不同的操作,这是vue双向数据绑定原理的雏形,我们会在响应式系统的源码分析时具体阐述。
var o = {}
var value;
Object.defineProperty(o, 'a', {
get() {
console.log('获取值')
return value
},
set(v) {
console.log('设置值')
value = v
}
})
o.a = 'sss'
// 设置值
console.log(o.a)
// 获取值
// 'sss'
然而Object.defineProperty的get和set方法只能观测到对象属性的变化,对于数组类型的变化并不能检测到,这是用Object.defineProperty进行数据监控的缺陷,而vue中对于数组类型的方法做了特殊的处理。 es6的proxy可以完美的解决这一类问题。
2.1.2 Proxy
Proxy 是es6的语法,和Object.defineProperty一样,也是用于