1. Vue constructor
In the first chapter of this article, let’s look at
Vue
the definition of the constructor in the source code.
(one)src/platforms/runtime-with-compiler.ts
The codes in platforms
the directory are all platform-related codes. As we mentioned in the previous article, in the file, methods runtime-with-compiler.ts
are defined and converted into functions. The key codes in this file are:$mount
html
render
import Vue from './runtime/index'
const mount = Vue.prototype.$mount
// 重写$mount方法
Vue.prototype.$mount = xx;
// 给vue添加compile方法,将html字符串转换成render函数
Vue.compile = compileToFunctions
export default Vue as GlobalAPI
This file is import
imported Vue
, the absolute path of the file: src/platforms/web/runtime/index.ts
,
(two)src/platforms/web/runtime/index.ts
Let's go into this file to check. Here are a few key lines of code:
import Vue from 'core/index'
// 注册平台相关的指令和组件
// extend:将参数二扩展到参数一上
extend(Vue.options.directives, platformDirectives)
// Vue.options.components存储全局组件
extend(Vue.options.components, platformComponents)
// 给Vue原型上添加$mount方法
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
export default Vue
This file is mainly used to register unique instructions and components for the platform, and defines Vue
methods . Its Vue is also imported from the outside world. Let’s check it out inprototype
$mount
core/index
(three)src/core/index.ts
Here are a few key lines of code:
import Vue from './instance/index'
import {
initGlobalAPI } from './global-api/index'
// 初始化Vue的静态成员
initGlobalAPI(Vue)
// 给Vue.prototype添加属性
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})
export default Vue
This method is used to initialize Vue
the static members of the instance and add Vue.prototype
several properties related to the running environment. But it Vue
is also introduced from the outside. We ./instance/index
look at Vue in
(Four)src/core/instance/index.ts
Here we finally see Vue
the constructor
function Vue(options) {
if (__DEV__ && !(this instanceof Vue)) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
// 调用_init方法
this._init(options)
}
// 给Vue初始化init方法
initMixin(Vue)
// 注册vm 的 $data/$props/$set/$delete/$watch
stateMixin(Vue)
// 初始化事件相关方法
eventsMixin(Vue)
// 初始化生命周期相关的混入方法
lifecycleMixin(Vue)
//@ts-expect-error Vue has function type
// 混入render方法
renderMixin(Vue)
The purpose of this file is to define Vue
the constructor and Vue
mix in common instance members.
2. Vue static members
In this chapter, we will introduce Vue
the definition of static members. Vue's static members are mainly defined in src/core/global-api/index.ts
file methods, and their functions are as shown in the figure below: Source code:initGlobalAPI()
initGlobalAPI()
initGlobalAPI()
export function initGlobalAPI(Vue: GlobalAPI) {
// config
const configDef: Record<string, any> = {
}
configDef.get = () => config
// 非生产环境下,不允许调用config的set方法修改Vue.config
if (__DEV__) {
configDef.set = () => {
warn(
'Do not replace the Vue.config object, set individual fields instead.'
)
}
}
// 初始化Vue.config静态属性
Object.defineProperty(Vue, 'config', configDef)
// Vue内部使用的方法,不建议外部使用
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
// 静态方法
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
// 2.6 explicit observable API
// 设置响应式数据
Vue.observable = <T>(obj: T): T => {
observe(obj)
return obj
}
// 初始化Vue.options静态属性,并给其扩展
// 初始化Vue.options.components、Vue.options.directives、Vue.options.filters
// 存储全局组件、指令、过滤器
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
// 记录当前Vue构造函数
Vue.options._base = Vue
// 把builtInComponents对象属性拷贝到Vue.options.components中
// 注册全局组件keep-alive
extend(Vue.options.components, builtInComponents)
// 初始化Vue.use静态方法,用来注册插件
initUse(Vue)
// 初始化Vue.mixin静态方法,实现混入
initMixin(Vue)
// 初始化Vue.extend静态方法,基于传入的options返回一个组件的构造函数
initExtend(Vue)
// 初始化Vue.component、Vue.directive、Vue.filter静态方法
initAssetRegisters(Vue)
}
initUse(Vue)
export function initUse(Vue: GlobalAPI) {
// Vue.use方法
// plugin:要注册的插件
Vue.use = function (plugin: Function | any) {
// this指向Vue构造函数
const installedPlugins =
this._installedPlugins || (this._installedPlugins = [])
if (installedPlugins.indexOf(plugin) > -1) {
// 已经注册过了,直接返回
return this
}
// 注册插件就是调用插件
// 如果插件是一个对象,那么调用其install方法
// 如果插件是一个函数,那么直接调用
// arguments是调用Vue.use时传入的参数,第一个参数一定是插件,
// 后面的参数是传给插件的参数
const args = toArray(arguments, 1)
// 把Vue构造函数添加到参数的第一个位置
args.unshift(this)
if (isFunction(plugin.install)) {
// apply将数组展开成一个个参数
plugin.install.apply(plugin, args)
} else if (isFunction(plugin)) {
plugin.apply(null, args)
}
// 把插件添加到已注册插件列表中
installedPlugins.push(plugin)
return this
}
}
initMixin(Vue)
export function initMixin(Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
// 把传过来的选项传到Vue.options中
// 注册全局选项
this.options = mergeOptions(this.options, mixin)
return this
}
}
initExtend(Vue)
export function initExtend(Vue: GlobalAPI) {
// 每个实例构造器,包括Vue,都有一个唯一的cid,便于缓存和创建子构造函数
Vue.cid = 0
let cid = 1
// 给Vue添加extend方法
Vue.extend = function (extendOptions: any): typeof Component {
extendOptions = extendOptions || {
}
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {
})
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
const name =
getComponentName(extendOptions) || getComponentName(Super.options)
if (__DEV__ && name) {
validateComponentName(name)
}
// 创建组件构造函数
const Sub = function VueComponent(this: any, options: any) {
this._init(options)
} as unknown as typeof Component
// 继承Vue
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
// 合并选项
Sub.options = mergeOptions(Super.options, extendOptions)
Sub['super'] = Super
// 对于props,定义到Sub.prototype._props上
if (Sub.options.props) {
initProps(Sub)
}
// computed定义到Sub.prototype上
if (Sub.options.computed) {
initComputed(Sub)
}
// 初始化extension/mixin/plugin
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// 初始化component()、directive()、filter()
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// 如果传进来的选项有name属性,将构造函数存储到options中
if (name) {
Sub.options.components[name] = Sub
}
//存储父类的options选项
Sub.superOptions = Super.options
// 传进来的选项
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({
}, Sub.options)
// 缓存 constructor
cachedCtors[SuperId] = Sub
// 返回组件构造函数
return Sub
}
}
initAssetRegisters(Vue)
export function initAssetRegisters(Vue: GlobalAPI) {
// ASSET_TYPES:['component', 'directive', 'filter]
// 这几个方法使用的时候都是Vue.component('xxx', definition)
// definition: Function | Object
// 如果没有传definition,那么就是获取全局组件、指令、过滤器
ASSET_TYPES.forEach(type => {
Vue[type] = function (
id: string,
definition?: Function | Object
): Function | Object | void {
if (!definition) {
// 如果没有第二个参数,获取对应id组件、指令、过滤器
return this.options[type + 's'][id]
} else {
if (__DEV__ && type === 'component') {
validateComponentName(id)
}
// isPlainObject:判断是否是纯对象,区分于函数、数组、null、Date、RegExp等
if (type === 'component' && isPlainObject(definition)) {
// 给组件定义name属性,如果没有name属性,就使用id
definition.name = definition.name || id
// this.options._base:Vue构造函数
// Vue.extend:根据传入的选项,返回一个组件的构造函数
// 所以这里的definition是一个组件的构造函数
definition = this.options._base.extend(definition)
}
if (type === 'directive' && isFunction(definition)) {
definition = {
bind: definition, update: definition }
}
// 给组件、指令、过滤器添加到Vue.options中
// 直接传递Vue.extend()构造函数会直接将构造函数存储到options中
this.options[type + 's'][id] = definition
return definition
}
}
})
}
3. Vue instance members
src/core/instance/init.ts
Vue
The instance members defined in the file are Vue.prototype
added members. Vue
The instance object can be inherited through the prototype chain. The instance members are initialized in the file through the following function blocks:
// 给Vue的实例对象上增加init方法
initMixin(Vue)
// 注册vm 的 $data/$props/$set/$delete/$watch
stateMixin(Vue)
// 初始化vm中事件相关方法
eventsMixin(Vue)
// 初始化生命周期相关的混入方法
// _update/$forceUpdate/$destroy
// _update中调用了__patch__方法对比差异更新DOM
lifecycleMixin(Vue)
// 混入render方法
renderMixin(Vue)
The key functions implemented are shown in the figure below: a method is added to
the instance, which is the instance initialization method. This method will be executed in the constructor. Let’s look at the specific functions of the method:initMixin(Vue)
Vue
_init()
Vue
Vue
_init()
4. Debugging the Vue initialization process
Vue
Vue
The initialization process is mainly carried out in the four files in Chapter 1. The initialization of instance members and the initialization of static methods are completed . By disconnecting debugging, the general execution process is as follows:
5. Debugging the first rendering process
Vue.proptotype._init()
In the mind map that has been drawn in Chapter 3 above , the last part of this function is vm.$mount()
the method. The first rendering process starts from this function.
6. Extension
1. Debugging skills: Click the small + in the watch bar of the Chrome browser and enter the data you want to view. You can always view the changes in the data during breakpoint debugging.