The process of code generation (Codegen) translates this sentence into English.
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:
- Static node hoisting
- Event handler caching
- Attribute merging
- 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
下一篇:静态分析的实现方法