vue 初始化请求例子_Vue源码之Vue实例初始化

这篇博客深入探讨了Vue的初始化过程,包括initLifecycle、initEvents、initInjections、initState等阶段,详细解释了如何创建Vue实例,如何处理事件、属性、方法、数据以及计算属性,并提到了Vue生命周期和组件间通信的关键细节。
摘要由CSDN通过智能技术生成

这一节主要记录一下:Vue 的初始化过程

以下正式开始:

Vue官网的生命周期图示表

3598f7f7b01d7747c49906d05d2a7ed5.png

重点说一下 new Vue()后的初始化阶段,也就是created之前发生了什么。

9ccd726dac8e02918cb0f67d50076193.png

initLifecycle 阶段

export function initLifecycle (vm: Component) {

const options = vm.$options

// locate first non-abstract parent

let parent = options.parent

if (parent && !options.abstract) {

while (parent.$options.abstract && parent.$parent) {

parent = parent.$parent

}

parent.$children.push(vm) // 自己把自己添加到父级的$children数组中

}

vm.$parent = parent // 父组件实例

vm.$root = parent ? parent.$root : vm // 根组件 如果不存在父组件,则本身就是根组件

vm.$children = [] // 用来存放子组件

vm.$refs = {}

vm._watcher = null

vm._inactive = null

vm._directInactive = false

vm._isMounted = false

vm._isDestroyed = false

vm._isBeingDestroyed = false

}

接下来是initEvents 阶段

// v-on如果写在平台标签上如:div,则会将v-on上注册的事件注册到浏览器事件中

// v-on如果写在组件标签上,则会将v-on注册的事件注册到子组件的事件系统

// 子组件(Vue实例)在初始化的时候,有可能接收到父组件向子组件注册的事件。

// 子组件(Vue实例)自身模板注册的事件,只要在渲染的时候才会根据虚拟DOM的对比结果

// 来确定是注册事件还是解绑事件

// 这里初始化的事件是指父组件在模板中使用v-on注册的事件添加到子组件的事件系统也就是vue的事件系统。

export function initEvents (vm: Component) {

vm._events = Object.create(null) // 初始化

vm._hasHookEvent = false

// init parent attached events 初初始化腹肌组件添加的事件

const listeners = vm.$options._parentListeners

if (listeners) {

updateComponentListeners(vm, listeners)

}

}

export function updateComponentListeners (

vm: Component,

listeners: Object,

oldListeners: ?Object

) {

target = vm

updateListeners(listeners, oldListeners || {}, add, remove, createOnceHandler, vm)

target = undefined

}

initjections 阶段

export function initInjections (vm: Component) {

// 自下而上读取inject

const result = resolveInject(vm.$options.inject, vm)

if (result) {

// 设置为false 避免defineReactive函数把数据转换为响应式

toggleObserving(false)

Object.keys(result).forEach(key => {

defineReactive(vm, key, result[key])

})

// 再次更改回来

toggleObserving(true)

}

}

export function resolveInject (inject: any, vm: Component): ?Object {

if (inject) {

// inject is :any because flow is not smart enough to figure out cached

const result = Object.create(null)

// 如果浏览器支持Symbol,则使用Reflect.ownkyes(),否则使用Object.keys()

const keys = hasSymbol

? Reflect.ownKeys(inject)

: Object.keys(inject)

for (let i = 0; i < keys.length; i++) {

const key = keys[i]

// #6574 in case the inject object is observed...

if (key === '__ob__') continue

const provideKey = inject[key].from

let source = vm

// 当provided注入内容的时候就是把内容注入到当前实例的_provided中

// 刚开始的时候 source就是实本身,挡在source._provided中找不到对应的值

// 就会把source设置为父实例

// Vue实例化的第一步就是规格化用户传入的数据,所以inject不管时数组还是对象

// 最后都会变成对象

while (source) {

if (source._provided && hasOwn(source._provided, provideKey)) {

result[key] = source._provided[provideKey]

break

}

source = source.$parent

}

// 处理默认值的情况

if (!source) {

if ('default' in inject[key]) {

const provideDefault = inject[key].default

result[key] = typeof provideDefault === 'function'

? provideDefault.call(vm)

: provideDefault

} else if (process.env.NODE_ENV !== 'production') {

warn(`Injection "${key}" not found`, vm)

}

}

}

return result

}

}

initState 阶段

在 Vue 中,我们经常会用到 props 、methods 、 watch 、computed 、data 。这些状态在使用前都需要初始化。而初始化的过程正是在 initState 阶段完成。

因为 injects 是在 initState 之前完成,所以可以在 State 中使用 injects 。

export function initState (vm: Component) {

vm._watchers = []

// 获取到经过初始化的用户传进来的options

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)

}

}

initProps

function initProps (vm: Component, propsOptions: Object) {

const propsData = vm.$options.propsData || {}

const props = vm._props = {}

// cache prop keys so that future props updates can iterate using Array

// instead of dynamic object key enumeration.

// 缓存props的key值

const keys = vm.$options._propKeys = []

const isRoot = !vm.$parent

// root instance props should be converted

// 如果不是跟组件则没必要转换成响应式数据

if (!isRoot) {

// 控制是否转换成响应式数据

toggleObserving(false)

}

for (const key in propsOptions) {

keys.push(key)

// 获取props的值

const value = validateProp(key, propsOptions, propsData, vm)

defineReactive(props, key, value)

// static props are already proxied on the component's prototype

// during Vue.extend(). We only need to proxy props defined at

// instantiation here.

// 把props代理到Vue实例上来,可以直接通过this.props访问

if (!(key in vm)) {

proxy(vm, `_props`, key)

}

}

toggleObserving(true)

}

initMethods

function initMethods (vm: Component, methods: Object) {

const props = vm.$options.props

for (const key in methods) {

if (process.env.NODE_ENV !== 'production') {

// 如果key不是一个函数 报错

if (typeof methods[key] !== 'function') {

warn(

`Method "${key}" has type "${typeof methods[key]}" in the component definition. ` +

`Did you reference the function correctly?`,

vm

)

}

// 如果props中存在同名的属性 报错

if (props && hasOwn(props, key)) {

warn(

`Method "${key}" has already been defined as a prop.`,

vm

)

}

// isReserved判断是否以$或_开头

if ((key in vm) && isReserved(key)) {

warn(

`Method "${key}" conflicts with an existing Vue instance method. ` +

`Avoid defining component methods that start with _ or $.`

)

}

}

// 把methods的方法绑定到Vue实例上

vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)

}

}

initData

function initData (vm: Component) {

let data = vm.$options.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

// 循环data

while (i--) {

const key = keys[i]

if (process.env.NODE_ENV !== 'production') {

// 如果在methods中存在和key同名的属性 则报错

if (methods && hasOwn(methods, key)) {

warn(

`Method "${key}" has already been defined as a data property.`,

vm

)

}

}

// 如果在props中存在和key同名的属性 则报错

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,使得可以直接通过this.key访问this._data.key

proxy(vm, `_data`, key)

}

}

// observe data

// 把data转换为响应式数据

observe(data, true /* asRootData */)

}

initComputed

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 = typeof userDef === 'function' ? userDef : userDef.get

if (process.env.NODE_ENV !== 'production' && getter == null) {

warn(

`Getter is missing for computed property "${key}".`,

vm

)

}

// 如果不是ssr,则创建Watcher实例

if (!isSSR) {

// create internal watcher for the computed property.

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.

// 如果vm不存在key的同名属性

if (!(key in vm)) {

defineComputed(vm, key, userDef)

} else if (process.env.NODE_ENV !== 'production') {

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)

}

}

}

}

sharedPropertyDefinition = {

enumerable: true,

cnfigurable: true,

get: noop,

set: noop

}

export function defineComputed (

target: any,

key: string,

userDef: Object | Function

) {

// 如果是服务端渲染,则computed不会有缓存,因为数据响应式的过程在服务器是多余的

const shouldCache = !isServerRendering()

// createComputedGetter返回计算属性的getter

// createGetterInvoker返回userDef的getter

if (typeof userDef === 'function') {

sharedPropertyDefinition.get = shouldCache

? createComputedGetter(key)

: createGetterInvoker(userDef)

sharedPropertyDefinition.set = noop

} else {

// 当userDef为一个对象时

sharedPropertyDefinition.get = userDef.get

? shouldCache && userDef.cache !== false

? createComputedGetter(key)

: createGetterInvoker(userDef.get)

: noop

sharedPropertyDefinition.set = userDef.set || noop

}

if (process.env.NODE_ENV !== 'production' &&

sharedPropertyDefinition.set === noop) {

sharedPropertyDefinition.set = function () {

warn(

`Computed property "${key}" was assigned to but it has no setter.`,

this

)

}

}

// 在tearget上定义一个属性, 属性名为key, 属性描述符为sharedPropertyDefinition

Object.defineProperty(target, key, sharedPropertyDefinition)

}

function createComputedGetter (key) {

return function computedGetter () {

// 查找是否存在key的Watcher

const watcher = this._computedWatchers && this._computedWatchers[key]

if (watcher) {

// 如果dirty为true,则重新计算,否则返回缓存

if (watcher.dirty) {

watcher.evaluate()

}

if (Dep.target) {

watcher.depend()

}

return watcher.value

}

}

}

function createGetterInvoker(fn) {

return function computedGetter () {

return fn.call(this, this)

}

}

initWatch

function initWatch (vm: Component, watch: Object) {

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,

expOrFn: string | Function,

handler: any,

options?: Object

) {

// isPlainObject检查是否是对象

if (isPlainObject(handler)) {

options = handler

handler = handler.handler

}

if (typeof handler === 'string') {

handler = vm[handler]

}

// 最后调用$watch

return vm.$watch(expOrFn, handler, options)

}

initProvide阶段

export function initProvide (vm: Component) {

const provide = vm.$options.provide

if (provide) {

// 把provided存到_provided上

vm._provided = typeof provide === 'function'

? provide.call(vm)

: provide

}

}

到这里 Vue 的初始化就结束了,接下来就是触发生命周期函数 created 。

总结一下:new Vue() 执行之后,Vue 进入初始化阶段。

初始化流程如下:

规格化 $options ,也就是用户自定义的数据

initLifecycle 注入生命周期

initEvents 初始化事件,注意:这里的事件是值在父组件在子组件上定义的事件

initRender

initjections 初始化 jetction

initProps 初始化props

initState 包括props 、methods 、data 、computed 、watch

initProvided 初始化 provide

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值