话不多说先上源码:
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)
}
第一部分:
// 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)
这里劫持了Vue
的config
属性,使的无法对其进行修改。
第二部分:
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
- 首先看
warn
,它实际来自于/src/core/util/debug.js
warn = (msg, vm) => {
const trace = vm ? generateComponentTrace(vm) : ''
if (config.warnHandler) {
config.warnHandler.call(null, msg, vm, trace)
} else if (hasConsole && (!config.silent)) {
console.error(`[Vue warn]: ${msg}${trace}`)
}
}
tip = (msg, vm) => {
if (hasConsole && (!config.silent)) {
console.warn(`[Vue tip]: ${msg}` + (
vm ? generateComponentTrace(vm) : ''
))
}
}
如果用过Vue
的warnHandler
功能应该都知道,这是一个自定义警告处理函数,哈哈其实我没用过,但是动手去官网查了一下。
其实也就是我们可以通过自定义warnHandler
函数做一些项目警告的收集,同样的功能还有errorHandler
,如果有需要可以去官方文档看看。generateComponentTrace
方法会追踪到项目警告组件的踪迹,也就是一个定位功能。
如果没有定义warnHandler
,在写组件不规范的情况下,就会在控制台打印错误,这应该非常常见了~
-
extend
, 顺着找下去来自于/share/utils
/**
* Mix properties into target object.
*/
export function extend (to: Object, _from: ?Object): Object {
for (const key in _from) {
to[key] = _from[key]
}
return to
}
作用就是将源对象的属性混入到目标对象。
-
mergeOptions
在/core/util/options.js
下
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)
normalizeInject(child, vm)
normalizeDirectives(child)
// Apply extends and mixins on the child options,
// but only if it is a raw options object that isn't
// the result of another mergeOptions call.
// Only merged options has the _base property.
if (!child._base) {
if (child.extends) {
parent = mergeOptions(parent, child.extends, vm)
}
if (child.mixins) {
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
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
作用是将父子的策略项合并为一个checkComponents
就是检查组件,内部遍历了传入的child
的components
属性检查组件名字是否规范。normalizeProps
其作用就是统一将props: [?]
和props: {?}
形式转换为后者的形式。normalizeInject
也是统一标准化传入的inject
, inject
一般是父组件或者祖父组件 provide
的值,也是一种通信方式。normalizeDirectives
标准化自定义指令,如果传入的是个function
, 会被定义成{ bind: function, update: function}
这种形式。
下面还有如果子组件有 extends
或者mixins
将会改变父组件等功能,等遇到具体调用的时候再分析。mergeField
这个是实际做的事情,它会按照strats
定义的的策略进行合并。
strats.el = strats.propsData = function (parent, child, vm, key) {
strats.data = function () {...}
strats.props = ...
strats.methods = ...
strats.computed = ...
strats.watch = ...
...
-
defineReactive
在/core/observer/index.js
下,是Vue
实现响应式的关键,后续会详细再说,这里相当于给util
赋予了这个功能。 -
set
和del
作用就是响应式的添加或删除属性 -
nextTick
可以在下次DOM
更新循环结束之后执行延迟回调。与JS
的事件运行机制非常像,会单独记录一篇文章。 -
Vue.observable
这个是Vue 2.6
版本新增的API,很明显的看到了它使用了Vue
的Observe
,它的作用就是,假如一个小型的项目根本用不上Vuex
进行状态管理,那么就可以使用它自定义一个小型的响应式store
供全局使用,详情可以参照官方文档。 Vue.options
// 遍历了ASSET_TYPES初始化Vue.options
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
// 下面是ASSET_TYPES的定义
export const ASSET_TYPES = [
'component',
'directive',
'filter'
]
-
extend(Vue.options.components, builtInComponents)
将builtInCompoents
属性混入Vue.options.components
,里面是一些keep-alive
相关的东西。 -
initUse(Vue)
如下:
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
}
}
给Vue
上添加了use
,接收一个插件参数,安装插件。平时我们注册类似router
等插件的时候,就会用到Vue.use(router)
。其内部维护了一个_installedPlugins
数组用来存储所有安装的插件,安装时会判断插件是否实现了install
方法,如果有就会执行插件的install
方法,在这里Vue
巧妙的将自己注入到install
方法的参数中,这样的好处就是,在实现install
方法就不需要import Vue
了。
initMixin
export function initMixin (Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}
全局混入,将mixin
通过合并策略混入全局的options
,一旦使用全局混入,由于局部注册组件,其实是调用了Vue.extend
方法,其调用时也会进行mergeOptions
,所以会影响每一个之后创建的 Vue
实例。应当恰当使用。
initExtend
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
}
}
这里实现了一个js
的经典继承,此方法用于创建Vue
的子类构造函数。在这里就可以验证了上面说的,调用时会将Super.options
和extendOptions
合并。
initAssetRegisters
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
}
}
})
}
ASSET_TYPES
在上面已经提到过了,这里是初始化Vue
的注册器,比如我们要注册组建的时候会调用Vue.component
,要自定义指令时会使用Vue.directive
,就是在这定义的。
总结
这里主要讲的就是Vue
官网全局API
的内容,不算特别详细,但也一一介绍到了,如果要追求更深入的了解,可以去git
上下载一份源码钻研钻研,其实在继承那里,类似initProps
、initComputed
也是能提升项目中对Vue
用法的理解。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。