咱们应用vue-cli搭建vue 2.x我的项目时,大抵由如下代码来做一个vue利用的初始化:
import Vue from "vue";import App from "./App.vue";Vue.config.productionTip = false;new Vue({ render: (h) => h(App),}).$mount("#app");
咱们能够就从此处开始对Vue的意识。能够看到,这里外表上只做了一个简略的工作,就是通过new操作创立了一个vue的实例,并传递了一个配置项对象,该对象蕴含了一个render办法。
依据这个调用,咱们找到src/core/instance/index.js
文件,内容如下:
// src/core/instance/index.jsimport { initMixin } from './init'import { stateMixin } from './state'import { renderMixin } from './render'import { eventsMixin } from './events'import { lifecycleMixin } from './lifecycle'import { warn } from '../util/index'function Vue (options) { if (process.env.NODE_ENV !== 'production' && !(this instanceof Vue) ) { warn('Vue is a constructor and should be called with the `new` keyword') } this._init(options)}initMixin(Vue)stateMixin(Vue)eventsMixin(Vue)lifecycleMixin(Vue)renderMixin(Vue)export default Vue
内容也很直观,这里定义了一个只承受new结构调用的Vue Function,并对Vue进行了一系列的混入操作。
再浅显地看一下这些Mixin都做了什么,能够看到是往Vue的prototype对象上挂了一些属性和办法。
大抵如下:
Vue.prototype|- initMixin |- _init(options?: Object)|- stateMixin |- $data |- $props |- $set(target: Array<any> | Object, key: any, val: any): any <- ../observer/index |- $delete(target: Array<any> | Object, key: any) <- ../observer/index |- $watch(expOrFn: string | Function, cb: any, options?: Object): Function|- eventMixin |- $on(event: string | Array<string>, fn: Function): Component |- $once(event: string, fn: Function): Component |- $off(event?: string | Array<string>, fn?: Function): Component |- $emit(event: string): Component|- lifecycleMixin |- $_update(vnode: VNode, hydrating?: boolean) |- $forceUpdate() |- $destrouy()|- renderMixin |- $nextTick(fn: Function) |- _render(): VNode
Vue的函数体中,调用了一个_init
的办法,并将参数传入,能够看到,_init
办法是在initMixin中定义的。
持续看_init
办法的定义:
// src/core/instance/init.jsVue.prototype._init = function (options?: Object) { const vm: Component = this // a uid vm._uid = uid++ let startTag, endTag /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { startTag = `vue-perf-start:${vm._uid}` endTag = `vue-perf-end:${vm._uid}` mark(startTag) } // a flag to avoid this being observed vm._isVue = true // merge 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 ) } /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { initProxy(vm) } else { vm._renderProxy = vm } // expose real self vm._self = vm initLifecycle(vm) initEvents(vm) initRender(vm) callHook(vm, 'beforeCreate') initInjections(vm) // resolve injections before data/props initState(vm) initProvide(vm) // resolve provide after data/props callHook(vm, 'created') /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { vm._name = formatComponentName(vm, false) mark(endTag) measure(`vue ${vm._name} init`, startTag, endTag) } if (vm.$options.el) { vm.$mount(vm.$options.el) }}
见名知意,这个函数是对vue实例做一系列的初始化操作。
- 获取vue实例的结构器以及父级结构器(顺次递归)上的配置项,以及参数传递进来的配置项,在加上实例自带的属性,都合并到一起,挂在实例的$option属性身上
- 将vue实例本身挂在_renderProxy属性上
初始化数据和办法前做一些筹备工作
- initLifecycle:初始化生命周期
- initEvents:初始化事件
- initRender:初始化render
- 触发
beforeCreate
钩子
初始化数据和办法
- initInjections:解决$options.inject,对注入的数据做响应式解决
initState做的几件事
- initProps:对$options.props做响应式解决
- initMethods:对$options.methods对象做解决,将所有的办法间接挂在实例对象上,并将办法的this绑定到vue实例对象
vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
initData:对$options.data进行observe
observe(data, true /* asRootData */)
,持续追踪能够看到observe
办法是对data进行响应式解决,返回一个Observer
实例// src/core/boserver/index.jsexport class Observer { value: any; dep: Dep; vmCount: number; // number of vms that have this object as root $data constructor (value: any) { this.value = value this.dep = new Dep() this.vmCount = 0 def(value, '__ob__', this) if (Array.isArray(value)) { if (hasProto) { protoAugment(value, arrayMethods) } else { copyAugment(value, arrayMethods, arrayKeys) } this.observeArray(value) } else { this.walk(value) } } /** * Walk through all properties and convert them into * getter/setters. This method should only be called when * value type is Object. */ walk (obj: Object) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i]) } } /** * Observe a list of Array items. */ observeArray (items: Array<any>) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) } }}
initComputed:解决计算属性$options.computed
给每个计算属性创立Watcher实例
// src/core/instance/state.jsconst computedWatcherOptions = { lazy: true }function initComputed(vm: Component, computed: Object) { // ... const watchers = (vm._computedWatchers = Object.create(null)) // ... 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. watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions ) } if (!(key in vm)) { defineComputed(vm, key, userDef) } // ... } // ...}export function defineComputed ( target: any, key: string, userDef: Object | Function) { const shouldCache = !isServerRendering() if (typeof userDef === 'function') { sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : createGetterInvoker(userDef) sharedPropertyDefinition.set = noop } else { // ... } // ... Object.defineProperty(target, key, sharedPropertyDefinition)}function createComputedGetter (key) { return function computedGetter () { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { if (watcher.dirty) { watcher.evaluate() } if (Dep.target) { watcher.depend() } return watcher.value } }}
能够看到创立Watcher实例时传入一个配置项
{ lazy: true }
,再看Watcher
的结构器中的代码,即默认watcher.dirty
为true
,所以执行watcher.evaluate()
,watcher.get()
。watcher.get()
会去执行计算方法或者计算属性的get()
办法,即this.getter.call(vm, vm)
。// src/core/observer/watcher.jsconstructor ( vm: Component, expOrFn: string | Function, cb: Function, options?: ?Object, isRenderWatcher?: boolean ) { this.vm = vm if (isRenderWatcher) { vm._watcher = this } vm._watchers.push(this) // options if (options) { // ... this.lazy = !!options.lazy // ... } else { // ... } // ... this.dirty = this.lazy // for lazy watchers // ...}evaluate () { this.value = this.get() this.dirty = false}get() { pushTarget(this) let value const vm = this.vm try { 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) } popTarget() this.cleanupDeps() } return value}depend() { let i = this.deps.length while (i--) { this.deps[i].depend() }}
initWatch:解决自定义监听$options.watch
执行了
$watch
办法,能够先看下它的定义:// src/core/instance/state.jsVue.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 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() } return function unwatchFn() { watcher.teardown() } }
能够看到也是创立了一个
Watcher
实例对象。
- initProvide:解决$options.provide,将provide的数据(或者provide执行后的数据)挂在实例的
_provided
属性上 - 触发
created
钩子
最初执行
vm.$mount
办法,执行挂载流程,因为挂载的形式由平台决定,所以$mount
的办法并未定义在src/core
中;web端的$mount
办法定义在src/platforms/web/runtime/index.js
中。// src/platforms/web/runtime/index.jsVue.prototype.$mount = function ( el?: string | Element, hydrating?: boolean): Component { el = el && inBrowser ? query(el) : undefined return mountComponent(this, el, hydrating)}
调用的
mountComponent(this, el, hydrating)
定义在src/core/instance/lifecycle.js
中。// src/core/instance/lifecycle.jsexport function mountComponent ( vm: Component, el: ?Element, hydrating?: boolean): Component { vm.$el = el if (!vm.$options.render) { vm.$options.render = createEmptyVNode if (process.env.NODE_ENV !== 'production') { /* istanbul ignore if */ if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') || vm.$options.el || el) { warn( 'You are using the runtime-only build of Vue where the template ' + 'compiler is not available. Either pre-compile the templates into ' + 'render functions, or use the compiler-included build.', vm ) } else { warn( 'Failed to mount component: template or render function not defined.', vm ) } } } callHook(vm, 'beforeMount') let updateComponent /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { updateComponent = () => { const name = vm._name const id = vm._uid const startTag = `vue-perf-start:${id}` const endTag = `vue-perf-end:${id}` mark(startTag) const vnode = vm._render() mark(endTag) measure(`vue ${name} render`, startTag, endTag) mark(startTag) vm._update(vnode, hydrating) mark(endTag) measure(`vue ${name} patch`, startTag, endTag) } } else { updateComponent = () => { vm._update(vm._render(), hydrating) } } // 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, { before () { if (vm._isMounted && !vm._isDestroyed) { callHook(vm, 'beforeUpdate') } } }, true /* isRenderWatcher */) hydrating = false // 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}
见名知意,是对挂载的解决:
- 拿到
el
放在vm.$el上 - 确认是否有
vm.$options.render
,没有则赋值创立一个空的VNode实例的办法 - 触发
beforeMount
钩子 创立一个新的
Watcher
实例,用于实例更新后触发从新渲染updateComponent = () => { vm._update(vm._render(), hydrating)}
并传递一个before办法,用于在组件更新前触发
beforeUpdate
钩子- 触发
mounted
钩子
- 拿到
Vue利用初始化大抵就是这样一个流程