阿里云主机折上折
  • 微信号
Current Site:Index > The process of code generation (Codegen) translates this sentence into English.

The process of code generation (Codegen) translates this sentence into English.

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

The Process of Code Generation (Codegen)

In Vue 3's compiler, code generation is the final and crucial stage when transforming templates into render functions. This stage takes the transformed and optimized Abstract Syntax Tree (AST) as input and outputs executable JavaScript code strings, ultimately forming the component's render function.

Transformation from Template AST to Render Code

The compiler traverses AST nodes and generates corresponding JavaScript code snippets based on node types. For example, a simple div element:

<div class="container">Hello</div>

The corresponding AST node structure roughly looks like this:

{
  "type": 1,
  "tag": "div",
  "attrsList": [{"name": "class", "value": "container"}],
  "children": [{"type": 2, "content": "Hello"}]
}

The code generator will transform it into:

const _Vue = Vue
return function render(_ctx, _cache) {
  with (_ctx) {
    return _createVNode("div", { class: "container" }, "Hello")
  }
}

Static Hoisting Optimization

Vue 3 performs hoisting optimization for static nodes to avoid repeated creation. For example:

<div>
  <span>static</span>
  <span>{{ dynamic }}</span>
</div>

The generated code separates static and dynamic parts:

const _hoisted_1 = _createVNode("span", null, "static")

return function render(_ctx, _cache) {
  with (_ctx) {
    return _createVNode("div", null, [
      _hoisted_1,
      _createVNode("span", null, dynamic)
    ])
  }
}

Event Handling Generation

For event bindings, the code generator performs special processing:

<button @click="handleClick">Click</button>

The generated code converts the event into the correct format:

return function render(_ctx, _cache) {
  with (_ctx) {
    return _createVNode("button", {
      onClick: _cache[1] || (_cache[1] = (...args) => handleClick(...args))
    }, "Click")
  }
}

Slot Content Generation

Slot handling is one of the more complex parts of code generation. For named slots:

<slot name="header" :user="user"></slot>

The generated code includes the slot's context information:

return function render(_ctx, _cache) {
  with (_ctx) {
    return _renderSlot(_ctx.$slots, "header", {
      user: user
    })
  }
}

Conditional Rendering Generation

v-if/v-else directives are transformed into conditional expressions:

<div v-if="show">A</div>
<div v-else>B</div>

The generated ternary expression code:

return function render(_ctx, _cache) {
  with (_ctx) {
    return show
      ? _createVNode("div", null, "A")
      : _createVNode("div", null, "B")
  }
}

List Rendering Generation

v-for directives are transformed into array map operations:

<li v-for="item in items" :key="item.id">{{ item.text }}</li>

The generated rendering logic:

return function render(_ctx, _cache) {
  with (_ctx) {
    return _createVNode("ul", null, 
      items.map(item => 
        _createVNode("li", { key: item.id }, item.text)
      )
    )
  }
}

Component Generation

Custom component generation includes more context information:

<MyComponent :msg="message" @update="handleUpdate" />

The generated component invocation code:

return function render(_ctx, _cache) {
  with (_ctx) {
    return _createVNode(MyComponent, {
      msg: message,
      onUpdate: _cache[1] || (_cache[1] = (...args) => handleUpdate(...args))
    })
  }
}

Static Attribute Merging

For cases with mixed static and dynamic attributes:

<div class="static" :class="dynamicClass"></div>

The generator merges attribute handling:

return function render(_ctx, _cache) {
  with (_ctx) {
    return _createVNode("div", {
      class: _normalizeClass(["static", dynamicClass])
    })
  }
}

Directive Generation

Custom directives require special handling:

<div v-my-directive:arg.modif="value"></div>

The generated directive code includes complete directive information:

return function render(_ctx, _cache) {
  with (_ctx) {
    return _withDirectives(_createVNode("div"), [
      [_resolveDirective("my-directive"), value, "arg", { modif: true }]
    ])
  }
}

Structure of the Generated Function

The complete generated function typically includes these parts:

export function generate(ast, options) {
  const context = createCodegenContext(ast, options)
  const { push, indent, deindent } = context
  
  // Generate function prefix
  push(`const _Vue = Vue\n`)
  push(`return function render(_ctx, _cache) {\n`)
  indent()
  push(`with (_ctx) {\n`)
  indent()
  
  // Generate main content
  genNode(ast, context)
  
  // Generate function suffix
  deindent()
  push(`}\n`)
  deindent()
  push(`}`)
  
  return {
    code: context.code,
    ast,
    map: context.map
  }
}

Key Functions in the Source Code

In Vue's source code, several key functions handle different parts of generation:

// packages/compiler-core/src/codegen.ts
function genElement(node: ElementNode, context: CodegenContext) {
  const { push, helper } = context
  const { tag, props, children } = node
  
  if (tag === 'slot') {
    genSlot(node, context)
  } else if (tag === 'template') {
    genChildren(node, context)
  } else {
    // Component or native element
    push(`${helper(CREATE_VNODE)}(`)
    genNodeList([tag, props, children], context)
    push(`)`)
  }
}

function genChildren(node: ParentNode, context: CodegenContext) {
  const { push, children } = context
  if (children.length) {
    push('[')
    genNodeList(children, context)
    push(']')
  }
}

Performance Optimization Handling

The generator applies optimization strategies for different scenarios:

  1. Static node hoisting
  2. Event handler caching
  3. Attribute merging
  4. Block marking (Block tree)

For example, a block with v-if:

// The generated code marks it as a dynamically updatable block
return function render(_ctx, _cache) {
  with (_ctx) {
    return (_openBlock(), _createBlock("div", null, [
      _createVNode("p", null, "Static"),
      show ? _createVNode("p", null, "Dynamic") : _createCommentVNode("v-if")
    ]))
  }
}

Source Map Generation

In development environments, source maps are generated for debugging:

function generate(
  ast: RootNode,
  options: CodegenOptions
): CodegenResult {
  const context = createCodegenContext(ast, options)
  // ...Generate code
  
  return {
    code: context.code,
    ast,
    map: context.map ? context.map.toJSON() : undefined
  }
}

Server-Side Rendering Differences

Code generation differs for server-side rendering:

// Client-side rendering
_createVNode("div", { class: "foo" }, "hello")

// Server-side rendering
_createSSRNode("div", { class: "foo" }, "hello")

Extensibility of the Generator

Vue's code generator design supports plugin extensions:

interface CodegenPlugin {
  (ast: RootNode, context: CodegenContext): void
}

function createCodegenContext(
  ast: RootNode,
  options: CodegenOptions
) {
  const plugins = options.plugins || []
  const context = { /* ... */ }
  
  plugins.forEach(plugin => plugin(ast, context))
  return context
}

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

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