vue源码分析(四)解密new Vue()之前做了哪些不为人知工
上一篇我们说过了vue实例化之前会调用 initGlobalAPI(Vue) 方法,那我们先看看一下定义initGlobalAPI 方法的路径 “src/core/global-api/index.js”。
打开文件可以看到上面导入了很多的对象,但基本都是定义函数的,没有涉及到执行,我们可以先不看,最主要的我们来看一下这个文件。
"import builtInComponents from '../components/index'"
代码如下:
import KeepAlive from './keep-alive'
export default {
KeepAlive
}
然后我们再打开 “src/core/components/keep-alive.js” 代码如下:
/* @flow */
import { isRegExp, remove } from 'shared/util'
import { getFirstComponentChild } from 'core/vdom/helpers/index'
type VNodeCache = { [key: string]: ?VNode };
......为了不增大篇幅,省略部分代码(大家可自行查看)
export default {
name: 'keep-alive',
abstract: true,
props: {
include: patternTypes,
exclude: patternTypes,
max: [String, Number]
},
created () {
this.cache = Object.create(null)
this.keys = []
},
destroyed () {
for (const key in this.cache) {
pruneCacheEntry(this.cache, key, this.keys)
}
},
mounted () {
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
},
render () {
const slot = this.$slots.default
const vnode: VNode = getFirstComponentChild(slot)
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// check pattern
const name: ?string = getComponentName(componentOptions)
const { include, exclude } = this
if (
// not included
(include && (!name || !matches(include, name))) ||
// excluded
(exclude && name && matches(exclude, name))
) {
return vnode
}
const { cache, keys } = this
const key: ?string = vnode.key == null
// same constructor may get registered as different local components
// so cid alone is not enough (#3269)
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance
// make current key freshest
remove(keys, key)
keys.push(key)
} else {
cache[key] = vnode
keys.push(key)
// prune oldest entry
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
}
vnode.data.keepAlive = true
}
return vnode || (slot && slot[0])
}
}
通过以上代码我们看到导出了一个对象 “keep-alive”,对于这个对象大家应该比较熟悉了,这是vue的一个内置组件,主要用来缓存路由的。这个我们后期再分享具体的代码,我们还是接着看 "initGlobalAPI(vue)"的具体代码:
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”属性进行了一个拦截。
“Object.defineProperty(Vue, 'config', configDef)”
然后给vue注册了 “set”、“delete”、“nextTick”三个属性,和我们在第三讲说的在vue原型对象上面挂载了“ delete”是一样的,都是指向同一个函数,正如官方说的 “这是全局 Vue.set 的别名”。
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
紧接着就是给vue添加了一个观察者,然后给 Vue.options对象添加了三个属性 directives、filters、components。
注释:ASSET_TYPES = ['component','directive','filter']
然后就是让“components”属性继承了 builtInComponents。
接下来可以清楚的看到调用了如下4个函数:
initUse(Vue)
// 在vue实例上挂载use方法(此方法用来加载插件)
initMixin(Vue)
// 在vue实例上挂载mixin方法
initExtend(Vue)
// 在vue实例上挂载extend方法
initAssetRegisters(Vue)
// 在vue实例上挂载directives、filters、components
到这一步vue的准备工作基本 做完了,接下来我们看看new Vue()之后做了哪些不为人知的工作。