Template compilation pre-optimization
Template Compilation Pre-Optimization
During Vue 3's template compilation phase, before converting the template string into a render function, a series of pre-optimization operations are performed. These optimizations aim to reduce runtime overhead and improve performance. The core idea is to complete as much deterministic computation as possible during the compilation phase, avoiding repeated execution of the same logic during each render.
Static Node Hoisting
The compiler identifies static nodes in the template (nodes without any dynamic bindings) and hoists them outside the render function. This means these nodes only need to be created once during the initial render and can be reused in subsequent updates.
<div>
<h1>Static Title</h1>
<p>{{ dynamicContent }}</p>
</div>
In this example, the <h1>
tag is marked as a static node. The compiled code roughly looks like this:
const _hoisted_1 = /*#__PURE__*/_createVNode("h1", null, "Static Title", -1 /* HOISTED */)
function render() {
return (_openBlock(), _createBlock("div", null, [
_hoisted_1,
_createVNode("p", null, _toDisplayString(_ctx.dynamicContent), 1 /* TEXT */)
]))
}
Static Attribute Hoisting
Not only entire nodes but also individual static attributes are hoisted. This is particularly effective for elements with many static attributes:
<div class="container" id="main" data-static="value">
{{ dynamicText }}
</div>
After compilation, it becomes:
const _hoisted_1 = { class: "container", id: "main", "data-static": "value" }
function render() {
return (_openBlock(), _createBlock("div", _hoisted_1, _toDisplayString(_ctx.dynamicText), 1 /* TEXT */))
}
Patch Flag Marking
Vue 3 introduces the concept of Patch Flags. During compilation, dynamic nodes are marked to indicate specific content that needs to be checked at runtime, avoiding unnecessary full comparisons.
<div :class="dynamicClass" :id="dynamicId">{{ message }}</div>
The compiled result includes a patch flag (9 indicates that CLASS and ID need to be checked):
function render() {
return (_openBlock(), _createBlock("div", {
class: _normalizeClass(_ctx.dynamicClass),
id: _ctx.dynamicId
}, _toDisplayString(_ctx.message), 9 /* CLASS, ID */, 0))
}
Event Caching
For event handlers, the compiler generates cached code to avoid creating new function instances during each render:
<button @click="handleClick">Click</button>
After compilation, the event handler is cached:
function render() {
return (_openBlock(), _createBlock("button", {
onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.handleClick && _ctx.handleClick(...args)))
}, "Click"))
}
Dynamic Node Tracking
The compiler analyzes the structural relationships of dynamic nodes in the template and generates a Block Tree. This allows the runtime to skip static subtrees and only compare dynamic parts:
<div>
<div v-if="show">
<span>{{ a }}</span>
<span>{{ b }}</span>
</div>
<p>{{ c }}</p>
</div>
After compilation, a Block structure is formed, with the dynamic content inside v-if
organized together:
function render() {
return (_openBlock(), _createBlock("div", null, [
(_ctx.show)
? (_openBlock(), _createBlock("div", { key: 0 }, [
_createVNode("span", null, _toDisplayString(_ctx.a), 1 /* TEXT */),
_createVNode("span", null, _toDisplayString(_ctx.b), 1 /* TEXT */)
]))
: _createCommentVNode("v-if", true),
_createVNode("p", null, _toDisplayString(_ctx.c), 1 /* TEXT */)
]))
}
Pre-Stringification
When encountering consecutive static nodes, the compiler merges them into a static string:
<div>
<header>
<h1>Title</h1>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
<main>{{ content }}</main>
</div>
During compilation, the entire header section is stringified:
const _hoisted_1 = /*#__PURE__*/_createStaticVNode("<header><h1>Title</h1><nav><a href=\"/\">Home</a><a href=\"/about\">About</a></nav></header>", 1)
function render() {
return (_openBlock(), _createBlock("div", null, [
_hoisted_1,
_createVNode("main", null, _toDisplayString(_ctx.content), 1 /* TEXT */)
]))
}
Caching Inline Event Handlers
For inline event handler expressions, the compiler generates cached code:
<button @click="count++">Increment</button>
The compiled result caches the entire expression:
function render() {
return (_openBlock(), _createBlock("button", {
onClick: _cache[1] || (_cache[1] = $event => (_ctx.count++))
}, "Increment"))
}
Dynamic Props Optimization
When handling dynamic Props, the compiler generates optimal code based on different scenarios:
<component :value="data.value" @input="data.value = $event" />
After compilation, static and dynamic Props are separated:
function render() {
return (_openBlock(), _createBlock(_component_component, {
value: _ctx.data.value,
onInput: $event => (_ctx.data.value = $event)
}, null, 8 /* PROPS */, ["value", "onInput"]))
}
Slot Content Optimization
For slot content, the compiler distinguishes between static and dynamic parts:
<slot>
<div>Fallback Content</div>
<p>{{ dynamicText }}</p>
</slot>
After compilation, static content is hoisted:
const _hoisted_1 = /*#__PURE__*/_createVNode("div", null, "Fallback Content", -1 /* HOISTED */)
function render() {
return [_hoisted_1,
_createVNode("p", null, _toDisplayString(_ctx.dynamicText), 1 /* TEXT */]
}
Static Root Node Handling
When the entire template has only one static root node, the compiler performs special processing:
<div class="app-container">
<h1>Static Application</h1>
<p>All content is static</p>
</div>
The compiled result completely skips update logic:
const _hoisted_1 = /*#__PURE__*/_createStaticVNode("<div class=\"app-container\"><h1>Static Application</h1><p>All content is static</p></div>", 1)
function render() {
return _hoisted_1
}
Conditional Branch Optimization
For conditional rendering, the compiler analyzes the static nature of each branch:
<div>
<template v-if="condition">
<h1>Title</h1>
</template>
<template v-else>
<h1>Another Title</h1>
</template>
</div>
After compilation, static nodes in both branches are hoisted:
const _hoisted_1 = /*#__PURE__*/_createVNode("h1", null, "Title", -1 /* HOISTED */)
const _hoisted_2 = /*#__PURE__*/_createVNode("h1", null, "Another Title", -1 /* HOISTED */)
function render() {
return (_openBlock(), _createBlock("div", null, [
(_ctx.condition)
? _hoisted_1
: _hoisted_2
]))
}
Dynamic Component Optimization
The is
attribute of dynamic components receives special treatment:
<component :is="currentComponent" />
The compiled result generates optimized dynamic component code:
function render() {
return (_openBlock(), _createBlock(_resolveDynamicComponent(_ctx.currentComponent)))
}
Static Class and Style Handling
Static class
and style
are normalized in advance:
<div class="static" :class="dynamicClass" style="color: red" :style="dynamicStyle"></div>
The compiled result separates static and dynamic parts:
const _hoisted_1 = {
class: "static",
style: {"color":"red"}
}
function render() {
return (_openBlock(), _createBlock("div", {
class: _normalizeClass([_hoisted_1.class, _ctx.dynamicClass]),
style: _normalizeStyle([_hoisted_1.style, _normalizeStyle(_ctx.dynamicStyle)])
}, null, 12 /* CLASS, STYLE */))
}
Static Props Object Hoisting
When passing a static Props object, the entire object is hoisted:
<child-component :options="{ size: 'large', variant: 'primary' }" />
After compilation, the entire options
object is hoisted:
const _hoisted_1 = {
options: { size: 'large', variant: 'primary' }
}
function render() {
return (_openBlock(), _createBlock(_component_child_component, _hoisted_1))
}
Static Slot Content Hoisting
Static slot content is hoisted as a whole:
<my-component>
<template #header>
<h1>Static Header</h1>
</template>
</my-component>
After compilation, the entire slot content is hoisted:
const _hoisted_1 = /*#__PURE__*/_createVNode("h1", null, "Static Header", -1 /* HOISTED */)
function render() {
return (_openBlock(), _createBlock(_component_my_component, null, {
header: () => [_hoisted_1]
}))
}
Dynamic Directive Argument Optimization
For dynamic directive arguments, the compiler generates special handling code:
<div v-bind:[dynamicAttr]="value"></div>
The compiled result includes dynamic attribute handling logic:
function render() {
return (_openBlock(), _createBlock("div", {
[_ctx.dynamicAttr]: _ctx.value
}, null, 16 /* FULL_PROPS */))
}
Complete Hoisting of Static Templates
When the entire template consists of static content, the compiler generates a completely static render function:
<div>
<h1>Static Page</h1>
<p>This page contains no dynamic content</p>
</div>
The compiled result is a simple static node:
const _hoisted_1 = /*#__PURE__*/_createStaticVNode("<div><h1>Static Page</h1><p>This page contains no dynamic content</p></div>", 1)
function render() {
return _hoisted_1
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:代码体积的缩减技术
下一篇:服务端渲染的水合优化