阿里云主机折上折
  • 微信号
Current Site:Index > Special handling of instruction compilation

Special handling of instruction compilation

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

Special Handling of Directive Compilation

Vue3's directive system undergoes a transformation from template strings to executable code during the compilation phase. This conversion involves several key steps, including directive parsing, AST generation, and code generation. The uniqueness of directives lies in their need to handle both static attributes and dynamic bindings simultaneously, which requires additional processing logic during compilation.

Basic Flow of Directive Parsing

When the compiler encounters attributes with the v- prefix, it recognizes them as directives and enters a special processing flow. The parsing process first separates the directive name and its arguments:

// Example directive: v-bind:title="message"
const dirRE = /^v-([^:]+)(?:$|:(.*)$)/;
const match = dirRE.exec('v-bind:title');
// match result: ["v-bind:title", "bind", "title"]

For modifiers, the compiler parses them into an object format:

// v-model.trim.number is parsed as
const modifiers = {
  trim: true,
  number: true
}

Transformation of Structural Directives

Structural directives like v-if and v-for alter the basic DOM structure. During compilation, they are converted into conditional expressions or loop statements. For example:

<div v-if="show">Content</div>

is compiled into:

function render() {
  return (show)
    ? _createVNode("div", null, "Content")
    : _createCommentVNode("v-if", true)
}

The processing of v-for directives is more complex, requiring the generation of iteration functions:

<li v-for="(item, index) in items" :key="item.id">
  {{ item.name }}
</li>

The compilation result is:

function render() {
  return _renderList(items, (item, index) => {
    return _createVNode("li", { key: item.id }, [
      _toDisplayString(item.name)
    ])
  })
}

Encapsulation of Event Directives

Event directives like v-on need to handle event modifiers and multiple event bindings. The compiler generates wrapper functions that include modifier logic:

<button v-on:click.stop.prevent="handleClick">Click</button>

The compiled code includes modifier handling:

function render() {
  return _createVNode("button", {
    onClick: _withModifiers(handleClick, ["stop", "prevent"])
  }, "Click")
}

Compilation Path for Custom Directives

After registering custom directives via the directives option, the compiler generates special patch flags for them:

app.directive('focus', {
  mounted(el) {
    el.focus()
  }
})

When used in templates:

<input v-focus />

The compilation result includes lifecycle calls for the directive:

function render() {
  return _withDirectives(_createVNode("input"), [
    [_directive_focus]
  ])
}

Special Handling of Dynamic Arguments

Dynamic argument directives require additional runtime parsing logic:

<div v-bind:[key]="value"></div>

The compiled code includes dynamic property name handling:

function render() {
  return _createVNode("div", {
    [key]: value
  })
}

Directive Merge Strategy

When multiple directives are applied to the same element, the compiler needs to determine their execution order. For example, when both v-if and v-for are present:

<div v-for="item in list" v-if="item.visible"></div>

In this case, the compiler prioritizes v-for, treating it as the outer loop, while v-if becomes an internal conditional check:

function render() {
  return _renderList(list, (item) => {
    return item.visible
      ? _createVNode("div", null, [...])
      : null
  })
}

Directive Differences in Server-Side Rendering

In SSR environments, certain client-side directives require special handling. For example, v-show is always rendered in the visible state on the server:

<div v-show="isVisible">Content</div>

The SSR compilation result ignores the v-show directive:

function render() {
  return _createVNode("div", null, "Content")
}

Performance Optimization Flags for Directives

The compiler adds optimization flags to static directives to avoid unnecessary update checks. For example, static class bindings:

<div class="static" v-bind:class="{ active: isActive }"></div>

During compilation, static and dynamic parts are separated:

function render() {
  return _createVNode("div", {
    class: _normalizeClass(["static", { active: isActive }])
  })
}

Compile-Time Directive Conversion

Certain directives are converted into simpler JavaScript expressions during compilation. For example, the v-once directive:

<span v-once>{{ message }}</span>

is compiled into a static string:

function render() {
  return _createVNode("span", null, _toDisplayString(message), 1 /* TEXT */)
}

Here, 1 is a static flag indicating that the node will not update.

Directives and Scoped Styles

When directives affect DOM structure, the compiler ensures scoped styles are correctly applied. For example, in v-if branches:

<style scoped>
.box { color: red }
</style>
<div v-if="show" class="box"></div>

The compiler adds attribute selectors for scoped styles:

function render() {
  return (show)
    ? _createVNode("div", { class: "box", "data-v-xxxx": "" })
    : null
}

Cross-Platform Handling of Directives

Different platforms may support directives differently. The compiler converts directives based on the target platform. For example, in WeChat Mini Programs:

<view v-if="show"></view>

might be compiled into a Mini Program conditional statement:

// WeChat Mini Program compilation result
{
  "type": "view",
  "wx:if": "{{show}}"
}

TypeScript Support for Directives

In TypeScript environments, the compiler ensures type safety for directive parameters. For example, generating type checks for v-model:

interface DirectiveBinding<T = any> {
  value: T
  oldValue: T | null
  // ...
}

function useDirective(binding: DirectiveBinding<string>) {
  // Only accepts string values
}

Compilation Caching for Directives

The compiler caches frequently used directive patterns for optimization. For example, identical v-for patterns:

<div v-for="item in list" :key="item.id">{{ item.name }}</div>

The compilation result is cached to avoid repeated parsing:

const _hoisted_for_template = (item) => [
  _createTextVNode(_toDisplayString(item.name))
]

function render() {
  return _renderList(list, (item) => {
    return _createVNode("div", { key: item.id }, _hoisted_for_template(item))
  })
}

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

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