阿里云主机折上折
  • 微信号
Current Site:Index > The internal representation of component definitions

The internal representation of component definitions

Author:Chuan Chen 阅读数:33649人阅读 分类: Vue.js

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 ID
  • type: Component type
  • name: Component name
  • render: Render function
  • setup: Setup function
  • props: Normalized props definition
  • emits: 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:

  1. Receives props and context parameters
  2. Can return a render function or reactive data
  3. 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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.