The internal representation of component definitions
Internal Representation of Component Definitions
In Vue 3, component definitions are described through objects, known as component option objects. These objects contain various configuration items for the component, such as data
, methods
, props
, etc. When Vue processes a component, it converts these options into an internal representation.
const MyComponent = {
name: 'MyComponent',
props: {
msg: String
},
setup() {
const count = ref(0)
return { count }
},
template: `<div>{{ msg }} - {{ count }}</div>`
}
Standardization of Component Options
Before a component is created, Vue standardizes the component options. This process primarily does two things: unifying different forms of component definitions into a standard format and applying global configurations and mixins.
// Example of functional component standardization
function FunctionalComp(props) {
return h('div', props.msg)
}
// After standardization, it is converted to
{
functional: true,
render: function(props) {
return h('div', props.msg)
}
}
Structure of the Internal Representation
The standardized component options are converted into an internal representation, which is an object containing multiple properties. The main properties include:
uid
: Unique component IDtype
: Component typename
: Component namerender
: Render functionsetup
: Setup functionprops
: Normalized props definitionemits
: Normalized emits definition
interface InternalComponentOptions {
uid: number
type: Component
name?: string
render?: InternalRenderFunction
setup?: (
props: Data,
setupContext: SetupContext
) => RenderFunction | Data
props?: ComponentPropsOptions
emits?: ComponentEmitsOptions
// ...other properties
}
Normalization of Props
Props definitions can take various forms, and Vue unifies them into a standardized format. For example:
// Array form
props: ['size', 'name']
// Object form
props: {
size: Number,
name: {
type: String,
default: 'anonymous'
}
}
// After standardization, both are converted to
props: {
size: {
type: Number
},
name: {
type: String,
default: 'anonymous'
}
}
Processing of the Setup Function
The setup
function is the core of Vue 3's Composition API. In the internal representation, the setup
function is specially processed:
- Receives
props
andcontext
parameters - Can return a render function or reactive data
- Executes before the component instance is created
const Comp = {
setup(props, { attrs, slots, emit }) {
const count = ref(0)
const increment = () => count.value++
return () => h('div', [
h('span', count.value),
h('button', { onClick: increment }, '+')
])
}
}
Internal Representation of the Render Function
The component's render function is ultimately converted into a VNode. Vue 3 uses an optimized representation:
// Render function after template compilation
function render(_ctx, _cache) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("span", null, _ctx.count),
_createVNode("button", { onClick: _ctx.increment }, "+")
]))
}
Relationship Between Component Instances and Internal Representation
Component instances are runtime objects created based on the internal representation. Each component instance holds a reference to the internal representation:
interface ComponentInternalInstance {
uid: number
type: Component
vnode: VNode
// Points to the internal representation
appContext: AppContext
// Other instance properties...
}
Special Handling of Functional Components
Functional components have a special representation internally:
// Functional component definition
const FunctionalComp = (props, { slots, attrs }) => h('div', props.msg)
// Internal representation
{
functional: true,
props: ['msg'],
render: (ctx) => h('div', ctx.msg)
}
Internal Representation of Async Components
Async components are converted into a special object representation:
const AsyncComp = defineAsyncComponent(() => import('./Comp.vue'))
// Internal representation
{
__asyncLoader: () => import('./Comp.vue'),
__asyncResolved: null, // Resolved component
suspensible: true
}
Internal Representation of Built-in Components
Vue 3's built-in components like <KeepAlive>
, <Transition>
, etc., also have special internal representations:
// Internal representation of KeepAlive
const KeepAliveImpl = {
__isKeepAlive: true,
props: {
include: [String, RegExp, Array],
exclude: [String, RegExp, Array],
max: [String, Number]
},
setup(props, { slots }) {
// Special implementation logic
}
}
Component Type Flags
Vue 3 uses bitmasks to flag component types for efficient type checking:
enum ShapeFlags {
ELEMENT = 1,
FUNCTIONAL_COMPONENT = 1 << 1,
STATEFUL_COMPONENT = 1 << 2,
COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
// ...other flags
}
// Usage example
const vnode = {
shapeFlag: ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.COMPONENT
}
Caching of Component Definitions
Vue 3 optimizes by caching component definitions:
// Cache for resolved async components
const resolvedCache = new WeakMap()
function resolveAsyncComponent(comp) {
if (resolvedCache.has(comp)) {
return resolvedCache.get(comp)
}
// Resolution logic...
resolvedCache.set(comp, resolvedComp)
return resolvedComp
}
Implementation of Component Inheritance
Component inheritance is implemented via prototype chains:
const BaseComponent = {
data() {
return { baseData: 'base' }
}
}
const ChildComponent = {
extends: BaseComponent,
data() {
return { childData: 'child' }
}
}
// Internal implementation is similar to
const mergedOptions = Object.create(BaseComponent)
Object.assign(mergedOptions, ChildComponent)
Internal Representation of Directives
Directives used in components also have corresponding internal representations:
const vMyDirective = {
mounted(el, binding, vnode) {
// Directive logic
},
updated(el, binding, vnode) {
// Update logic
}
}
// In the component's internal representation
{
directives: {
myDirective: vMyDirective
}
}
Internal Processing of Slots
Slot content is internally converted into functions:
// Parent component
const Parent = {
template: `
<Child>
<template #default="{ msg }">
{{ msg }}
</template>
</Child>
`
}
// Slots in the internal representation
slots: {
default: (props) => [createTextVNode(props.msg)]
}
Internal Registration of Component Lifecycles
Lifecycle hooks are internally converted into an array format:
const Comp = {
created() {
console.log('created 1')
},
mixins: [{
created() {
console.log('created 2')
}
}]
}
// Internal representation
{
created: [
function() { console.log('created 2') },
function() { console.log('created 1') }
]
}
Integration with the Reactivity System
The component's internal representation is tightly integrated with the reactivity system:
const Comp = {
setup() {
const state = reactive({ count: 0 })
return { state }
}
}
// Internal processing
const setupResult = Comp.setup()
if (isReactive(setupResult)) {
instance.setupState = setupResult
}
Template Compilation and Internal Representation
For components defined via templates, they are first compiled into render functions:
const template = `<div @click="count++">{{ count }}</div>`
// After compilation
const render = function(_ctx, _cache) {
return _openBlock(), _createBlock("div", {
onClick: _cache[0] || (_cache[0] = $event => (_ctx.count++))
}, _toDisplayString(_ctx.count), 1)
}
// Final internal representation
{
render,
// Other options...
}
Implementation of Component Proxies
Component instances access data via proxies:
const instance = {
props: { msg: 'hello' },
setupState: { count: 0 },
data: { foo: 'bar' }
}
const proxy = new Proxy(instance, {
get(target, key) {
if (key in target.setupState) {
return target.setupState[key]
} else if (key in target.props) {
return target.props[key]
}
// Other property accesses...
}
})
Optimization Flags for Component Updates
The internal representation includes optimization-related flags:
interface InternalComponentOptions {
// ...
patchFlag?: number
dynamicProps?: string[]
dynamicChildren?: VNode[]
}
These flags are used for optimizations during updates, for example:
// Static node
const staticVNode = {
patchFlag: PatchFlags.HOISTED
}
// Dynamic props node
const dynamicPropsVNode = {
patchFlag: PatchFlags.PROPS,
dynamicProps: ['title', 'value']
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:与选项式API的互操作性
下一篇:组件实例的创建过程