赞
踩
经过上篇简单实现vue响应式原理(如果打不开可以看这个)之后,
我们已经简略地了解了响应式是怎么通过发布-订阅模式来实现的,这篇主要从源码分析Vue对响应式原理的实现。
在vue源码入口文件vue/src/core/index.js中,可以看到import Vue from './instance/index'
,导入了Vue这个对象。
在vue/src/core/instance/index.js中,
import {
initMixin } from './init'
//...
function Vue (options) {
//...
this._init(options)
}
initMixin(Vue)
// ...
export default Vue
可以看到Vue是一个函数方法,调用该方法时会调用一个叫_init的初始化方法,并传入options参数,同时这个文件还执行了initMixin方法。
在vue/src/core/instance/init.js中,
// ... import { initState } from './state' import { extend, mergeOptions, formatComponentName } from '../util/index' export function initMixin (Vue: Class<Component>) { Vue.prototype._init = function (options?: Object) { const vm: Component = this // ... // merge options if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options) } else { vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || { }, vm ) } // ... initState(vm) // ... } }
看到_init方法就是在initMixin方法中定义的,在_init方法中,声明了常量vm并赋值当前实例,接受了options并做了处理,还调用了initState方法。
本篇我们不对options做深入研究,但是要知道实例化Vue时传入的对象参数可以在这里取到。
在vue/src/core/instance/state.js中,
import { set, del, observe, defineReactive, toggleObserving } from '../observer/index' export function initState (vm: Component) { // ... const opts = vm.$options // ... if (opts.data) { initData(vm) } else { observe(vm._data = { }, true /* asRootData */) } // ... }
当实例化Vue,如
new Vue({
el: '#app',
data: {
text: 'hello world',
}
});
传入的data就是这里的opts.data,当它存在时,调用initData,否则调用observe方法,并初始化_data属性为空对象。
observe方法我们后面会再讲,上面我们已经找到了initData方法,来看一下。
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {
}
}
声明data对象,赋值为vm.$options.data。
当data为函数时调用getData(data, vm),结果赋值给data和vm._data。否则判断data是否存在,存在就将vm._data设置为data,不存在则讲data和vm._data都设为空对象。
export function getData (data: Function, vm: Component): any { // #7573 disable dep collection when invoking data getters pushTarget() try { return data.call(vm, vm) } catch (e) { handleError(e, vm, `data()`) return { } } finally { popTarget() } }
Dep.target = null
const targetStack = []
export function pushTarget (target: ?Watcher) {
targetStack.push(target)
Dep.target = target
}
export function popTarget () {
targetStack.pop()
Dep.target = targetStack[targetStack.length - 1]
}
可以发现实际上getData主要就是执行并返回了data.call(vm, vm)
,当data为函数时,将data方法的this指向当前vue实例,调用data方法并传入该vue实例。
接着往下
function initData (vm: Component) { // ... // 判断data是否为一个Object实例,如果不是,赋值为空对象,并在非生产环境报警告:data函数返回的值必须是一个object 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 ) } // ... }
export function isPlainObject (obj: any): boolean {
return _toString.call(obj) === '[object Object]'
}
继续往下
function initData (vm: Component) { // ... // 获取属性名数组 const keys = Object.keys(data) // 获取props const props = vm.$options.props // 获取methods const methods = vm.$options.methods // 遍历属性 let i = keys.length while (i--) { const key = keys[i] // 非生产环境,方法名和属性名重名警告 if (process.env.NODE_ENV !== 'production') { if (methods && hasOwn(methods, key)) { warn( `Method "${ key}" has already been defined as a data property.`, vm ) } } // 非生产环境,props和属性名重名警告 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)) { // 过滤 $ 或者 _开头的属性,将其余属性代理到实
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。