Re-learn Vue source code, according to Huang Yi big man Vue technology revealed, one by one, consolidate the Vue source knowledge points, after all, chewed up is their own, all the articles are synchronized in the public number (road in the front stack) and github.
The body of the
During development, custom components must be registered before they can be used. If used directly, an error will be reported: unknown custom elements, like the following:
'Unknown custom element: <xxx> - did you register the component correctly?
For recursive components, make sure to provide the "name" option.'
Copy the code
There are two ways to register components in VUE: global and local. Let’s examine them below.
Global registration
To register a component globally, this is typically done in main.js:
Vue.component("comp-name", {
// options
})
Copy the code
USES a ponent function to register the Vue.com, the definition of the function process is in the most began to initialize Vue global function code in the SRC/core/global – API/assets. In js:
export const ASSET_TYPES = [
'component'.'directive'.'filter'
]
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
}
}
})
Copy the code
You can see that by iterating through ASSET_TYPES, several methods are extended to Vue. Each method takes two arguments, an ID, and a custom function or object. If you don’t have definitioin, you don’t move forward, otherwise you continue. If type is a component and its definition is a normal object, name is assigned, and this.options._base.extend() is used to convert the second argument to a constructor. This.options._base is actually the big Vue(as previously analyzed by vue.options._base = Vue), and then uses vue.extend () to convert the argument to the constructor, Finally, assign this constructor to this.options[type + ‘s’][id], which defines a components constructor for the large Vue extension, and eventually mount it on Vue.options.components.
Since a _createElement method is called when the Vue is initialized (which is analyzed when the render function renders and creates elements), there is a code in this method:
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 (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)
}
Copy the code
The registered component goes to the vNode = createComponent(Ctor, data, Context, Children, tag) logic, creates a component vNode, and calls a resolveAssets method, $options, components, and tag are passed in. This method is defined in SRC /core/util/options.js:
export function resolveAsset (
options: Object, type: string, id: string, warnMissing? : boolean) :any {
/* istanbul ignore if */
if (typeofid ! = ='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
}
Copy the code
Note that the first parameter, options, was mentioned in the merge configuration analysis (see the merge configuration process in this article) : Vm. $options is a custom configuration that is merged with the larger Vue. Options parameter, so at the end of asset.js, extend the larger Vue components. That’s the if below.
If assets have an ID, return it. If assets have an ID, return it. If assets have an ID, return id to a hump. The _createElement method is used to create a vNode with an id, a hump, and a capital letter. If the vnode is not found, the _createElement method is empty. Create a VNode with new:
// 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
)
Copy the code
This is why you can write the ID as a hump or use a capital letter when customizing global components.
To remember, a global custom component extends a constructor in the large Vue.options.components, and then initializes the creation element (_createElement) with the tag passed in by the resolveAsset. Parse out a definition of the component label, return the constructor, pass it into createComponent to create a vNode for the component, and then patch and update it to a real DOM.
Local registration
Local registrations are typically written in a vue file like this:
<template>
<Comp />
</template>
<script>
import Comp from "Comp.vue";
export default {
components:{
Comp
}
}
</script>
Copy the code
This introduces a local component, and now let’s examine its process.
Review how vue.extend merges options:
const Sub = function VueComponent (options) {
this._init(options)
}
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
Sub.options = mergeOptions(
Super.options,
extendOptions
)
Copy the code
$options = Vue.$options = extendOptions = Vue.$options = Vue.
export default {
components:{
Comp
}
}
Copy the code
Merge these two components into the options of the child component constructor, sub. options. Then, when the Sub is initialized, an initInternalComponent method is called. Code in/SRC/core/instance/init. Js) :
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
Copy the code
Now look at the initInternalComponent method:
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
}
}
Copy the code
Sub.options gets the component configuration we wrote on the page and assigns it to vm.$options. As a result, the component configuration defined in the page can be retrieved from VM.quarrels. Com Ponents, and assets mentioned in the global registry can be retrieved from the local registry.
Note: Since the merge configuration of local components is extended to sub. options, the introduced local component can only be used under the current component (or the current VUE page), while the global registry is extended under the large vue.options. $options = mergeOptions() where the vm goes to _init:
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
Copy the code
So it can be used globally.
My public number: the front end stack in the road, a front end article every day, the feeling of chewing is wonderful ~