阿里云主机折上折
  • 微信号
Current Site:Index > The implementation mechanism of dynamic components

The implementation mechanism of dynamic components

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

Implementation Mechanism of Dynamic Components

Vue3's dynamic components allow switching between different components at runtime, with the core mechanism based on the collaboration between the built-in <component> component and the is attribute. Its underlying implementation involves virtual DOM creation, component resolution, and dynamic rendering optimization.

Core Principle Analysis

The core implementation of dynamic components is located in runtime-core/src/components/KeepAlive.ts and runtime-core/src/helpers/resolveAssets.ts. When the template parser encounters a <component> tag, Vue executes the following steps:

  1. Parse the is attribute value
  2. Determine the component definition based on the value type
  3. Create the corresponding component VNode
  4. Handle context inheritance relationships
// Simplified example of virtual DOM creation process
function createComponentVNode(
  type: Component,
  props: Data | null,
  children: unknown,
  patchFlag: number,
  dynamicProps: string[] | null
) {
  // Handle dynamic component types
  if (isDynamicComponent(type)) {
    type = resolveDynamicComponent(type, currentRenderingInstance)
  }
  // Create component VNode
  return baseCreateVNode(
    type,
    props,
    children,
    patchFlag,
    dynamicProps,
    true /* isComponent */
  )
}

Processing Logic of the is Attribute

The is attribute supports multiple forms of component definitions:

  1. String form: Directly look up registered components
<component :is="currentComponent"></component>
  1. Component options object: Use the component definition directly
const dynamicComp = {
  template: '<div>Dynamic Component</div>'
}
  1. Async component: Supports Promise-based definitions
const asyncComp = defineAsyncComponent(() => 
  import('./AsyncComponent.vue')
)

The processing logic primarily follows the code path in the resolveDynamicComponent function:

export function resolveDynamicComponent(
  component: unknown,
  instance: ComponentInternalInstance | null
): VNodeTypes {
  if (isString(component)) {
    // String form processing
    return resolveAsset(COMPONENTS, component, false, instance) || component
  } else if (isObject(component) || isFunction(component)) {
    // Component object/constructor processing
    return component as VNodeTypes
  }
  // Other invalid cases
  return null
}

Integration Mechanism with keep-alive

Dynamic components are often used with <keep-alive> to implement component state caching:

<keep-alive>
  <component :is="currentView"></component>
</keep-alive>

The caching mechanism is implemented using shapeFlags:

// Set dynamic component flag when creating VNode
vnode.shapeFlag |= 
  isDynamicComponent(type)
    ? ShapeFlags.COMPONENT
    : ShapeFlags.COMPONENT | ShapeFlags.DYNAMIC_COMPONENT

The caching strategy is managed via key:

// Key generation logic inside keep-alive
const key = vnode.key == null
  ? comp.__asyncResolved || comp.__file
  : vnode.key

Rendering Update Process

Conditions triggering dynamic component updates:

  1. The value bound to is changes
  2. Internal component state changes
  3. Parent component forces an update

Core logic of the update process:

function updateComponent(
  n1: VNode | null,
  n2: VNode,
  optimized: boolean
) {
  // Handle dynamic component switching
  if (n1.component && isSameVNodeType(n1, n2)) {
    // Same component update
    updateComponentPreRender(n2, optimized)
  } else {
    // Different component unmount/mount
    unmount(n1)
    mountComponent(n2, parentComponent)
  }
}

Performance Optimization Strategies

Vue3 implements several optimizations for dynamic components:

  1. Static hoisting: Static content in templates is hoisted
// Compiled render function
function render() {
  return (_openBlock(),
    _createBlock(_resolveDynamicComponent(currentComponent.value), {
      key: currentComponent.value
    })
  )
}
  1. Patch flags: Mark dynamic parts with patchFlag
// Dynamic component VNode creation
const vnode = {
  type: currentComponent,
  patchFlag: PatchFlags.DYNAMIC_COMPONENT
}
  1. Event caching: Avoid repeated event binding
// Event handler caching
const handlers = vnode.props.on
if (handlers) {
  vnode.props.on = cacheHandlers(handlers)
}

Edge Case Handling

Special scenarios requiring attention for dynamic components:

  1. Transition animations: Additional handling for transition components
<transition name="fade" mode="out-in">
  <component :is="view"></component>
</transition>
  1. Attribute inheritance: Handling non-prop attribute passing
// Attribute inheritance logic
if (vnode.props && vnode.props.on) {
  extend(instance.attrs, vnode.props)
}
  1. Async component loading states:
const AsyncComp = defineAsyncComponent({
  loader: () => import('./MyComp.vue'),
  loadingComponent: LoadingSpinner,
  delay: 200,
  timeout: 3000
})

Interaction with Composition API

Usage patterns of dynamic components in setup:

const current = ref('CompA')

// Dynamically get component reference
const dynamicComp = computed(() => 
  defineAsyncComponent(() => import(`./${current.value}.vue`))
)

// Manual component control
function toggleComponent() {
  current.value = current.value === 'CompA' ? 'CompB' : 'CompA'
}

Compile-Time Processing Details

Special handling of dynamic components by the template compiler:

// Example of compiled render function
function _sfc_render(_ctx) {
  return (_openBlock(),
    _createBlock(
      _resolveDynamicComponent(_ctx.componentName),
      { 
        msg: _ctx.message 
      },
      null,
      8 /* PROPS */,
      ["msg"]
    )
  )
}

Compiler-generated flags:

  • DYNAMIC_SLOTS: Handles dynamic slots
  • COMPONENT_SHOULD_KEEP_ALIVE: Keep-alive optimization hint

Server-Side Rendering Adaptation

Special handling of dynamic components in SSR environments:

  1. Async components need synchronous resolution
// Server-side async component loading
async function resolveAsyncComponent(comp) {
  if (comp.__asyncLoader) {
    return comp.__asyncLoader().then(res => res.default)
  }
  return comp
}
  1. Component matching during client hydration
function hydrateComponent(vnode, container) {
  if (isDynamicComponent(vnode.type)) {
    // Dynamic component hydration logic
    matchComponent(vnode, container.firstChild)
  }
}

Type System Integration

TypeScript support for dynamic components:

interface DynamicComponentProps {
  is: Component | string
}

// Component type inference
const comp = defineComponent({
  props: {
    component: {
      type: [Object, String] as PropType<Component | string>,
      required: true
    }
  },
  setup(props) {
    return () => h(resolveDynamicComponent(props.component))
  }
})

Custom Resolution Strategies

Custom resolution logic via app.config:

app.config.compilerOptions.isCustomElement = tag => {
  // Handle Web Components and other custom elements
  return tag.startsWith('x-')
}

app.config.componentResolve = (name) => {
  // Custom component resolution logic
  return components[name] || null
}

DevTools Support

DevTools optimizations for dynamic components:

  1. Display actual component names in the component tree
  2. Highlight keep-alive cache states
  3. Record dynamic component switching actions
// Component name resolution logic
function getComponentName(comp) {
  return comp.name || 
    comp.__name ||
    (comp.displayName || comp.__file || '').match(/([^/]+)(?=\.\w+$)/)?.[0]
}

Interaction with Other Features

Dynamic components working with other features:

  1. Teleport: Dynamic components can contain teleport content
<component :is="modalComponent">
  <teleport to="#modals">
    <div class="modal"></div>
  </teleport>
</component>
  1. Suspense: Handling nested async components
<Suspense>
  <component :is="asyncComponent"/>
  <template #fallback>
    <LoadingIndicator/>
  </template>
</Suspense>
  1. v-model: Two-way binding on dynamic components
<component :is="inputComponent" v-model="text"/>

Underlying Virtual DOM Differences

Special attributes of dynamic component VNodes:

interface VNode {
  // Dynamic component specific attributes
  __isDynamic?: boolean
  __dynamicProps?: string[]
  __dynamicContext?: RendererContext
}

Special handling in the diff algorithm:

function isSameVNodeType(n1: VNode, n2: VNode): boolean {
  // Dynamic component type comparison
  if (n1.type !== n2.type && 
      !(isDynamicComponent(n1.type) && 
      isDynamicComponent(n2.type))) {
    return false
  }
  return true
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱: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 ☕.