源码
话不多说,直接放出源码。这里的整体代码也不多,我基本也已做了注释。
// 对于process.env.NODE_ENV !== 'production'可以忽略掉,都是开发时候的警告提示处理
function proxy (target: Object, sourceKey: string, key: string) {
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
// Component 当前初始化的组件
function initData (vm: Component) {
// 拿到options.data
let data = vm.$options.data
// 判断是否为函数,是则拿到返回结果,否则直接使用 》 data || {}
// getData(data, vm): 拿到data对象的返回结果
data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}
// isPlainObject 是否为一个原生对象,否则将data = {}
if (!isPlainObject(data)) {
data = {}
process.env.NODE_ENV !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
)
}
// proxy data on instance
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
// 检查options.methods与data是否有重名方法
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(
`Method "${key}" has already been defined as a data property.`,
vm
)
}
}
// 检查options.props与data是否有重名方法
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
// isReserved: 非 $和_开头
// 将data里面的数据通过Object.defineProperty代理到vm上 vm._data.aa 可以vm.aa拿到
proxy(vm, `_data`, key)
}
}
// observe data
// 响应式数据初始
// asRootData: 是否为根数据
observe(data, true /* asRootData */)
}
疑问
这是我看这段源码中的疑问,答出来,我觉得这段代码就可以算基本理解了。
Q、为什么data是一个函数
- 因为组件的一个可复用特性,使得可以存在多个引用,而使用return出去可以保证每个组件都不互相影响,如果直接定义data: {…},则修改a处,会导致b一起被修改
Q、为什么调用proxy函数代理
- 方便开发者,可以通过vm直接访问、修改操作,最终映射到 vm._data[key] 里面
// 非代理
vm.data.aaa
// 代理后
vm.aaa
- 再次有个细节 isReserved 判断了非 $ 和 _ 开头的变量,避免和vm的内部变量方法冲突
Q、最后一行observe的作用
- 调用此函数,初始化数据响应式监听
- 具体流程可以参数我之前的博客,实现了简易版的响应式数据,基本流程跟vue源码中是一样的。
- 实现vue2.0响应式原理