Vue 元件全域性註冊和區域性註冊使用及原理
Vue在註冊元件時有兩種方式,全域性註冊 和區域性註冊
全域性註冊的話我們可以在任意元件中使用註冊的元件,而區域性註冊的話我們只能在當前的元件中使用註冊到的元件,
全域性註冊例項
Vue.component("app",App);
區域性註冊例項
components:{ App }
下面我們進入兩種註冊方式的原始碼實現,首先進入Vue.component
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 } }
這裡是對三種方法的定義'component', 'directive', 'filter',這裡我們要說的是component,可以看到,當type = 'component
時,直接呼叫了extend方法,extends 就是對Vue物件的擴充套件,就是一個元件的Vue建構函式,擁有Vue的所有方法,最後,將這個擴充套件賦值給了全域性Vue 的options 的components 屬性中,然後,我們進入到patch時 解析標籤時的
let vnode, ns if (typeof tag === 'string') { let Ctor ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag) if (config.isReservedTag(tag)) { // platform built-in elements vnode = new VNode( config.parsePlatformTagName(tag), data, children, undefined, undefined, context ) } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) { // component vnode = createComponent(Ctor, data, context, children, tag) } else { // unknown or unlisted namespaced elements // check at runtime because it may get assigned a namespace when its // parent normalizes children vnode = new VNode( tag, data, children, undefined, undefined, context ) } } else { // direct component options / constructor vnode = createComponent(tag, data, context, children) }
當標籤型別為普通標籤時,會建立普通型別的標籤,如果不是普通標籤時我們可以看else if裡,Ctor = resolveAsset(context.$options, 'components', tag)
我們找到這個函式,定義在原始碼src/core/util/options.js
export function resolveAsset ( options: Object, type: string, id: string, warnMissing?: boolean ): any { /* istanbul ignore if */ if (typeof id !== 'string') { return } const assets = options[type] // check local registration variations first if (hasOwn(assets, id)) return assets[id] const camelizedId = camelize(id) if (hasOwn(assets, camelizedId)) return assets[camelizedId] const PascalCaseId = capitalize(camelizedId) if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId] // fallback to prototype chain const res = assets[id] || assets[camelizedId] || assets[PascalCaseId] if (process.env.NODE_ENV !== 'production' && warnMissing && !res) { warn( 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, options ) } return res }
這裡的options是 Vue和當前元件options的組合,這裡是重點,,這個options是由之前呼叫的extends 方法完成合並的,,這也是全域性元件和區域性元件的區別所在,可以看到程式碼中是通過這個options 物件來找 註冊的元件,三個if是對駝峰式和大寫式查詢的支援,因為之前的component方法定義中已經將這個全域性註冊的元件的名字放到了Vue的options中,所以在這裡可以通過組合的options[組建名稱] 查詢到對應的元件建構函式,因為全域性註冊時存放於元件樹最高層的Vue的options中,所以我有低層次的元件在合併options時都會合併到Vue的options,所以在任何地方都可以使用全域性註冊的元件,而區域性註冊的元件只會在區域性元件的配置合併中合併到區域性options中,當在其他元件的options中尋找註冊的元件時就會找不到,所以區域性註冊的元件只存在於區域性options中,下面我們看下區域性options的合併
export function initInternalComponent (vm: Component, options: InternalComponentOptions) { const opts = vm.$options = Object.create(vm.constructor.options) // doing this because it's faster than dynamic enumeration. const parentVnode = options._parentVnode opts.parent = options.parent opts._parentVnode = parentVnode const vnodeComponentOptions = parentVnode.componentOptions opts.propsData = vnodeComponentOptions.propsData opts._parentListeners = vnodeComponentOptions.listeners opts._renderChildren = vnodeComponentOptions.children opts._componentTag = vnodeComponentOptions.tag if (options.render) { opts.render = options.render opts.staticRenderFns = options.staticRenderFns } }
這裡是區域性元件options 合併的程式碼,區域性註冊的components屬性便會合併到options中,所以在當前templete中的component標籤便可以在此元件的options中找到對應的建構函式從而構造出對應的元件vnode。
所以options就是元件的建構函式的屬性包括data啊,computed啊,props啊,component那些,然後因為元件樹的底層options都會包含上層的options,所以,在Vue中註冊的component可以在任何地方訪問到,因為他是最頂層的元件,當然只有這個component 屬性可以訪問到,因為這裡的訪問是在patch中授予的許可權,其他在區域性註冊的元件,只有在他以下的元件才可以訪問的到