本文接着上一节继续来阅读Vue源码初始化内容
关键性代码
import { initProxy } from './proxy'
import { initState } from './state'
import { initRender } from './render'
import { initEvents } from './events'
import { initLifecycle, callHook } from './lifecycle'
import { initProvide, initInjections } from './inject'
import { extend, mergeOptions, formatComponentName } from '../util/index'
export function initMixin (Vue) {
Vue.prototype._init = function (options) {
const vm = this
vm._uid = uid++
vm._isVue = true
if (options && options._isComponent) {
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
vm._renderProxy = vm
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm)
initState(vm)
initProvide(vm)
callHook(vm, 'created')
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
1. hook调用
callHook(vm, 'beforeCreate')
Vue的生命周期大家应该都很熟悉了,主要的事件执行机制可以从这里开始了解。 首先进入该函数。
export function callHook (vm, hook) {
pushTarget()
// 取出在该hook下的所有内容,这会是一个函数数组
const handlers = vm.$options[hook]
const info = `${hook} hook`
// 执行数组中的每一个方法
if (handlers) {
for (let i = 0, j = handlers.length; i < j; i++) {
// 具有错误处理功能的函数调用
invokeWithErrorHandling(handlers[i], vm, null, vm, info)
}
}
// 触发事件
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook)
}
popTarget()
}
注意这行语句
const handlers = vm.$options[hook]
1.1 mergeHook
这显然是挂载在实例options上的内容,可以在src/shared/constants.js中看到LIFECYCLE_HOOKS,里面有我们熟悉的所有钩子函数名称。搜索后可以在src/core/util/options.js中找到相关内容。
LIFECYCLE_HOOKS.forEach(hook => {
strats[hook] = mergeHook
})
function mergeHook (parentVal, childVal) {
const res = childVal
? parentVal
? parentVal.concat(childVal) : Array.isArray(childVal)
? childVal : [childVal] : parentVal
return res ? dedupeHooks(res) : res
}
function dedupeHooks (hooks) {
const res = []
for (let i = 0; i < hooks.length; i++) {
if (res.indexOf(hooks[i]) === -1) {
res.push(hooks[i])
}
}
return res
}
相对来说,就是把hooks都合并到相应名称的数组下
1.2 pushTarget/popTarget
export function pushTarget (target: ?Watcher) {
targetStack.push(target)
Dep.target = target
}
export function popTarget () {
targetStack.pop()
Dep.target = targetStack[targetStack.length - 1]
}
简单说一下这两个函数的作用。Vue响应式最重要的是进行依赖收集。而只有当Dep.target存在时,才会进行依赖收集。hook函数作用时不需要进行,因此pushTarget没有传值,将Dep.target置空即可。
2. initInjections 初始化inject
export function initInjections (vm) {
const result = resolveInject(vm.$options.inject, vm)
if (result) {
// 是否响应式处理,false为不处理
toggleObserving(false)
Object.keys(result).forEach(key => {
defineReactive(vm, key, result[key])
})
toggleObserving(true)
}
}
2.1 resolveInject
export function resolveInject (inject, vm) {
if (inject) {
const result = Object.create(null)
// 获取inject的所有key
const keys = hasSymbol
? Reflect.ownKeys(inject)
: Object.keys(inject)
// 遍历
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
if (key === '__ob__') continue
// 标准化后的inject[key]中一定会有from参数
const provideKey = inject[key].from
let source = vm
// 遍历祖先组件,直到找到相应的provideKey
while (source) {
if (source._provided && hasOwn(source._provided, provideKey)) {
result[key] = source._provided[provideKey]
break
}
source = source.$parent
}
// 如果祖先组件未找到provideKey,启用default选项
if (!source) {
if ('default' in inject[key]) {
const provideDefault = inject[key].default
result[key] = typeof provideDefault === 'function'
? provideDefault.call(vm)
: provideDefault
}
}
}
return result
}
}
3. initState
export function initState (vm) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
可看到由上至下分别初始化props,methods,data,computed,watch,如果产生重名冲突,初始化在前优先级更高
3.1 initProps
function initProps (vm, propsOptions) {
const propsData = vm.$options.propsData || {}
const props = vm._props = {}
// 创建并维护一个存入所有key的数组
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
// root instance props should be converted
// 根实例的props需要响应式处理
if (!isRoot) {
toggleObserving(false)
}
for (const key in propsOptions) {
keys.push(key)
// prop验证
const value = validateProp(key, propsOptions, propsData, vm)
// 响应式处理关键函数
defineReactive(props, key, value)
// 代理到组件的_props_上
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
toggleObserving(true)
}
4.2 initMethods
function initMethods (vm: Component, methods: Object) {
const props = vm.$options.props
直接把相关函数挂载到组件实例下
for (const key in methods) {
vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
}
}
3.3 initData
function initData (vm) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
// getData 进行错误处理,不做深入了解了
? getData(data, vm)
: data || {}
if (!isPlainObject(data)) {
data = {}
}
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]
// 如果key没有在props和methods并且不是保留值,进行代理
proxy(vm, `_data`, key)
}
// 响应式处理
observe(data, true /* asRootData */)
}
3.4 initComputed
function initComputed (vm, computed) {
const watchers = vm._computedWatchers = Object.create(null)
for (const key in computed) {
const userDef = computed[key]
// getter的默认方式是fuction,但也可以直接设置get,set
const getter = typeof userDef === 'function' ? userDef : userDef.get
if (!isSSR) {
// 如果不是服务端渲染,进行响应式处理,添加到watchers中
watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions)
}
if (!(key in vm)) {
defineComputed(vm, key, userDef)
}
}
}
3.4.1 defineComputed
export function defineComputed (target, key, userDef) {
// 服务端渲染不缓存
const shouldCache = !isServerRendering()
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache
// 缓存处理
? createComputedGetter(key)
// 直接调用
: createGetterInvoker(userDef)
sharedPropertyDefinition.set = noop
} else {
sharedPropertyDefinition.get = userDef.get
// 处理用户设置的cache
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: createGetterInvoker(userDef.get)
: noop
sharedPropertyDefinition.set = userDef.set || noop
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
function createGetterInvoker (fn) {
return function computedGetter () {
return fn.call(this, this)
}
}
// 默认值,便于后续设置get,set
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
}
3.4.2 createComputedGetter
function createComputedGetter (key) {
return function computedGetter () {
// 获取到挂载在实例上的watcher
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
if (watcher.dirty) {
// 调用evaluate重新获取属性值
watcher.evaluate()
}
if (Dep.target) {
// 依赖收集
watcher.depend()
}
return watcher.value
}
}
}
3.5 initWatch
function initWatch (vm, watch) {
for (const key in watch) {
const handler = watch[key]
if (Array.isArray(handler)) {
for (let i = 0; i < handler.length; i++) {
createWatcher(vm, key, handler[i])
}
} else {
createWatcher(vm, key, handler)
}
}
}
3.5.1 createWatcher
function createWatcher (vm, expOrFn, handler, options) {
// 获取到watcher的具体函数并传给vm.$watch
// 如果是对象形式,就获取它的handler
if (isPlainObject(handler)) {
options = handler
handler = handler.handler
}
// 如果是一个字符串,直接在vm上获取
if (typeof handler === 'string') {
handler = vm[handler]
}
return vm.$watch(expOrFn, handler, options)
}
3.5.2 vm.$watch
Vue.prototypevm.$watch = function (expOrFn, cb, options) {
const vm = this
if (isPlainObject(cb)) {
return createWatcher(vm, expOrFn, cb, options)
}
options = options || {}
// 用户级watcher
options.user = true
// 设置响应式
const watcher = new Watcher(vm, expOrFn, cb, options)
// 有immediate参数需要立即执行
if (options.immediate) {
const info = `callback for immediate watcher "${watcher.expression}"`
pushTarget()
invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
popTarget()
}
return function unwatchFn () {
watcher.teardown()
}
}
}
4. initProvide
// 解析配置项的provide,并挂载到_provide选项
export function initProvide (vm: Component) {
const provide = vm.$options.provide
if (provide) {
vm._provided = typeof provide === 'function'
? provide.call(vm)
: provide
}
}
本节我们延续之前的内容,讲述了hook调用的原理,以及inject,provide的初始化,当然还有最重要的state,按顺序分别是props,methods,data,computed以及watcher的初始化。下一节,我会带大家一起看一下Vue中最重要的响应式处理机制。