# Vue 初始化
# Vue 的构造函数
/src/core/instance/init.js
- Vue的
_init方法大致- 每个 Vue 实例,会将所有的属性挂载到
vm.$options。 - 初始化组件实例关系,定义 prototype 方法。例:
$mount、_render、_update...。初始化自定义事件vm._events。 - 解析插槽信息得到
vm.$slot、vm.$scopedSlots...。 - 使用
vm.$mount函数构建 DOM。(如果没有使用 loader 和 template-compiler ,会改写vm.$mount使之生成template对应的render函数)
- 每个 Vue 实例,会将所有的属性挂载到
export class Vue {
constructor(options?: Object) {
const vm: Component = this
// 每个 Vue 实例上的 _uid,依次递增, 唯一
vm._uid = uid++
// 代表当前实例已经被 observed
vm._isVue = true
// 处理组件配置项
if (options && options._isComponent) {
/**
* _isComponent 估计 components 属性或者 router-view 中会赋值为 true
* 每个子组件初始化走这,做了一些性能优化
* 将每个实例上的深层次属性放到 $options 选项中,以提高代码的执行效率
*/
initInternalComponent(vm, options);
} else {
// 初始化根组件时走这里,合并 Vue 的全局配置到根组件的局部配置
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
if (process.env.NODE_ENV !== 'production') {
// 设置代理,将vm实例上的属性代理到vm._renderProxy
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose(暴露) real self
vm._self = vm
// 初始化组件实例关系属性,如 $parent、$children、$root、$refs 等
initLifecycle(vm)
// 初始化自定义事件
initEvents(vm)
// 解析插槽信息,得到 $slots,$createElement
initRender(vm)
// 调用 beforeCreate 钩子函数
callHook(vm, 'beforeCreate')
// 初始化组件的 inject 配置型,得到 result[key] = val 形式的配置对象,然后对数据进行响应式处理,然后将每个key挂载到自身实例上
initInjections(vm)
// 处理响应式数据。props,data,computed,watch,methods
initState(vm);
// 解析组件配置项上的 provide 对象,将其挂载到 vm._provided 属性上
initProvide(vm)
// 调用created钩子函数
callHook(vm, 'created')
// 如果发现配置项上有el选项,则自动调用$mount方法
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
# initInjections
/src/core/instance/inject
export function initInjections (vm: Component) {
// 解析 inject 配置项,然后从祖代组件的配置中找到 配置项中每一个 key 对应的 value
const result = resolveInject(vm.$options.inject, vm)
// 对获得 inject 对象挂载到 this 上,同时做数据响应处理
if (result) {
toggleObserving(false);
Object.keys(result).forEach(key => {
defineReactive(vm, key, result[key])
})
toggleObserving(true);
}
}
# resolveInject
/src/core/instance/inject
// inject 对象在合并选项是对组件配置做了标准化处理,所以 inject 的结构是一样的
interface Inject {
[key: string]: {
from: string
default?: unknown
}
}
export function resolveInject (inject: Inject, vm: Component): Object | void {
if (inject) {
const result = Object.create(null)
const keys = hasSymbol
? Reflect.ownKeys(inject)
: Object.keys(inject)
// 遍历
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
// 跳过 __ob__ 对象
if (key === '__ob__') continue
// 处理 form 属性获取 provideKey
const provideKey = inject[key].form
let source = vm
// 遍历所有祖代实例,找到最近的祖代实例中的 provide 中对应 key 的值
while (source) {
if (source.provide && hasOwn(source._provide, provideKey)) {
// 在 initProvide 函数中会将 provide 对象,将其挂载在 source._provided 属性上
result[key] = source._provided[provideKey]
break
}
source = source.$parent
}
}
// 如果没找到对应的 inject, 则使用 default 属性
if (!source) {
if ('default' in inject[key]) {
const proviedeDefault = inject[key].default
result[key] = provideDefault === 'function'
? provideDefault.call(vm)
: provideDefault
} else {
throw new Error('error')
}
}
return result
}
}
# initState
/src/core/instance/state
- 依顺序分别处理
- initProps: 为
props各个属性实现响应式 - initMethods: 保证
methods中都必须是一个函数,同时key不会和其他属性冲突 - initData: 进行observe处理,进行依赖收集
- initComputed: 为
computed每个属性创建懒加载 Wathcer- 通过Watcher的
get()获取值,并将得到的值缓存与 Wathcer 的value属性(dirty为false)。 - 通过
definePeoperty代理返回返回watcher.value。 - 当它依赖的数据发生改变。 会将自身的 Watcher 的
dirty属性改为true。此时会重新再运行get()函数,并收集依赖。
- 通过Watcher的
- initWatch: 将 Watcher 都放入到
vm._watchers数组中
- initProps: 为
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
// 处理 props
if (opts.props) initProps(vm, opts.props)
// 处理 methods
if (opts.methods) initMethods(vm, opts.methods)
// 处理 data,所以在 data 里能访问 props,但是拜托希望别这么用
if (opts.data) {
initData(vm)
} else {
// 放在下一章节
observe(vm._data = {}, true /* asRootData */)
}
// 处理 computed
if (opts.computed) initComputed(vm, opts.computed)
/**
* 处理 watch,为每个 key 创建 watcher 实例,key 与 watcher 实例可能是一对多的管理
* 如果配置了 immediate 则会立即执行回调函数
*/
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
# initProps
- 处理props对象,为每个属性设置响应式,并将其代理到实例 vm 上
export function initProps (vm: Component, propsOptions: Object) {
const propsData = vm.$options.propsData || {}
const props = vm._props = {}
// 缓存 props 的每个key,性能优化
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
// 因为根组件的 props 是不会改变的 所以不需要收集根组件的 props 的依赖
if (!isRoot) {
toggleObserving(false)
}
for (const key in propsOptions) {
keys.push(key)
// 获取 props[key] 的正真的值
// propsOptions = { value: { type: string, default: '' } }
const value = validateProp(key, propsOptions, propsData, vm)
// 为每个属性设置响应式
defineReactive(props, key, value)
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
toggleObseving(true)
}
# initMethods
export function initMethods (vm: Component, methods: Object) {
// initProps 是由 $options.propsData 取,这里从 props 取是为了防止触发 props 的 get 与 set
const props = vm.$options.props
// 遍历 methods 放置 key 重复
for (const key in methods) {
if (process.env.NODE_ENV !== 'production') {
if (typeof methods[key] !== 'function') {
warn(/* ... */)
}
if (props && hasOwn(props, key)) {
warn(/* key与props中key重复 */)
}
if ((key in vm) && isReserved(key)) {
warn(/* key与vue中定义的属性相同 */)
}
}
vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
}
}
# initData
export function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? 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]
// 省略与 props 和 methods 中 key 的去重处理
observe(data, true)
}
}
# initComputed
- 为所有
computed[key]创建 watcher 实例,默认是懒执行。
export function initComputed (vm: Component, computed: Object) {
const wathcers = vm._computedWatchers = Object.create(null)
// SSR 服务端渲染
const isSSR = isServerRendering()
// 遍历computed
for (const key in computed) {
// 获取对应的 getter 函数
const userDef = computed[key]
const getter = typeof userDef === 'function' ? userDef: userDef.get
if (getter === null) warn(/* error */)
if (!isSSR) {
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions // 默认是懒执行
)
}
if (!(key in vm)) {
defineComputed(vm, key, userDef)
} else {
warn(/* 重复 */)
}
}
}
function defineComputed (target: any, key: string, userDef: Object | Function) {
if (typeof userDef === 'funciton') {
/* 如果是服务端渲染会进行createGetterInvoker(userDef)处理 */
sharedPropertyDefinition.get = createComputedGetter(key)
} else {
// 一般用户定义的是不会指定 cache 属性, 如果 cache: false,则会创建空的 getter 函数
sharedPropertyDefinition.get = userDef.get && userDef.cache !== false
? createComputedGetter(key)
: noop
sharedPropertyDefinition.set = userDef.set || noop
}
// 如果没有设置 set 会进行warn
// 绑定
Object.defineProperty(target, key, sharedPropertyDefinition)
}
function createComputedGetter (key) {
// computed 缓存, 当获取值时会触发 get 方法
return function computedGetter () {
// 通过key 去寻找对应的 Watcher
const watcher = this._computedWatchers && this._computedWatchers[key]
if (wathcer) {
/**
* computed 中被引用的值发生变化的时候
* 会将 computed 的 watcher 的 dirty 属性设为 true
* 代表需要重新调用一次 handler 函数来得到最新的值
* 否则直接返回缓存的 watcher.value
*/
if (watcher.dirty) {
// evaluate 之后会再次将 dirty 设为 false
watcher.evaluate()
}
// 依赖收集
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
}
}
# initWatch
- 处理watch对象,通过
vm.$watch为每个属性创建一个Watcher
// watch的几种使用方式
export default {
watch: {
key1: function (newVal, oldVal) { /* handler */ },
key2: {
handler (newVal, oldVal) { /* handler */ },
deep: true,
immediate: true
},
key3: [
this.methodName,
fucntion() { /* handler */ },
{
handler() { /* handler */ },
deep: false
}
],
'key4': 'methodsName', // vm 上的 methods 方法名
'props.key5': {
handler (newVal, oldVal) { /* handler */ }
}
}
}
export function initWatch (vm: Component, watch: Object) {
// 为每个 key 都调用 createWatcher
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)
}
}
}
function createWatcher (
vm: Component,
exOrFn: string | Function
handler: any
options?: Object
) {
// 如果 handler 为对象,则获取其中的 handler 选项的值
if (isPlainObject(handler)) {
options = handler
handler = handler.handler
}
// 如果为 string,则获取 this[handler] 方法
if (typeof handler === 'string') {
handler = vm[handler]
}
return vm.$watch(expOrFn, handler, options)
}
# vm.$watcher
Vue.prototype.$watch = function (
expOrFn: string | Function,
cb: any,
options?: Object
) {
const vm: Component = this
// 是对象就处理成函数再进行处理
if (isPlainObject(cb)) {
return createWatcher(vm, expOrFn, cb, options)
}
options = options || {}
options.user = true // 标记 此用户创建的 wathcer
const watcher = new Watcher(vm, expOrFn, cb, options)
// 如果设置了 immediate 则立即执行回调函数
if (options.immdiate) {
try {
cb.call(vm, wathcer.value)
} catch (err) {
// doSomthing...
}
}
// 用来解除监听
return function unwatchFn () {
watcher.teardown()
}
}
# initProvide
- /src/core/instance/inject
provide基本属性值是非响应式的
// 将 provide 对象,将其挂载在 vm._provided 属性上
export function initProvide (vm: Component) {
const provide = vm.$options.provide
if (provide) {
// provide 属性可以是一个函数,也可以是一个对象
vm._provided = typeof provide === 'function'
? provide.call(vm)
: provide
}
}
Observe →