初始化状态
前三个初始化步骤,initProps、initSetup、initMethods,这几个步骤暂不分析 本文主要分析 initData、initComputed、initWatch
1. initData
- initData 内部主要调用了 observe 方法,将 data 对象转换成响应式对象
- observe 内部主要调用了 Observer 类,将 data 对象转换成响应式对象
- Observer 类内部主要调用了 defineReactive 方法,将 data 对象转换成响应式对象
js
function initData(vm: Component) {
let data: any = vm.$options.data
data = vm._data = isFunction(data) ? getData(data, vm) : data || {}
if (!isPlainObject(data)) {
data = {}
__DEV__ &&
warn(
'data functions should return an object:\n' +
'https://v2.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]
if (__DEV__) {
if (methods && hasOwn(methods, key)) {
warn(`Method "${key}" has already been defined as a data property.`, vm)
}
}
if (props && hasOwn(props, key)) {
__DEV__ &&
warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
proxy(vm, `_data`, key)
}
}
// observe data
const ob = observe(data)
ob && ob.vmCount++
}
/**
* Attempt to create an observer instance for a value,
* returns the new observer if successfully observed,
* or the existing observer if the value already has one.
*/
export function observe(
value: any,
shallow?: boolean,
ssrMockReactivity?: boolean
): Observer | void {
if (value && hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
return value.__ob__
}
if (
shouldObserve &&
(ssrMockReactivity || !isServerRendering()) &&
(isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value.__v_skip /* ReactiveFlags.SKIP */ &&
!isRef(value) &&
!(value instanceof VNode)
) {
return new Observer(value, shallow, ssrMockReactivity)
}
}
export class Observer {
dep: Dep
vmCount: number // number of vms that have this object as root $data
constructor(public value: any, public shallow = false, public mock = false) {
// this.value = value
this.dep = mock ? mockDep : new Dep()
this.vmCount = 0
def(value, '__ob__', this)
if (isArray(value)) {
if (!mock) {
if (hasProto) {
/* eslint-disable no-proto */
;(value as any).__proto__ = arrayMethods
/* eslint-enable no-proto */
} else {
for (let i = 0, l = arrayKeys.length; i < l; i++) {
const key = arrayKeys[i]
def(value, key, arrayMethods[key])
}
}
}
if (!shallow) {
this.observeArray(value)
}
} else {
/**
* Walk through all properties and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
const keys = Object.keys(value)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
defineReactive(value, key, NO_INITIAL_VALUE, undefined, shallow, mock)
}
}
}
/**
* Define a reactive property on an Object.
*/
export function defineReactive(obj: object, key: string, val?: any, customSetter?: Function | null, shallow?: boolean, mock?: boolean, observeEvenIfShallow = false) {
// 处于闭包中的dep
const dep = new Dep();
const property = Object.getOwnPropertyDescriptor(obj, key);
if (property && property.configurable === false) {
return;
}
// cater for pre-defined getter/setters
const getter = property && property.get;
const setter = property && property.set;
if ((!getter || setter) && (val === NO_INITIAL_VALUE || arguments.length === 2)) {
val = obj[key];
}
let childOb = shallow ? val && val.__ob__ : observe(val, false, mock);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
// 在挂载阶段,render函数内部对state的访问会调用getter,此时会收集依赖
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val;
if (Dep.target) {
if (__DEV__) {
dep.depend({
target: obj,
type: TrackOpTypes.GET,
key
});
} else {
// 在这里收集依赖每个相应式属性对应的dep收集渲染watcher
dep.depend();
}
if (childOb) {
childOb.dep.depend();
if (isArray(value)) {
dependArray(value);
}
}
}
return isRef(value) && !shallow ? value.value : value;
},
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val;
if (!hasChanged(value, newVal)) {
return;
}
if (__DEV__ && customSetter) {
customSetter();
}
if (setter) {
setter.call(obj, newVal);
} else if (getter) {
// #7981: for accessor properties without setter
return;
} else if (!shallow && isRef(value) && !isRef(newVal)) {
value.value = newVal;
return;
} else {
val = newVal;
}
childOb = shallow ? newVal && newVal.__ob__ : observe(newVal, false, mock);
if (__DEV__) {
dep.notify({
type: TriggerOpTypes.SET,
target: obj,
key,
newValue: newVal,
oldValue: value
});
} else {
//在state发生变更时,在这里触发依赖每个相应式属性对应的dep触发渲染watcher
dep.notify();
}
}
});
return dep;
}
2. initComputed
- 内部主要调用了 defineComputed 方法,将 computed 对象转换成响应式对象
- defineComputed 内部主要调用了 createComputedGetter 方法,将 computed 对象转换成响应式对象
js
// 计算属性的watcher配置项, lazy为true,表示计算属性的watcher是懒执行的
const computedWatcherOptions = { lazy: true };
function initComputed(vm: Component, computed: Object) {
// $flow-disable-line
const watchers = (vm._computedWatchers = Object.create(null));
// computed properties are just getters during SSR
const isSSR = isServerRendering();
for (const key in computed) {
const userDef = computed[key];
const getter = isFunction(userDef) ? userDef : userDef.get;
if (__DEV__ && getter == null) {
warn(`Getter is missing for computed property "${key}".`, vm);
}
if (!isSSR) {
// create internal watcher for the computed property.
// 这里为每个计算属性创建一个watcher,这里称作计算属性的watcher(三类watcher中的一种)
watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions);
}
// component-defined computed properties are already defined on the
// component prototype. We only need to define computed properties defined
// at instantiation here.
if (!(key in vm)) {
// 主要是调用了defineComputed方法
defineComputed(vm, key, userDef);
} else if (__DEV__) {
if (key in vm.$data) {
warn(`The computed property "${key}" is already defined in data.`, vm);
} else if (vm.$options.props && key in vm.$options.props) {
warn(`The computed property "${key}" is already defined as a prop.`, vm);
} else if (vm.$options.methods && key in vm.$options.methods) {
warn(`The computed property "${key}" is already defined as a method.`, vm);
}
}
}
}
export function defineComputed(target: any, key: string, userDef: Record<string, any> | (() => any)) {
const shouldCache = !isServerRendering();
// 处理计算属性的get,生成一个包装函数对options.computed[key]进行包装
if (isFunction(userDef)) {
sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : createGetterInvoker(userDef);
sharedPropertyDefinition.set = noop;
} else {
sharedPropertyDefinition.get = userDef.get
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: createGetterInvoker(userDef.get)
: noop;
sharedPropertyDefinition.set = userDef.set || noop;
}
if (__DEV__ && sharedPropertyDefinition.set === noop) {
sharedPropertyDefinition.set = function () {
warn(`Computed property "${key}" was assigned to but it has no setter.`, this);
};
}
// 在这里将计算属性挂载到vm实例上,这样就可以通过this.xxx访问计算属性了
Object.defineProperty(target, key, sharedPropertyDefinition);
}
function createComputedGetter(key) {
// 返回一个包装过的get函数
return function computedGetter() {
const watcher = this._computedWatchers && this._computedWatchers[key];
if (watcher) {
// 脏检查,并重新求值
if (watcher.dirty) {
// 这里会调用watcher的evaluate方法,进而调用watcher的get方法
// 在get方法中将计算watcher入栈,调用计算属性函数,计算属性依赖的dep会收集计算watcher
watcher.evaluate();
// 执行完毕后,将计算watcher出栈,栈顶为渲染watcher
}
if (Dep.target) {
if (__DEV__ && Dep.target.onTrack) {
Dep.target.onTrack({
effect: Dep.target,
target: this,
type: TrackOpTypes.GET,
key
});
}
// 在这里重点是,计算属性本身是不存在dep的,计算属性只有对应的watcher
// 这里调用dep.depend()方法取出计算watcher的deps,对渲染watcher进行依赖收集, 此时栈顶为渲染watcher
watcher.depend();
}
return watcher.value;
}
};
}
3. initWatch
- 内部主要调用了 createWatcher 方法,将 watch 对象转换成响应式对象
- createWatcher 内部主要调用了$watch 方法
- $watch 内部主要调用了 Watcher 类,为每个 watcher 创建一个 watcher 实例
js
function initWatch(vm: Component, watch: Object) {
for (const key in watch) {
const handler = watch[key];
if (isArray(handler)) {
for (let i = 0; i < handler.length; i++) {
createWatcher(vm, key, handler[i]);
}
} else {
createWatcher(vm, key, handler);
}
}
}
function createWatcher(vm: Component, expOrFn: string | (() => any), handler: any, options?: Object) {
if (isPlainObject(handler)) {
options = handler;
handler = handler.handler;
}
if (typeof handler === 'string') {
handler = vm[handler];
}
return vm.$watch(expOrFn, handler, options);
}
export function stateMixin(Vue: typeof Component) {
// flow somehow has problems with directly declared definition object
// when using Object.defineProperty, so we have to procedurally build up
// the object here.
const dataDef: any = {};
dataDef.get = function () {
return this._data;
};
const propsDef: any = {};
propsDef.get = function () {
return this._props;
};
if (__DEV__) {
dataDef.set = function () {
warn('Avoid replacing instance root $data. ' + 'Use nested data properties instead.', this);
};
propsDef.set = function () {
warn(`$props is readonly.`, this);
};
}
Object.defineProperty(Vue.prototype, '$data', dataDef);
Object.defineProperty(Vue.prototype, '$props', propsDef);
Vue.prototype.$set = set;
Vue.prototype.$delete = del;
Vue.prototype.$watch = function (expOrFn: string | (() => any), cb: any, options?: Record<string, any>): Function {
const vm: Component = this;
if (isPlainObject(cb)) {
return createWatcher(vm, expOrFn, cb, options);
}
options = options || {};
options.user = true;
// 这里调用了Watcher类,为每个watcher创建一个watcher实例
const watcher = new Watcher(vm, expOrFn, cb, options);
if (options.immediate) {
const info = `callback for immediate watcher "${watcher.expression}"`;
pushTarget();
invokeWithErrorHandling(cb, vm, [watcher.value], vm, info);
popTarget();
}
// 返回一个unwatch函数,用于取消监听
return function unwatchFn() {
watcher.teardown();
};
};
}
4. $mount
- $mount 方法的作用是把 vue 实例挂载到 dom 上,这里的核心是调用了 mountComponent 方法
- mountComponent 方法的作用是把 vue 实例挂载到 dom 上,这里的核心是调用了 mountComponent 方法
js
Vue.prototype.$mount = function (el?: string | Element, hydrating?: boolean): Component {
el = el && inBrowser ? query(el) : undefined;
return mountComponent(this, el, hydrating);
};
// 函数较长,只保留了核心代码
export function mountComponent(vm: Component, el: Element | null | undefined, hydrating?: boolean): Component {
vm.$el = el;
// 调用beforeMount钩子函数
callHook(vm, 'beforeMount');
// vm._render()方法生成vnode
// vm_update进行diff算法,生成patch方法,patch方法会将vnode转换成真实dom
updateComponent = () => {
vm._update(vm._render(), hydrating);
};
// 渲染watcher才有的属性
const watcherOptions: WatcherOptions = {
before() {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate');
}
}
};
// 这里为每个组件创建一个渲染watcher,这里称作渲染watcher(三类watcher中的一种)
// we set this to vm._watcher inside the watcher's constructor
// since the watcher's initial patch may call $forceUpdate (e.g. inside child
// component's mounted hook), which relies on vm._watcher being already defined
new Watcher(vm, updateComponent, noop, watcherOptions, true /* isRenderWatcher */);
hydrating = false;
// 这里判断是否是首次挂载,如果是首次挂载,调用mounted钩子函数
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true;
callHook(vm, 'mounted');
}
return vm;
}
5. Watcher
-- Watcher 类的核心代码,功能点在下方注释
js
constructor(
vm: Component | null, // 组件实例
expOrFn: string | (() => any), // 渲染函数,或者计算属性函数,或者watch的keypath|keyfunction
cb: Function, // watch的回调函数
options?: WatcherOptions | null, // watch的配置项
isRenderWatcher?: boolean // 是否是渲染watcher
) {
recordEffectScope(
this,
// if the active effect scope is manually created (not a component scope),
// prioritize it
activeEffectScope && !activeEffectScope._vm
? activeEffectScope
: vm
? vm._scope
: undefined
)
if ((this.vm = vm) && isRenderWatcher) {
vm._watcher = this
}
// options
if (options) {
this.deep = !!options.deep
this.user = !!options.user
this.lazy = !!options.lazy
this.sync = !!options.sync
this.before = options.before
if (__DEV__) {
this.onTrack = options.onTrack
this.onTrigger = options.onTrigger
}
} else {
this.deep = this.user = this.lazy = this.sync = false
}
this.cb = cb
this.id = ++uid // uid for batching
this.active = true
this.post = false
this.dirty = this.lazy // 计算属性的dirty, lazy默认为true
this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()
this.expression = __DEV__ ? expOrFn.toString() : ''
// 对渲染函数,或者计算属性函数,或者watch的keypath|keyfunction,进行包装
if (isFunction(expOrFn)) {
this.getter = expOrFn
} else {
this.getter = parsePath(expOrFn)
if (!this.getter) {
this.getter = noop
__DEV__ &&
warn(
`Failed watching path: "${expOrFn}" ` +
'Watcher only accepts simple dot-delimited paths. ' +
'For full control, use a function instead.',
vm
)
}
}
/* 核心解释 */
// 在创建watcher阶段
// 第一轮是计算属性的watcher,由于计算属性的lazy为true,所以不会执行get方法
// 第二轮创建的是渲染watcher,将直接执行get方法,实现依赖收集
// 最后是在$mount期间创建的渲染watcher,将直接执行get方法,进而执行updateComponent方法
// 在updateComponent方法中,会执行vm._render()方法生成vnode
// 在生成vnode的过程中,会访问state,触发state的getter,进而触发state的依赖收集
// 同时也会访问计算属性,触发计算属性的getter,进而触发计算属性的依赖收集
this.value = this.lazy ? undefined : this.get()
}
/**
* Evaluate the getter, and re-collect dependencies.
*/
get() {
// 维护一栈结构栈底为渲染watcher, 在触发getter时,将watcher入栈
pushTarget(this)
let value
const vm = this.vm
try {
// 在初始化阶段,watch watcher的getter,直接执行完成将回调注入到dep中
// 在渲染阶段,首先会执行渲染watcher的getter,会执行vm._render()方法生成vnode,进而在渲染过程中完成整个依赖收集
value = this.getter.call(vm, vm)
} catch (e: any) {
if (this.user) {
handleError(e, vm, `getter for watcher "${this.expression}"`)
} else {
throw e
}
} finally {
// "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
traverse(value)
}
// 依赖收集完成后,将watcher出栈
popTarget()
// 清理依赖
this.cleanupDeps()
}
return value
}
/**
* Add a dependency to this directive.
*/
// 在这里收集依赖每个相应式属性对应的dep收集渲染watcher
addDep(dep: Dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
dep.addSub(this)
}
}
}
/**
* Clean up for dependency collection.
*/
// 在这里对新旧depid进行对比,如果旧的depid不存在于新的depid中,
// 则从的deps中移除当前watcher
cleanupDeps() {
let i = this.deps.length
while (i--) {
const dep = this.deps[i]
if (!this.newDepIds.has(dep.id)) {
dep.removeSub(this)
}
}
let tmp: any = this.depIds
this.depIds = this.newDepIds
this.newDepIds = tmp
this.newDepIds.clear()
tmp = this.deps
this.deps = this.newDeps
this.newDeps = tmp
this.newDeps.length = 0
}
/**
* Subscriber interface.
* Will be called when a dependency changes.
*/
update() {
/* istanbul ignore else */
if (this.lazy) {
// 计算属性的watcher重新被触发,dirty重置为true,表示为脏,在下一轮render时,会重新计算
this.dirty = true
} else if (this.sync) {
// watch watcher被重新触发,同步执行回调
this.run()
} else {
// 渲染watcher被重新触发,将watcher放入队列中,等待下一轮event loop执行
queueWatcher(this)
}
}
/**
* Scheduler job interface.
* Will be called by the scheduler.
*/
// 执行watcher的回调
run() {
if (this.active) {
const value = this.get()
if (
value !== this.value ||
// Deep watchers and watchers on Object/Arrays should fire even
// when the value is the same, because the value may
// have mutated.
isObject(value) ||
this.deep
) {
// set new value
const oldValue = this.value
this.value = value
if (this.user) {
const info = `callback for watcher "${this.expression}"`
invokeWithErrorHandling(
this.cb,
this.vm,
[value, oldValue],
this.vm,
info
)
} else {
this.cb.call(this.vm, value, oldValue)
}
}
}
}
/**
* Evaluate the value of the watcher.
* This only gets called for lazy watchers.
*/
// 计算属性求值,求值完成后,将dirty重置为false
evaluate() {
this.value = this.get()
this.dirty = false
}
/**
* Depend on all deps collected by this watcher.
*/
// 计算watcher, 被dep收集后,取出所有的dep,对渲染watcher进行依赖收集
depend() {
let i = this.deps.length
while (i--) {
this.deps[i].depend()
}
}
/**
* Remove self from all dependencies' subscriber list.
*/
// 从所有依赖的dep中移除当前watcher
teardown() {
if (this.vm && !this.vm._isBeingDestroyed) {
remove(this.vm._scope.effects, this)
}
if (this.active) {
let i = this.deps.length
while (i--) {
this.deps[i].removeSub(this)
}
this.active = false
if (this.onStop) {
this.onStop()
}
}
}
}