Vue 2.6.13 源码分析
Posted 白瑕
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue 2.6.13 源码分析相关的知识,希望对你有一定的参考价值。
文章目录
前言
Vue 2.6.13 源码分析 (一)
你需要有Vue 2.6.13的包.
https://github.com/vuejs/vue/releases/tag/v2.6.13
一、入口
1.index.js
src/core/instance/index.js
负责Vue配置对象的直接接收, 即
new Vue()
需要传入options
配置对象:
也就是el
, data
之类的.
import initMixin from './init'
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) // 该处this指向Vue构造函数
// _init确实定义于构造函数Vue上, 只不过并非现在而是在构造函数Vue带着参数传入initMixin之后
initMixin(Vue) // 构造函数Vue与initMinxin建立联系以将option传入initMixin
2.init.js
src/core/instance/init.js
initMixin
所在文件, 先看options
传值, initMixin
接受构造函数Vue, 然后将_init
方法定义到Vue构造函数原型上, 并接受参数.
export function initMixin (Vue: Class<Component>)
Vue.prototype._init = function (options?: Object)
// ...函数体
然后将外层initMixin
剥离:
Vue.prototype._init = function (options?: Object)
const vm: Component = this // vm: Component为ts类型注释写法, 忽略看作const声明变量即可.
// vm: VueModel
// this指向Vue.prototype即本作用域对应函数
vm._uid = uid++
// 每个VueModel都有一个自己的uid编号
if (options && options._isComponent)
// 如果配置对象存在且存在于子组件中
initInternalComponent(vm, options) // 支线
else
// 如果配置对象存在且存在于根组件中
vm.$options = mergeOptions( // 支线
resolveConstructorOptions(vm.constructor), // 支线
options || ,
vm
)
// expose real self
vm._self = vm
if (vm.$options.el)
// 如果配置对象内存在el属性,则调用$mount方法, 即配置对象内存在el属性就不需要手动调用 $mount
vm.$mount(vm.$options.el)
二、支线
1.initInternalComponent
一堆赋值操作
参数:
vm: Vue.prototype
options: _init()参数options
完整函数:
export function initInternalComponent (vm: Component, options: InternalComponentOptions)
const opts = vm.$options = Object.create(vm.constructor.options)
// 将以Vue构造函数对象作为原型生成的对象挂载到组件实例vm.$options的__proto__
const parentVnode = options._parentVnode
opts.parent = options.parent // 组件根实例挂载到组件实例
opts._parentVnode = parentVnode
const vnodeComponentOptions = parentVnode.componentOptions
opts.propsData = vnodeComponentOptions.propsData
opts._parentListeners = vnodeComponentOptions.listeners
opts._renderChildren = vnodeComponentOptions.children
opts._componentTag = vnodeComponentOptions.tag
if (options.render) // 如果有render, 把render相关挂载到$options
opts.render = options.render
opts.staticRenderFns = options.staticRenderFns
指定组件$options原型, 把组件依赖于父组件的props、listeners挂载到options上,方便子组件调用.
2.resolveConstructorOptions
函数全貌, Ctor
是vm.constrator
即Vue构造函数.
export function resolveConstructorOptions (Ctor: Class<Component>)
let options = Ctor.options // `vm.constructor = Vue.prototype.constructor = Vue`
if (Ctor.super) // 有super属性,说明Ctor是Vue.extend构建的子类, 进入递归
// Vue.extend构建的子类为何会有super属性见下
const superOptions = resolveConstructorOptions(Ctor.super) // sub
const cachedSuperOptions = Ctor.superOptions
if (superOptions !== cachedSuperOptions)
Ctor.superOptions = superOptions
const modifiedOptions = resolveModifiedOptions(Ctor)
if (modifiedOptions)
extend(Ctor.extendOptions, modifiedOptions)
options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
if (options.name)
options.components[options.name] = Ctor
return options // 如果Ctor.super不存在就直接返回options, 比如new Vue()创建的情况
2.1.Vue
, this
, Vue.prototype.constructor
分别是什么?
const options =
el: '#app',
data:
msg: 'hello vue'
function Vue(options)
this._init(options)
Vue.prototype._init = function ()
const vm = this
console.log(Vue)
console.log(this)
console.log(Vue.prototype.constructor)
new Vue()
2.2.什么情况下会有super?
可见当Ctor.super
存在时才会进行resolveConstructorOptions
的递归操作, 而使用new extend()
创建组件的时候vm.constructor
才会有super
.
new extend()
使用基础Vue构造器创建子类, 参数是一个组件配置对象.
src/core/global-api/extend.js
完整函数:
Vue.extend = function (extendOptions: Object): Function
extendOptions = extendOptions ||
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = )
if (cachedCtors[SuperId])
return cachedCtors[SuperId]
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name)
validateComponentName(name)
const Sub = function VueComponent (options)
this._init(options)
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
Sub.options = mergeOptions(
Super.options,
extendOptions
)
Sub['super'] = Super
if (Sub.options.props)
initProps(Sub)
if (Sub.options.computed)
initComputed(Sub)
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
ASSET_TYPES.forEach(function (type)
Sub[type] = Super[type]
)
if (name)
Sub.options.components[name] = Sub
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend(, Sub.options)
// cache constructor
cachedCtors[SuperId] = Sub
return Sub
简化:
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = )
if (cachedCtors[SuperId])
return cachedCtors[SuperId]
const Sub = function VueComponent (options)
this._init(options)
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub['super'] = Super
return Sub
Sub['super'] = Super
定义super
属性, 值为父类构造函数Vue.
2.3.Sub最后成为了什么?
仍旧src/core/global-api/extend.js
简化版:
Vue.extend = function (extendOptions: Object): Function
extendOptions = extendOptions || // vm.constructor
const Super = this // 将Super指向Vue构造函数, 为后面建Sub子类准备资源
const Sub = function VueComponent (options) // 组件构造函数Sub
this._init(options)
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.options = mergeOptions( // 记住这个Sub.options
Super.options,
extendOptions
)
Sub['super'] = Super
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions // 用作子类变化对照组
Sub.sealedOptions = extend(, Sub.options)
return Sub
构造函数Sub
我抽出来写了一下, _init()
并不像new Vue()
那样可以在initMixin
里定义, 会报undefined
, 而且这个Sub
现在什么都没有就是个空函数一样的东西.
所以Sub
构造函数定义之后的下一步立马就是把Super
的原型, 也就是Vue.prototype
拿过来当自己的prototype
用, Object.create()
基于Vue构造函数的原型创建一个对象, 作为组件构造函数Sub的原型, 这样Sub就是一个Vue构造函数复制品, 其内部也会拥有_init()
方法可以进入initMixin()
执行从而到达Ctor.super
判定.
Sub.prototype
刚赋值完和Vue构造函数一模一样, constructor
要指向Sub构造函数
才好, 所以接着把Sub.prototype指向Sub
构造函数.
之后就是往Sub
上挂载属性了.
2.4.super存在, resolveConstructorOptions如何执行
Vue.extend()
使用Vue构造器创建一个子类, 这个子类有一大部分基于构造函数Vue这个父类.
这其中牵扯到一个比较, 先用Ctor.super
拿到父构造函数传入resolveConstructorOptions
拿到父类的options
存下来, 然后再Ctor.superOptions
直接将自己的options
存下来, 然后父子options比较
, 如果不同的话就是父类的options
发生了改变, 由于extend
里说sub
的时候说到子类大部基于父类所以子类需要伴随父类的更新而做出更新.
子类直接被赋值更新, 完成之后使用resolveModifiedOptions
检测子类是否发生变化.
export function resolveConstructorOptions (Ctor: Class<Component>)
let options = Ctor.options
if (Ctor.super) // 继续递归的前提是有super, 没有就直接返回.
const superOptions = resolveConstructorOptions(Ctor.super) // 传入Ctor.Super即父类构造函数拿到父options
const cachedSuperOptions = Ctor.superOptions // 将自身的options存下
if (superOptions !== cachedSuperOptions) // 如果superOptions不等于最新的superOptions
Ctor.superOptions = superOptions
// 那么更新子类, 更新完super必存在必定还要递归一次所以不用担心构造函数的问题
const modifiedOptions = resolveModifiedOptions(Ctor) // 夫子类变动校验
if (modifiedOptions)
// 如果resolveModifiedOptions返回正常值, 那么将子类对照与Ctor.extendOptions与存异项合并
extend(Ctor.extendOptions, modifiedOptions)
options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions) // 新版子类与子类对照合并
if (options.name)
options.components[options.name] = Ctor
return options
Ctor.super
初始为构造函数Vue, 从最开始首次调用的父类构造函数Vue开始, 每轮递归都把父类最新的options
赋值给superOptions
.
3.resolveModifiedOptions
一个校验方法, 在相同时就不执行extend(Ctor.extendOptions, modifiedOptions)
function resolveModifiedOptions (Ctor: Class<Component>): ?Object
let modified
const latest = Ctor.options // 自己, 或者说子类的options
const sealed = Ctor.sealedOptions // 执行extends时封的options, 没有变化过, 专门用来对照子类自身变化
for (const key in latest)
if (latest[key] !== sealed[key])
if (!modified) modified = // 他这个modifined在完全相同的时候是个`undefined`, 不同的时候会用对象形式列出第一个不同的项
modified[key] = latest[key]
return modified
测试:
function resolveModifiedOptions()
let modified
const latest =
a: 2,
b: 3,
c: 8
// 自己, 或者说子类的options
const sealed =
a: 1,
b: 4,
c: 3
// 执行extends时封的options, 没有变化过, 专门用来对照子类自身变化
for (const key in latest)
if (latest[key] !== sealed[key])
if (!modified)
if (!modified) modified =
modified[key] = latest[key]
console.log(modified)
return modified
resolveModifiedOptions()
4.mergeOptions
合并两选项, 出现相同配置项, 那么子类中的选项会覆盖父类中的选项.
子类最初直接源自Vue构造函数对象, 它需要尽可能发展自己的特色, 能用自己的就用自己的.
参数:
parent: 父类构造函数对象
child: 子类构造函数对象
vm: (可选)组件实例
完整函数:
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object
if (process.env.NODE_ENV !== 'production')
checkComponents(child)
if (typeof child === 'function')
child = child.options
normalizeProps(child, vm) // 标准化props
normalizeInject(child, vm) // 标准化inject
normalizeDirectives(child) // 标准化directive
if (!child._base)
if (child.extends) //处理子类对象上的extends, 将继承而来的选项合并到parent
parent = mergeOptions(parent, child.extends, vm)
if (child.mixins) //处理子类对象上的mixins, 将继承而来的选项合并到parent
for (let i = 0, l = child.mixins.length; i < l; i++)
parent = mergeOptions(parent, child.mixins[i], vm)
const options =
let key
for (key in parent)
mergeField(key)
for (key in child)
if (!hasOwn(parent, key))
mergeField(key)
function mergeField (key)
const strat = strats[key] || defaultStrat // strats受引入的合并策略集
// strat函数, 履行特定合并合并策略的合并函数
options[key] = strat(parent[key], child[key], vm, key)
return options
总结
–
以上是关于Vue 2.6.13 源码分析的主要内容,如果未能解决你的问题,请参考以下文章