本系列
-vue2中值得关注的全局Api之config对象
-vue2中值得关注的全局Api之util对象
Vue2中这些全局Api很重要,谈到源码大家都关注响应原理
、模板编译
、更新策略
这些点,但是Vue在第一步做的事情是注册全局Api,没有全局Api就没有后面的一切
全局Api都在initGlobalAPI
函数中初始化
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)
}
ASSET_TYPES
中有'component', 'directive', 'filter' 这三个变量,通过循环添加上options中静态变量'components', 'directives', 'filters'
keep-alive
extend(Vue.options.components, builtInComponents)
components属性通过extend方法赋值,Vue.util.extend方法就是简单的浅拷贝,用后者覆盖前者相同key的值对象
这里builtInComponents只有keep-alive内部组件
基本用法 include、exclude、max 参数作用
看源码
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 {
// delay setting the cache until update
this.vnodeToCache = vnode
this.keyToCache = key
}
vnode.data.keepAlive = true
}
return vnode || (slot && slot[0])
}
-
getFirstComponentChild
获取默认default插槽的第一个子组件,所以如果有多个子组件只取第一个子组件,其他都废弃
-
组件名name是怎么得来的,根据
getComponentName
先判断name属性,没有就取标签名 -
如果有include 或者 exclude 参数,那么根据name判断,如果在include就缓存,或者在exclue就不缓存
-
如果没有include 和 exclude 那就直接要检查缓存
-
如果cache[key]存在,则从cacel中获取
componentInstance
对象给vnode,并刷新key在数组中的顺序 -
key是什么呢,如果vnode有key就直接用,如果没有就是拼接cid和标签名的字符串,cid很重要,如果是同一组件多次出现,这cid是不同的
-
如果是第一次进来,则会在mounted和updated两个钩子中,把vnode放入数组,在render函数中只是设置vnodeToCache,真正在mounted和updated中才去设置
cache[keyToCache]
// delay setting the cache until update
this.vnodeToCache = vnode
为什么不这里直接压入缓存呢,因为componentInstance对象就是子组件实例this,还没有生成此时是null
- max的作用是设置最大缓存对象个数
这就是实际缓存的函数,keys.push最新的key,如果是updated执行,也会把key从旧位置放到队列最后,下面pruneCacheEntry检查到max超了,就去掉第一个key,也就是LRU
策略,最新的在最后面,超出后第一个就去掉
cacheVNode() {
const { cache, keys, vnodeToCache, keyToCache } = this
if (vnodeToCache) {
const { tag, componentInstance, componentOptions } = vnodeToCache
cache[keyToCache] = {
name: getComponentName(componentOptions),
tag,
componentInstance,
}
keys.push(keyToCache)
// prune oldest entry
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
this.vnodeToCache = null
}
}