一、Vue构造函数
本篇第一章我们来找一下
Vue
源码中的构造函数的定义。
(一)src/platforms/runtime-with-compiler.ts
在platforms
目录下的代码都是和平台相关的代码,上一篇我们说到,在runtime-with-compiler.ts
文件中,定义了$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
这个文件中通过import
导入了Vue
,文件绝对路径:src/platforms/web/runtime/index.ts
,
(二)src/platforms/web/runtime/index.ts
我们进入这个文件中查看,下面是几行关键代码:
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
这个文件主要用于给平台注册特有的指令和组件,并且给Vue
的prototype
定义了$mount
方法,它的Vue也是从外界导入的,我们再去到core/index
中查看
(三)src/core/index.ts
以下是几行关键代码:
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
这个方法用于初始化Vue
实例的静态成员,并且给Vue.prototype
添加了几个运行环境相关的属性。但是它的Vue
也是通过外部引入的,我们在到./instance/index
中查看Vue
(四)src/core/instance/index.ts
在这里终于看到了Vue
的构造函数
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)
这个文件的作用是定义Vue
构造函数,以及给Vue
混入常见的实例成员
二、Vue静态成员
这一章我们来介绍一下Vue
中静态成员的定义。Vue的静态成员主要是在src/core/global-api/index.ts
文件的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
}
}
})
}
三、Vue实例成员
src/core/instance/init.ts
文件中定义了Vue
的实例成员,是给Vue.prototype
上添加成员,Vue
实例对象可以通过原型链继承,文件中通过一下几个函数分块对实例成员进行初始化:
// 给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)
实现的关键的功能如下图所示:
其中initMixin(Vue)
给Vue
实例增加了_init()
方法,是Vue
实例初始化的方法,在Vue
的构造函数中会执行这个方法。我们再来看_init()
方法的具体功能:
四、调试Vue初始化过程
Vue
初始化过程主要在第一章的四个文件中进行,完成了实例成员的初始化和Vue
静态方法的初始化,通过断掉调试,大致的执行过程如下:
五、调试首次渲染过程
上述第三章已经画过了Vue.proptotype._init()
的思维导图,这个函数的最后是vm.$mount()
方法,首次渲染的过程从这个函数开始调试
六、扩展
1、调试技巧:在chrome浏览器的watch栏点击小+输入要查看的数据,在断点调试的过程中就可以一直查看该数据的变化。