全局API,文件定位: vue/src/core/global-api
assets.js
定义全局 component, directive, filter(在Vue3已被弃用,尽量减少使用)
Vue.component(id, Function | Object): 注册或获取全局组件,注册还会自动使用给定的id设置组件的名称Vue.directive(id, Function | Object): 注册或获取全局指令Vue.filter(id, Function | Object): 注册或获取全局过滤器
export const ASSET_TYPES = [ // 资源类型
'component',
'directive',
'filter'
]
// 验证组件名是否合法
export function validateComponentName (name: string) {
if (!new RegExp(`^[a-zA-Z][\\-\\.0-9_${unicodeRegExp.source}]*$`).test(name)) {
warn(
'Invalid component name: "' + name + '". Component names ' +
'should conform to valid custom element name in html5 specification.'
)
}
if (isBuiltInTag(name) || config.isReservedTag(name)) {
warn(
'Do not use built-in or reserved HTML elements as component ' +
'id: ' + name
)
}
}
import { ASSET_TYPES } from 'shared/constants'
import { isPlainObject, validateComponentName } from '../util/index'
export function initAssetRegisters (Vue: GlobalAPI) {
/**
* Create asset registration methods.
*/
ASSET_TYPES.forEach(type => {
Vue[type] = function (
id: string,
definition: Function | Object
): Function | Object | void {
if (!definition) {
return this.options[type + 's'][id]
} else {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && type === 'component') {
validateComponentName(id)
}
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id
definition = this.options._base.extend(definition)
}
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition }
}
this.options[type + 's'][id] = definition
return definition
}
}
})
}
- 如果
type是 组件component, 并且definition是一个对象的时候, 会自动调用Vue.extend
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id
definition = this.options._base.extend(definition)
}
所以在定义组件的时候,以下几种形式都是可以的:
// 函数
Vue.component('CustomComponent', function () {
return new Promise(resolve => {
resolve({
data () {
return {
name: 'CustomComponent'
}
},
template: '<div>{{name}}</div>'
})
})
})
// 对象 会自动调用 Vue.extend
Vue.component('CustomComponent', {
data () {
return {
name: 'CustomComponent'
}
},
template: '<div>{{name}}</div>'
})
// 或者 Vue.extend
Vue.component('CustomComponent', Vue.extend({
data () {
return {
name: 'CustomComponent From Vue.extend'
}
},
template: '<div>{{name}}</div>'
}))
- 如果
type是 指令directive, 并且definition是一个函数的时候, 返回{ bind: definition, update: definition }也就是, 在bind和update的时候 都会触发该函数
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition }
}
- 其余的都采用
this.options[type + 's'][id] = definition
extend.js
使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。
注意: data 必须是一个函数
- JS的继承,参考 知识拓展
源码
import { ASSET_TYPES } from 'shared/constants'
import { defineComputed, proxy } from '../instance/state'
import { extend, mergeOptions, validateComponentName } from '../util/index'
export function initExtend (Vue: GlobalAPI) {
/**
* Each instance constructor, including Vue, has a unique
* cid. This enables us to create wrapped "child
* constructors" for prototypal inheritance and cache them.
*/
Vue.cid = 0
let cid = 1
/**
* Class inheritance
*/
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
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
if (Sub.options.props) {
initProps(Sub)
}
if (Sub.options.computed) {
initComputed(Sub)
}
// allow further extension/mixin/plugin usage
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// create asset registers, so extended classes
// can have their private assets too.
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// enable recursive self-lookup
if (name) {
Sub.options.components[name] = Sub
}
// keep a reference to the super options at extension time.
// later at instantiation we can check if Super's options have
// been updated.
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// cache constructor
cachedCtors[SuperId] = Sub
return Sub
}
}
function initProps (Comp) {
const props = Comp.options.props
for (const key in props) {
proxy(Comp.prototype, `_props`, key)
}
}
function initComputed (Comp) {
const computed = Comp.options.computed
for (const key in computed) {
defineComputed(Comp.prototype, key, computed[key])
}
}
逐行解析
- 基本结构
Vue.extend = function (extendOptions) {}
extendOptions如果不传值, 则默认为:{}
extendOptions = extendOptions || {}
- 获取 父类 以及 父类cid
const Super = this
const SuperId = Super.cid
- 缓存策略, 如果 传入的
extendOptions已经被 构造过了, 那么直接返回
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
例如:
const ctor = {
name: 'Child'
}
const Child = Vue.extend(ctor)
// 因为 ctor之前被构造过, 所以 直接返回 之前构造的, 不需要再构造
const Sub = Vue.extend(ctor)
console.log(Child === Sub) // true
- 验证名称的有效性
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
validateComponentName(name)
}
- JS的寄生组合继承
const Sub = function VueComponent (options) {
this._init(options);
};
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
- 增加cid, 合并 父类的
options, 新增 子类的super属性, 指向父类
Sub.cid = cid++;
Sub.options = mergeOptions(
Super.options,
extendOptions
);
Sub['super'] = Super;
- 初始化
props和computed
if (Sub.options.props) {
initProps$1(Sub);
}
if (Sub.options.computed) {
initComputed$1(Sub);
}
- 拷贝父类的
extend,mixin,use,component,filter,directive
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;
}
- 新增额外属性
superOptions,extendOptions,sealedOptions
Sub.superOptions = Super.options;
Sub.extendOptions = extendOptions;
Sub.sealedOptions = extend({}, Sub.options);
- 缓存 构造函数
cachedCtors[SuperId] = Sub;
mixin.js
Vue.mixin() 源码如下, 还是很容易理解的, 就是将 传入的 mixin 对象 和 this.options进行合并
import { mergeOptions } from '../util/index'
export function initMixin (Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}
use.js
Vue.use 主要用于 注册插件
import { toArray } from '../util/index'
export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// additional parameters
const args = toArray(arguments, 1)
args.unshift(this)
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
installedPlugins.push(plugin)
return this
}
}
- 判断 插件是否已经注册过
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
- 获取额外参数,并且转为数组, 并且将 当前的
this追加到数组的 头部, 如果你调用Vue.use 那么this就是Vue
const args = toArray(arguments, 1)
args.unshift(this)
- 如果 传入的
plugin含有install并且install是一个函数, 调用install函数, 并且将plugin作为 上下文
plugin.install.apply(plugin, args)
例如:
Vue.use({
config: {
name: '请叫我张先森'
},
install (vm, ...params) {
console.log(this)
}
})
4. 否则
plugin 是函数, 直接调用 该函数
plugin.apply(null, args)
例如:
Vue.use(function (vm, ...params) {
console.log(vm, params)
})
- 缓存策略
installedPlugins.push(plugin)
index.js
import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'
import { observe } from 'core/observer/index'
import {
warn,
extend,
nextTick,
mergeOptions,
defineReactive
} from '../util/index'
export function initGlobalAPI (Vue: GlobalAPI) {
// config
const configDef = {}
configDef.get = () => config
if (process.env.NODE_ENV !== 'production') {
configDef.set = () => {
warn(
'Do not replace the Vue.config object, set individual fields instead.'
)
}
}
Object.defineProperty(Vue, 'config', configDef)
// exposed util methods.
// NOTE: these are not considered part of the public API - avoid relying on
// them unless you are aware of the risk.
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 = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
// this is used to identify the "base" constructor to extend all plain-object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue
extend(Vue.options.components, builtInComponents)
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
}
- 引入 所需文件
import config from '../config' // 配置文件
// 就上面讲到的 Vue.use, Vue.mixin, Vue.extend
// Vue.component, Vue.directive, Vue.filter
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'
import { observe } from 'core/observer/index'
import {
warn,
extend,
nextTick,
mergeOptions,
defineReactive
} from '../util/index'
- 设置config只能获取, 不能设置
const configDef = {}
configDef.get = () => config
if (process.env.NODE_ENV !== 'production') {
configDef.set = () => {
warn(
'Do not replace the Vue.config object, set individual fields instead.'
)
}
}
Object.defineProperty(Vue, 'config', configDef)
关于 Object.defineProperty可以 阅读我之前写的文章,
Vue源码阅读——Shared
- 暴露一些工具函数
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
- 将
set,delete,nextTick,observable添加到全局, 并且设置Vue.options为空对象, 并且增加components,directives,filters属性, 以及_base = Vue;
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 = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
// this is used to identify the "base" constructor
// to extend all plain-object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue
- 将内置组件
keep-alive增加到Vue.options.components上
extend(Vue.options.components, builtInComponents)
关于keep-alive的源码解析可以阅读 Vue2源码阅读——keep-alive
- 添加
use,mixin,extend,component,filter,directive
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
知识拓展
js继承
假设我们有一个 父类ParentClass, 构造函数如下:
function ParentClass() {
this.names = ['Demo1', 'Demo2', 'Demo3']
}
ParentClass.prototype.say = function () {
console.log(`我的名字叫: ${this.names}`)
}
原型链继承
function ChildClass () {}
ChildClass.prototype = new ParentClass()
ChildClass.prototype.constructor = ChildClass
缺点
- 将父类初始化之后, 引用类型的属性被所有的实例共享
const aChild = new ChildClass()
const bChild = new ChildClass()
aChild.say() // 我的名字叫: Demo1,Demo2,Demo3
bChild.say() // 我的名字叫: Demo1,Demo2,Demo3
aChild.names.push('Demo4')
aChild.say() // 我的名字叫: Demo1,Demo2,Demo3,Demo4
bChild.say() // 我的名字叫: Demo1,Demo2,Demo3,Demo4
- 在创建 子类的时候, 不能向父类传参
- 无法实现多继承
- 要想
ChildClass.prototype上新增 属性或者方法, 需要在ChildClass.prototype = new ParentClass()之后
借用构造函数
function ChildClass () {
ParentClass.call(this)
this.say = function () {
console.log(`我的名字叫: ${this.names}`)
}
}
优点
- 解决了 原型链继承中的,引用对象的共享
- 在创建 子类的时候, 可以向父类传参
- 可以实现多继承(call多个父类函数) 也就是解决了, 原型链继承的缺点
缺点
- 实例并不是 父类的实例,只是子类的实例
- 只能继承父类的实例的属性和方法, 不能继承原型属性和方法
- 不能实现实例中函数的复用,比如实例代码的
say函数, 每一次创建新的实例,都会创建一次
组合式继承
原型链继承 + 经典继承 = 组合式继承
function ChildClass () {
ParentClass.call(this)
}
ChildClass.prototype = new ParentClass()
ChildClass.prototype.constructor = ChildClass
优点
- 融合原型链继承和构造函数的优点
缺点
- 初始化的时候调用了一次父类, 原型上也初始化了一次父类,
寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
function createObj (o) {
var clone = Object.create(o);
clone.sayName = function () {
console.log('hi');
}
return clone;
}
缺点
- 跟借用构造函数模式一样,每次创建对象都会创建一遍方
寄生组合式继承
function ChildClass () {
ParentClass.call(this)
}
ChildClass.prototype = Object.create(ParentClass.prototype)
ChildClass.constructor = ChildClass