VUE源码阅读笔记3:vue初始化都发生了哪些故事
接上节,我们看一下vue的初始化都做了哪些事情
所在文件:src/instance/init.js
import config from '../config' import { initProxy } from './proxy' import { initState } from './state' import { initRender } from './render' import { initEvents } from './events' import { mark, measure } from '../util/perf' import { initLifecycle, callHook } from './lifecycle' import { initProvide, initInjections } from './inject' import { extend, mergeOptions, formatComponentName } from '../util/index' let uid = 0 export function initMixin (Vue: Class<Component>) { Vue.prototype._init = function (options?: Object) { const vm: Component = this // a uid //给每个vue实例指定一个uid vm._uid = uid++ .... // 合并 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 ) } // expose real self vm._self = vm //初始化生命周期 initLifecycle(vm) //初始化事件 initEvents(vm) //初始化render initRender(vm) //调用钩子函数beforeCreate,此时还没有初始化state,data/props还不能调用 callHook(vm, 'beforeCreate') //初始化注入 initInjections(vm) // resolve injections before data/props //初始化状态 initState(vm) initProvide(vm) // resolve provide after data/props //调用钩子函数created,此时可以调用data/props了 callHook(vm, 'created') ... //如果实例化Vue时指明了el,则进行挂载 if (vm.$options.el) { vm.$mount(vm.$options.el) } } }
初始化分成这几步
合并参数options
初始化声明周期、事件、render
调用钩子函数 beforeCreate
初始化注入、状态
调用钩子函数created
挂载
接下来我们一个一个细看都发生了什么,首先看一下初始化声明周期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) } vm.$parent = parent vm.$root = parent ? parent.$root : vm vm.$children = [] vm.$refs = {} //初始化声明周期的标识flag值 vm._watcher = null vm._inactive = null vm._directInactive = false vm._isMounted = false vm._isDestroyed = false vm._isBeingDestroyed = false }
声明周期初始化比较简单,接下来看看初始化事件initEvents
export function initEvents (vm: Component) { //创建一个空对象,用来存储事件 vm._events = Object.create(null) vm._hasHookEvent = false // init parent attached events //初始化父组件捕获的事件,_parentListeners其实是父组件模板中写的v-on const listeners = vm.$options._parentListeners if (listeners) { //将父组件模板中注册的事件,放到当前组件实例的listeners里面 updateComponentListeners(vm, listeners) } }
继续深入看updateComponentListeners
export function updateComponentListeners ( vm: Component, listeners: Object, oldListeners: ?Object ) { target = vm updateListeners(listeners, oldListeners || {}, add, remove, vm) target = undefined }
继续updateListeners,不是十分明白这块,先暂时忽略吧,哈哈
export function updateListeners ( on: Object, oldOn: Object, add: Function, remove: Function, vm: Component ) { let name, def, cur, old, event for (name in on) { def = cur = on[name] old = oldOn[name] //事件name、once、capture、passive event = normalizeEvent(name) /* istanbul ignore if */ if (__WEEX__ && isPlainObject(def)) { cur = def.handler event.params = def.params } if (isUndef(cur)) { process.env.NODE_ENV !== 'production' && warn( `Invalid handler for event "${event.name}": got ` + String(cur), vm ) } else if (isUndef(old)) { if (isUndef(cur.fns)) { cur = on[name] = createFnInvoker(cur) } add(event.name, cur, event.once, event.capture, event.passive, event.params) } else if (cur !== old) { old.fns = cur on[name] = old } } for (name in oldOn) { if (isUndef(on[name])) { event = normalizeEvent(name) remove(event.name, oldOn[name], event.capture) } } }
接下来查看initRender
export function initRender (vm: Component) { vm._vnode = null // the root of the child tree vm._staticTrees = null // v-once cached trees const options = vm.$options const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree const renderContext = parentVnode && parentVnode.context //处理组件slot,返回slot插槽对象 vm.$slots = resolveSlots(options._renderChildren, renderContext) vm.$scopedSlots = emptyObject // bind the createElement fn to this instance // so that we get proper render context inside it. // args order: tag, data, children, normalizationType, alwaysNormalize // internal version is used by render functions compiled from templates vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false) // normalization is always applied for the public version, used in // user-written render functions. vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true) // $attrs & $listeners are exposed for easier HOC creation. // they need to be reactive so that HOCs using them are always updated const parentData = parentVnode && parentVnode.data /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { //定义响应式属性$attrs,其值为父组件的属性,猜想是不是当父组件的属性变化后,子组件也要跟着更新 defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => { !isUpdatingChildComponent && warn(`$attrs is readonly.`, vm) }, true) //同上 defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => { !isUpdatingChildComponent && warn(`$listeners is readonly.`, vm) }, true) } else { defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true) defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true) } }
这里defineReactive就是利用观察者模式进行数据绑定的,下一节我们仔细来看
接下来查看调用钩子函数callHook
export function callHook (vm: Component, hook: string) { // #7573 disable dep collection when invoking lifecycle hooks pushTarget() const handlers = vm.$options[hook] if (handlers) { //同名钩子函数可能有多个,数组遍历调用 for (let i = 0, j = handlers.length; i < j; i++) { try { //钩子函数中的this,执行组件实例 handlers[i].call(vm) } catch (e) { handleError(e, vm, `${hook} hook`) } } } if (vm._hasHookEvent) { vm.$emit('hook:' + hook) } popTarget() }
初始化注入initInjections,不太明白,跳过
接下来看非常重要的initState
export function initState (vm: Component) { //应该是用来存储监听 vm._watchers = [] const opts = vm.$options if (opts.props) initProps(vm, opts.props) //如果有属性props,则初始化props if (opts.methods) initMethods(vm, opts.methods) //如果有methods,初始化methods if (opts.data) { initData(vm) //有data,初始化data } else { observe(vm._data = {}, true /* asRootData */) } if (opts.computed) initComputed(vm, opts.computed) //初始化计算属性 if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) //初始化watch } }
可以看到初始化state主要包含这几块
初始化props
初始化methods
初始化data
初始化computed
初始化watch
初始化props
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. const keys = vm.$options._propKeys = [] const isRoot = !vm.$parent // root instance props should be converted if (!isRoot) { toggleObserving(false) } //遍历props配置对象 for (const key in propsOptions) { keys.push(key) //获取属性值 const value = validateProp(key, propsOptions, propsData, vm) /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { const hyphenatedKey = hyphenate(key) if (isReservedAttribute(hyphenatedKey) || config.isReservedAttr(hyphenatedKey)) { warn( `"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`, vm ) } //响应式处理 defineReactive(props, key, value, () => { if (!isRoot && !isUpdatingChildComponent) { warn( `Avoid mutating a prop directly since the value will be ` + `overwritten whenever the parent component re-renders. ` + `Instead, use a data or computed property based on the prop's ` + `value. Prop being mutated: "${key}"`, vm ) } }) } else { //响应式处理 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. if (!(key in vm)) { //代理,访问this.**,相当于访问 this._props.** proxy(vm, `_props`, key) } } toggleObserving(true) }
初始化data
function initData (vm: Component) { let data = vm.$options.data data = vm._data = typeof data === 'function' ? getData(data, vm) : 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 while (i--) { const key = keys[i] if (process.env.NODE_ENV !== 'production') { //data中属性不能和methods重名 if (methods && hasOwn(methods, key)) { warn( `Method "${key}" has already been defined as a data property.`, vm ) } } if (props && hasOwn(props, key)) { //data中属性不能和props重名 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)) { //代理,访问this.** 相当于访问 this._data.** proxy(vm, `_data`, key) } } // observe data //观察数据 observe(data, true /* asRootData */) }
观察数据observe
export function observe (value: any, asRootData: ?boolean): Observer | void { if (!isObject(value) || value instanceof VNode) { return } let ob: Observer | void if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { ob = value.__ob__ } else if ( shouldObserve && !isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue ) { //实例化观察者 ob = new Observer(value) } if (asRootData && ob) { ob.vmCount++ } return ob }
针对data进行了实例化观察者操作,下一节我们来看vue的观察者设计模式。
点赞(0)