// 全局组件注册源码核心
Vue.options.components = Object.create(null)
Vue.component = function(id, definition) {
// 标准化组件选项
if (isObject(definition)) {
definition = Vue.extend(definition)
}
// 存储到全局组件库
this.options.components[id] = definition
return definition
}
// 组件构造器生成
Vue.cid = 0
Vue.extend = function(extendOptions) {
const Super = this
const Sub = function VueComponent(options) {
this._init(options)
}
Sub.cid = Vue.cid++
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.options = mergeOptions(Super.options, extendOptions)
return Sub
}
注册过程解析:
// 组件合并策略
function mergeAssets(parentVal, childVal) {
const res = Object.create(parentVal || null)
return childVal
? extend(res, childVal)
: res
}
// 组件选项合并
const strats = {
components: function(parentVal, childVal) {
return mergeAssets(parentVal, childVal)
}
}
合并规则:
// 组件VNode创建
function createComponent(Ctor, data, context, children) {
// 安装组件钩子
installComponentHooks(data)
return new VNode(
`vue-component-${Ctor.cid}`,
data, undefined, undefined, undefined, context,
{ Ctor, children }
)
}
// 组件钩子安装
const componentVNodeHooks = {
init(vnode) {
const child = vnode.componentInstance = new vnode.componentOptions.Ctor()
child.$mount(undefined) // 不指定el
},
prepatch(oldVnode, vnode) {
// 更新组件props等
},
insert(vnode) {
if (!vnode.componentInstance._isMounted) {
vnode.componentInstance._isMounted = true
callHook(vnode.componentInstance, 'mounted')
}
}
}
// Props标准化处理
function normalizeProps(options) {
const props = options.props
if (!props) return
const res = {}
if (Array.isArray(props)) {
props.forEach(key => {
res[key] = { type: null }
})
} else {
for (const key in props) {
res[key] = isObject(props[key])
? props[key]
: { type: props[key] }
}
}
options.props = res
}
// Props响应化处理
function initProps(vm, propsOptions) {
const propsData = vm.$options.propsData || {}
const props = vm._props = {}
for (const key in propsOptions) {
defineReactive(props, key, propsData[key])
if (!(key in vm)) {
proxy(vm, '_props', key)
}
}
}
// 事件中心实现
function eventsMixin(Vue) {
Vue.prototype.$on = function(event, fn) {
const vm = this
;(vm._events[event] || (vm._events[event] = [])).push(fn)
return vm
}
Vue.prototype.$emit = function(event) {
const vm = this
let cbs = vm._events[event]
if (cbs) {
const args = Array.prototype.slice.call(arguments, 1)
for (let i = 0; i < cbs.length; i++) {
cbs[i].apply(vm, args)
}
}
return vm
}
}
// 组件v-on处理
function updateComponentListeners(vm, listeners) {
for (const name in listeners) {
const handler = listeners[name]
if (!handler) continue
vm.$on(name, handler)
}
}
Vue.component('smart-list', {
functional: true,
props: ['items'],
render(h, context) {
return h('ul', context.data,
context.props.items.map(item =>
h('li', item.name)
)
)
}
})
性能优势:
Vue.component('async-component', (resolve, reject) => {
setTimeout(() => {
resolve({
template: 'Loaded!'
})
}, 1000)
})
// 结合Webpack代码分割
Vue.component('async-webpack', () => import('./AsyncComponent.vue'))
加载阶段生命周期:
const AsyncComponent = () => ({
component: import('./MyComponent.vue'),
loading: LoadingComponent,
error: ErrorComponent,
delay: 200,
timeout: 3000
})
// keep-alive实现原理
export default {
name: 'keep-alive',
abstract: true,
created() {
this.cache = Object.create(null)
},
render() {
const slot = this.$slots.default
const vnode = slot[0]
const key = vnode.key
if (this.cache[key]) {
vnode.componentInstance = this.cache[key].componentInstance
} else {
this.cache[key] = vnode
}
vnode.data.keepAlive = true
return vnode
}
}
// 结合路由的懒加载方案
const router = new VueRouter({
routes: [
{
path: '/heavy',
component: () => import(
/* webpackChunkName: "heavy" */
'./HeavyComponent.vue'
)
}
]
})
深度实践建议:
// 组件注册调试示例
Vue.component('global-comp', { template: 'Global' })
new Vue({
components: { 'local-comp': { template: 'Local' } },
mounted() {
console.log(this.$options.components)
}
})