组件注册
在 Vue.js 中,除了它内置的组件如 keep-alive
、component
、transition
、transition-group
等,其它用户自定义组件在使用前必须注册。
全局注册
要注册一个全局组件,可以使用 Vue.component(tagName, options)
。例如:
Vue.component('my-component', {
// 选项
})
Vue.component
函数的定义过程发生在最开始初始化 Vue 的全局函数的时候,代码在 src/core/global-api/assets.js
中:
import { ASSET_TYPES } from 'shared/constants'
import { isPlainObject, validateComponentName } from '../util/index'
export const ASSET_TYPES = [
'component',
'directive',
'filter'
]
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
}
}
})
}
局部注册
局部注册会把组件扩展到 Sub.options
下:
Sub.options = mergeOptions(Super.options, extendOptions);
总结
- 全局注册的组件可以在任意地方使用,因为组件会通过
Vue.component
函数扩展到Vue.options
上,而各个组件初始化时都会将Vue.options
与自身options
合并,这样每个组件都能访问到这个全局注册的组件。 - 局部注册的组件只能在当前组件使用,因为组件仅仅只是扩展到
Sub.options
也就是当前组件构造函数的options
上。
异步组件
在官网中异步组件有三种写法:
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 向 `resolve` 回调传递组件定义
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
Vue.component(
'async-webpack-example',
// 这个 `import` 函数会返回一个 `Promise` 对象。
() => import('./my-async-component')
)
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
分析一下createComponent
函数中的resolveAsyncComponent
:
export function createComponent (
Ctor: Class<Component> | Function | Object | void,
data: ?VNodeData,
context: Component,
children: ?Array<VNode>,
tag?: string
): VNode | Array<VNode> | void {
if (isUndef(Ctor)) {
return
}
const baseCtor = context.$options._base
// 由于组件的定义并不是一个普通对象,所以不会执行 Vue.extend 的逻辑把它变成一个组件的构造函数
if (isObject(Ctor)) {
Ctor = baseCtor.extend(Ctor)
}
// ...
// 异步组件
let asyncFactory
if (isUndef(Ctor.cid)) {
asyncFactory = Ctor
// 处理了 3 种异步组件的创建方式
Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context)
if (Ctor === undefined) {
// 实际上就是就是创建了一个占位的注释 VNode,同时把 asyncFactory 和 asyncMeta 赋值给当前 vnode。
return createAsyncPlaceholder(
asyncFactory,
data,
context,
children,
tag
)
}
}
}