src/core/instance/index.ts - 构造函数创建
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'
import type { GlobalAPI } from 'types/global-api'
// 创建构造函数
function Vue(options) {
if (__DEV__ && !(this instanceof Vue)) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
// 往构造函数原型上新增_init方法
// _init方法是构造函数具体实现内容
// 合并配置项
// 关联组件父子关系
// 一些私有属性创建例如$slot,$emit等
// 触发 beforeCreate
// 仅inject 初始化
// 进行props data computed等的初始化
// 进行Provider初始化
// 触发 created
// 如果组件已经挂载 调用 $mount
initMixin(Vue)
// 对 $data $props等进行响应式处理,拦截set访问
// 挂载$set $delete $watch
stateMixin(Vue)
// 事件系统实现 挂载 $on $emit等
eventsMixin(Vue)
// 挂载 _update $forceUpdate $destroy 等方法
lifecycleMixin(Vue)
// 挂载render函数
renderMixin(Vue)
export default Vue as unknown as GlobalAPI
src/core/instance/init.ts - 构造函数中_init实现
-
initMixin 实现
- 触发前置的生命周期,初始化状态和合并配置项,关联父子关系
-
let uid = 0 export function initMixin(Vue: typeof Component) { Vue.prototype._init = function (options?: Record<string, any>) { const vm: Component = this // 组件唯一id vm._uid = uid++ let startTag, endTag /* istanbul ignore if */ if (__DEV__ && config.performance && mark) { startTag = `vue-perf-start:${vm._uid}` endTag = `vue-perf-end:${vm._uid}` mark(startTag) } // 标记是vue的实例 无需重新new vm._isVue = true // 跳过观测 vm.__v_skip = true // 用于收集副作用 vm._scope = new EffectScope(true /* detached */) // 对父组件产生副作用 vm._scope.parent = undefined vm._scope._vm = true // 合并配置 if (options && options._isComponent) { // 跳过内置组件的合并 initInternalComponent(vm, options as any) } else { // 合并构造函数的配置到组件上 // 校验props inject 的属性 vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor as any), options || {}, vm ) } /* istanbul ignore else */ if (__DEV__) { initProxy(vm) } else { vm._renderProxy = vm } // 保留实例到_self上 vm._self = vm // 关联组件父子关系 initLifecycle(vm) // 新增_events属性 initEvents(vm) // 新增$slots $scopedSlots $createElement 等方法 initRender(vm) // 触发生命周期函数 callHook(vm, 'beforeCreate', undefined, false /* setContext */) // 初始化inject initInjections(vm) // // 初始化prop data computed watch等 事关数据合并覆盖 initState(vm) // 初始化Provider,里面会有查找过程 initProvide(vm) // 触发created 钩子 callHook(vm, 'created') /* istanbul ignore if */ if (__DEV__ && config.performance && mark) { vm._name = formatComponentName(vm, false) mark(endTag) measure(`vue ${vm._name} init`, startTag, endTag) } // 如果已经挂载过了执行$mount if (vm.$options.el) { vm.$mount(vm.$options.el) } } }
-
initInternalComponent 实现
-
export function initInternalComponent( vm: Component, options: InternalComponentOptions ) { const opts = (vm.$options = Object.create((vm.constructor as any).options)) // 合并父组件实例和 VNode const parentVnode = options._parentVnode opts.parent = options.parent opts._parentVnode = parentVnode // 提取组件 VNode 中的关键数据 const vnodeComponentOptions = parentVnode.componentOptions! opts.propsData = vnodeComponentOptions.propsData opts._parentListeners = vnodeComponentOptions.listeners opts._renderChildren = vnodeComponentOptions.children opts._componentTag = vnodeComponentOptions.tag // 处理渲染函数(如动态插槽) if (options.render) { opts.render = options.render opts.staticRenderFns = options.staticRenderFns } }
-
-
src/core/instance/lifecycle.ts - initLifecycle 实现
- 关联组件父子关系,新增children,_watcher等属性
-
export function initLifecycle(vm: Component) { const options = vm.$options // 查找父组件 并且往父元素children属性插入当前组件 let parent = options.parent if (parent && !options.abstract) { while (parent.$options.abstract && parent.$parent) { parent = parent.$parent } parent.$children.push(vm) } // 记录父组件 vm.$parent = parent // 记录根节点 使用父元素的$root 如果自己是跟根节点使用自己做为$root vm.$root = parent ? parent.$root : vm vm.$children = [] vm.$refs = {} // 记录Provider链条 vm._provided = parent ? parent._provided : Object.create(null) // 初始化一些其他的私有属性 vm._watcher = null vm._inactive = null vm._directInactive = false vm._isMounted = false vm._isDestroyed = false vm._isBeingDestroyed = false }
-
src/core/instance/events.ts - initEvents 实现
- 新增_events 属性,调用updateComponentListeners,更新父组件listeners
-
export function initEvents(vm: Component) { vm._events = Object.create(null) vm._hasHookEvent = false // 获取父组件传递的 自定义事件 // 子组件需要保留这些自定义事件,以便后续执行 const listeners = vm.$options._parentListeners if (listeners) { updateComponentListeners(vm, listeners) } }
-
src/core/instance/render.ts - initRender 实现
-
export function initRender(vm: Component) { // 当前渲染的 VNode vm._vnode = null // 静态树缓存 vm._staticTrees = null const options = vm.$options // 获取父组件虚拟dom const parentVnode = (vm.$vnode = options._parentVnode!) // 父组件上下文 const renderContext = parentVnode && (parentVnode.context as Component) // 获取插槽需要渲染的内容 vm.$slots = resolveSlots(options._renderChildren, renderContext) // 初始化 $scopedSlots // 编译为函数,存储在 vm.$scopedSlots,延迟到子组件渲染时执行 vm.$scopedSlots = parentVnode ? normalizeScopedSlots( vm.$parent!, parentVnode.data!.scopedSlots, vm.$slots ) : emptyObject // 保留虚拟dom创建函数 vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false) vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true) // 获取父组件的data数据 const parentData = parentVnode && parentVnode.data /* istanbul ignore else */ if (__DEV__) { 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 { // 把$attrs和$listeners变为响应式数据 defineReactive( vm, '$attrs', (parentData && parentData.attrs) || emptyObject, null, true ) defineReactive( vm, '$listeners', options._parentListeners || emptyObject, null, true ) } }
-
-
src/core/instance/lifecycle.ts - callHook 实现
-
export function callHook( vm: Component, hook: string, args?: any[], setContext = true ) { // #7573 disable dep collection when invoking lifecycle hooks pushTarget() const prevInst = currentInstance const prevScope = getCurrentScope() setContext && setCurrentInstance(vm) 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, args || null, vm, info) } } if (vm._hasHookEvent) { vm.$emit('hook:' + hook) } if (setContext) { setCurrentInstance(prevInst) prevScope && prevScope.on() } popTarget() }
-
-
src/core/instance/inject.ts - initInjections 实现
- inject早于prop 和 data
-
export function initInjections(vm: Component) { const result = resolveInject(vm.$options.inject, vm) if (result) { toggleObserving(false) Object.keys(result).forEach(key => { /* istanbul ignore else */ if (__DEV__) { defineReactive(vm, key, result[key], () => { warn( `Avoid mutating an injected value directly since the changes will be ` + `overwritten whenever the provided component re-renders. ` + `injection being mutated: "${key}"`, vm ) }) } else { defineReactive(vm, key, result[key]) } }) toggleObserving(true) } } export function resolveInject( inject: any, vm: Component ): Record<string, any> | undefined | null { if (inject) { // inject is :any because flow is not smart enough to figure out cached 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] // #6574 in case the inject object is observed... if (key === '__ob__') continue const provideKey = inject[key].from if (provideKey in vm._provided) { result[key] = vm._provided[provideKey] } else if ('default' in inject[key]) { const provideDefault = inject[key].default result[key] = isFunction(provideDefault) ? provideDefault.call(vm) : provideDefault } else if (__DEV__) { warn(`Injection "${key as string}" not found`, vm) } } return result } }
-
src/core/instance/state.ts - initState
- 初始化data prop等数据配置
-
export function initState(vm: Component) { const opts = vm.$options if (opts.props) initProps(vm, opts.props) // Composition API initSetup(vm) if (opts.methods) initMethods(vm, opts.methods) if (opts.data) { initData(vm) } else { const ob = observe((vm._data = {})) ob && ob.vmCount++ } if (opts.computed) initComputed(vm, opts.computed) if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) } }
-
src/core/instance/inject.ts - initProvide 实现
-
import { resolveProvided } from 'v3/apiInject' export function initProvide(vm: Component) { const provideOption = vm.$options.provide if (provideOption) { const provided = isFunction(provideOption) ? provideOption.call(vm) : provideOption if (!isObject(provided)) { return } const source = resolveProvided(vm) // IE9 doesn't support Object.getOwnPropertyDescriptors so we have to // iterate the keys ourselves. const keys = hasSymbol ? Reflect.ownKeys(provided) : Object.keys(provided) for (let i = 0; i < keys.length; i++) { const key = keys[i] Object.defineProperty( source, key, Object.getOwnPropertyDescriptor(provided, key)! ) } } }
-
src/core/instance/state.ts - stateMixin实现
-
实现 props $set等原型方法
-
export function stateMixin(Vue: typeof Component) { const dataDef: any = {} dataDef.get = function () { return this._data } const propsDef: any = {} propsDef.get = function () { return this._props } if (__DEV__) { dataDef.set = function () { warn( 'Avoid replacing instance root $data. ' + 'Use nested data properties instead.', this ) } propsDef.set = function () { warn(`$props is readonly.`, this) } } // 对$data进行get处理, 禁止set访问 Object.defineProperty(Vue.prototype, '$data', dataDef) Object.defineProperty(Vue.prototype, '$props', propsDef) // 挂载$set 和 $delete Vue.prototype.$set = set Vue.prototype.$delete = del Vue.prototype.$watch = function ( expOrFn: string | (() => any), cb: any, options?: Record<string, any> ): Function { const vm: Component = this // cb如果是对象 需要进行拆解 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() } } }
-
-
src/core/instance/state.ts - createWatcher实现
-
function createWatcher( vm: Component, expOrFn: string | (() => any), handler: any, options?: Object ) { // 拆解hanlder 和 options 再次调用$watch if (isPlainObject(handler)) { options = handler handler = handler.handler } if (typeof handler === 'string') { handler = vm[handler] } return vm.$watch(expOrFn, handler, options) }
-
src/core/instance/events.ts - eventsMixin 实现
- 事项emit off 等原型方法
export function eventsMixin(Vue: typeof Component) {
const hookRE = /^hook:/
// $on 支持一对多和一对一,一对多需要遍历数据进行监听
Vue.prototype.$on = function (
event: string | Array<string>,
fn: Function
): Component {
const vm: Component = this
if (isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn)
}
} else {
;(vm._events[event] || (vm._events[event] = [])).push(fn)
// 这里做了优化 判断key是否存在
if (hookRE.test(event)) {
vm._hasHookEvent = true
}
}
return vm
}
// 内部包装了一下,调用完成后解除订阅
Vue.prototype.$once = function (event: string, fn: Function): Component {
const vm: Component = this
function on() {
vm.$off(event, on)
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
}
// 删除订阅
Vue.prototype.$off = function (
event?: string | Array<string>,
fn?: Function
): Component {
const vm: Component = this
// all
if (!arguments.length) {
vm._events = Object.create(null)
return vm
}
// array of events
if (isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$off(event[i], fn)
}
return vm
}
// specific event
const cbs = vm._events[event!]
if (!cbs) {
return vm
}
if (!fn) {
vm._events[event!] = null
return vm
}
// specific handler
let cb
let i = cbs.length
while (i--) {
cb = cbs[i]
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break
}
}
return vm
}
// 触发订阅
Vue.prototype.$emit = function (event: string): Component {
const vm: Component = this
if (__DEV__) {
const lowerCaseEvent = event.toLowerCase()
if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
tip(
`Event "${lowerCaseEvent}" is emitted in component ` +
`${formatComponentName(
vm
)} but the handler is registered for "${event}". ` +
`Note that HTML attributes are case-insensitive and you cannot use ` +
`v-on to listen to camelCase events when using in-DOM templates. ` +
`You should probably use "${hyphenate(
event
)}" instead of "${event}".`
)
}
}
let cbs = vm._events[event]
if (cbs) {
cbs = cbs.length > 1 ? toArray(cbs) : cbs
const args = toArray(arguments, 1)
const info = `event handler for "${event}"`
for (let i = 0, l = cbs.length; i < l; i++) {
invokeWithErrorHandling(cbs[i], vm, args, vm, info)
}
}
return vm
}
}
src/core/instance/lifecycle.ts - lifecycleMixin 实现
- 挂载 _update destroy 等方法,实现虚拟dom到真实dom的更新
- $destroy 实现销毁逻辑,触发销毁声明周期
export function lifecycleMixin(Vue: typeof Component) {
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
const vm: Component = this
const prevEl = vm.$el
const prevVnode = vm._vnode
const restoreActiveInstance = setActiveInstance(vm)
vm._vnode = vnode
// 首次渲染 将 VNode 渲染为真实 DOM
if (!prevVnode) {
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
} else {
// 比较新旧 VNode,局部更新 当前DOM
vm.$el = vm.__patch__(prevVnode, vnode)
}
restoreActiveInstance()
// update __vue__ reference
if (prevEl) {
prevEl.__vue__ = null
}
if (vm.$el) {
vm.$el.__vue__ = vm
}
let wrapper: Component | undefined = vm
// 进行新旧节点比较
while (
wrapper &&
wrapper.$vnode &&
wrapper.$parent &&
wrapper.$vnode === wrapper.$parent._vnode
) {
wrapper.$parent.$el = wrapper.$el
wrapper = wrapper.$parent
}
}
// 强制刷新
Vue.prototype.$forceUpdate = function () {
const vm: Component = this
if (vm._watcher) {
vm._watcher.update()
}
}
Vue.prototype.$destroy = function () {
const vm: Component = this
// 已经销毁不再执行
if (vm._isBeingDestroyed) {
return
}
// 触发 beforeDestroy
callHook(vm, 'beforeDestroy')
// 开始销毁
vm._isBeingDestroyed = true
//从父组件移除
const parent = vm.$parent
if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
remove(parent.$children, vm)
}
vm._scope.stop()
if (vm._data.__ob__) {
vm._data.__ob__.vmCount--
}
vm._isDestroyed = true
// 更新真实dom
vm.__patch__(vm._vnode, null)
// 触发卸载完成
callHook(vm, 'destroyed')
// 卸载订阅函数
vm.$off()
//
if (vm.$el) {
vm.$el.__vue__ = null
}
// 移除父组件绑定
if (vm.$vnode) {
vm.$vnode.parent = null
}
}
}
src/core/instance/render.ts - renderMixin 实现
- 挂载原型方法 $nextTick 和 _render
- 往原型上增加一些辅助函数
export function renderMixin(Vue: typeof Component) {
// 挂载一些辅助函数
installRenderHelpers(Vue.prototype)
// 挂载nextTick函数
Vue.prototype.$nextTick = function (fn: (...args: any[]) => any) {
return nextTick(fn, this)
}
// 挂载render函数
Vue.prototype._render = function (): VNode {
const vm: Component = this
// loader编译完后会把template变为render函数
const { render, _parentVnode } = vm.$options
// 执行slot和scopeSlot
if (_parentVnode && vm._isMounted) {
vm.$scopedSlots = normalizeScopedSlots(
vm.$parent!,
_parentVnode.data!.scopedSlots,
vm.$slots,
vm.$scopedSlots
)
if (vm._slotsProxy) {
syncSetupSlots(vm._slotsProxy, vm.$scopedSlots)
}
}
// 标记当前组件在父组件中的位置
vm.$vnode = _parentVnode!
// render self
const prevInst = currentInstance
const prevRenderInst = currentRenderingInstance
let vnode
try {
setCurrentInstance(vm)
currentRenderingInstance = vm
// 调用用户定义的 render 函数或编译后的模板函数
vnode = render.call(vm._renderProxy, vm.$createElement)
} catch (e: any) {
handleError(e, vm, `render`)
// return error render result,
// or previous vnode to prevent render error causing blank component
/* istanbul ignore else */
if (__DEV__ && vm.$options.renderError) {
try {
vnode = vm.$options.renderError.call(
vm._renderProxy,
vm.$createElement,
e
)
} catch (e: any) {
handleError(e, vm, `renderError`)
vnode = vm._vnode
}
} else {
vnode = vm._vnode
}
} finally {
currentRenderingInstance = prevRenderInst
setCurrentInstance(prevInst)
}
// 确保返回的是合法的 VNode 必须是单根节点
if (isArray(vnode) && vnode.length === 1) {
vnode = vnode[0]
}
// return empty vnode in case the render function errored out
if (!(vnode instanceof VNode)) {
if (__DEV__ && isArray(vnode)) {
warn(
'Multiple root nodes returned from render function. Render function ' +
'should return a single root node.',
vm
)
}
vnode = createEmptyVNode()
}
// 设置当前实例的 _vnode,用于后续的 patch 阶段
vnode.parent = _parentVnode
return vnode
}
}