持续上一篇的节奏,上一篇咱们剖析了Vue3
的render
函数的生成进程,在genNode
函数中,咱们看到了许多节点类型的生成函数;
今日咱们就来详细剖析一下这些节点类型的生成函数,看看它们是怎么生成的;、
genNode
咱们先简略的回顾一下上一篇的genNode
函数,详细能够看上一篇的文章:【源码&库】Vue3模版解析后的AST转化为render函数的进程
function genNode(node, context) {
// 假如是 string 类型,则直接作为代码
if (isString(node)) {
context.push(node);
return;
}
// 假如是 symbol 类型,则运用辅佐函数生成代码
if (isSymbol(node)) {
context.push(context.helper(node));
return;
}
// 依据节点类型,履行不同的生成函数
switch (node.type) {
case 1: // 元素节点
case 9: // if
case 11: // for
// 这些节点直接递归生成
assert(
node.codegenNode != null,
`Codegen node is missing for element/if/for node. Apply appropriate transforms first.`
);
genNode(node.codegenNode, context);
break;
case 2: // 文本节点
genText(node, context);
break;
case 4: // 表达式节点
genExpression(node, context);
break;
case 5: // 插值节点
genInterpolation(node, context);
break;
case 12: // fragment 节点
genNode(node.codegenNode, context);
break;
case 8: // 复合表达式节点
genCompoundExpression(node, context);
break;
case 3: // 注释节点
genComment(node, context);
break;
case 13: // 生成 createVNode 的调用
genVNodeCall(node, context);
break;
case 14: // 生成一般函数调用
genCallExpression(node, context);
break;
case 15: // 生成目标表达式
genObjectExpression(node, context);
break;
case 17: // 生成数组表达式
genArrayExpression(node, context);
break;
case 18: // 生成函数表达式
genFunctionExpression(node, context);
break;
case 19: // 生成条件表达式
genConditionalExpression(node, context);
break;
case 20: // 生成缓存表达式
genCacheExpression(node, context);
break;
case 21: // 生成节点列表
genNodeList(node.body, context, true, false);
break;
case 22:
break;
case 23:
break;
case 24:
break;
case 25:
break;
case 26:
break;
case 10:
break;
default:
{
assert(false, `unhandled codegen node type: ${node.type}`);
const exhaustiveCheck = node;
return exhaustiveCheck;
}
}
}
在这儿咱们能够看到有许多的节点类型,如下:
-
genText
: 文本节点 -
genExpression
: 表达式节点 -
genInterpolation
: 插值节点 -
genCompoundExpression
: 复合表达式节点 -
genComment
: 注释节点 -
genVNodeCall
: 生成 createVNode 的调用 -
genCallExpression
: 生成一般函数调用 -
genObjectExpression
: 生成目标表达式 -
genArrayExpression
: 生成数组表达式 -
genFunctionExpression
: 生成函数表达式 -
genConditionalExpression
: 生成条件表达式 -
genCacheExpression
: 生成缓存表达式 -
genNodeList
: 生成节点列表
咱们就来一一剖析一下这些节点类型的生成函数,咱们这一章仍是一样能够经过如下示例代码进行调试和剖析:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id='app'></div>
</body>
<script src="./vue.global.js"></script>
<script>
const {createApp, h} = Vue;
const app = createApp({
template: '直接在这儿写模板语法'
});
debugger;
app.mount('#app');
</script>
</html>
genText
genText
函数用来生成文本节点,详细代码如下:
function genText(node, context) {
// 假如是纯文本,则直接经过 JSON.stringify 转成字符串
// 最终的结果是 return "xxx"
context.push(JSON.stringify(node.content), node);
}
验证代码如下:
const app = createApp({
template: 'xxx'
});
生成的代码如下:
return function render(_ctx, _cache) {
with (_ctx) {
return "xxx"
}
}
genExpression
genExpression
函数用来生成表达式节点,详细代码如下:
function genExpression(node, context) {
// 获取表达式 和 是否静态标识
const { content, isStatic } = node;
// 假如是静态的,直接将内容作为字符串进行处理,不然就直接将表达式进行返回
context.push(isStatic ? JSON.stringify(content) : content, node);
}
验证代码如下:
const app = createApp({
template: '{{ 1 + 1 }}'
});
生成的代码如下:
const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString } = _Vue
return _toDisplayString(1 + 1)
}
}
genInterpolation
能够看到上面的终究生成的结果会有一个toDisplayString
的函数包装,这个函数是由genInterpolation
函数生成的;
而这个函数便是由genInterpolation
函数生成的,这个函数便是用来处理插值表达式的,详细代码如下:
function genInterpolation(node, context) {
// 从上下文中获取 push、helper 和 pure
const { push, helper, pure } = context;
// pure 用来符号是否是纯的,假如是纯的,则会增加 PURE_ANNOTATION
// 也便是 /*#__PURE__*/ 符号
// 有了这个符号,在代码优化阶段,就会告诉编译器,能够安全地删去或替换它的调用,而不影响程序的行为
if (pure)
push(PURE_ANNOTATION);
// TO_DISPLAY_STRING 便是生成 toDisplayString 函数的辅佐函数
// 经过调用 helper(TO_DISPLAY_STRING) 就会生成:_toDisplayString
push(`${helper(TO_DISPLAY_STRING)}(`);
// 递归生成插值表达式的内容,依照咱们的示例就会进入上面的 genExpression 函数
genNode(node.content, context);
// 最终将 ) 增加到代码中
push(`)`);
}
验证代码和结果同上;
genCompoundExpression
复合表达式节点指的是一个节点中包括多个表达式,例如:
{{ 1 + 1 }} {{ 2 + 2 }}
,这儿便是一个复合表达式节点,这个节点中包括了两个表达式,分别是1 + 1
和2 + 2
;
只要是一个节点中包括多个表达式,那么这个节点便是一个复合表达式节点;
genCompoundExpression
函数用来生成复合表达式节点,详细代码如下:
function genCompoundExpression(node, context) {
// 直接遍历子节点
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
// 假如是 string 类型,则直接推入到上下文中
if (isString(child)) {
context.push(child);
}
// 不然就递归调用 genNode 函数
else {
genNode(child, context);
}
}
}
验证代码如下:
const app = createApp({
template: '{{ 1 + 1 }} {{ 2 + 2 }}'
});
生成的代码如下:
const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString } = _Vue
return _toDisplayString(1 + 1) + " " + _toDisplayString(2 + 2)
}
}
genComment
genComment
函数用来生成注释节点,详细代码如下:
function genComment(node, context) {
// 同上
const { push, helper, pure } = context;
if (pure) {
push(PURE_ANNOTATION);
}
// helper(CREATE_COMMENT) 用来生成 _createCommentVNode 函数
// 将注释内容转化为 JSON 格式的字符串作为内容
push(`${helper(CREATE_COMMENT)}(${JSON.stringify(node.content)})`, node);
}
验证代码如下:
const app = createApp({
template: '<!-- 这是注释 -->'
});
生成的代码如下:
const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { createCommentVNode: _createCommentVNode } = _Vue
return _createCommentVNode(" 这是注释 ")
}
}
genVNodeCall
genVNodeCall
函数用来生成复合表达式节点,详细代码如下:
function genVNodeCall(node, context) {
// 获取 push、helper 和 pure
const { push, helper, pure } = context;
// 获取节点的类型和 props
const {
tag,
props,
children,
patchFlag,
dynamicProps,
directives,
isBlock,
disableTracking,
isComponent
} = node;
// 是否存在指令,假如存在,则需求增加 WITH_DIRECTIVES 符号
// 最终会生成:_withDirectives
if (directives) {
push(helper(WITH_DIRECTIVES) + `(`);
}
// 是否是一个块,块指的是有能成对呈现的节点,像 html 标签都是
// 也会有自闭合标签,像 img、input 等,可是他们也都是成块的
// 所以我理解的块指的是不行拆分的一个整体就表明为一个块
// 这儿最终会生成:_openBlock
if (isBlock) {
push(`(${helper(OPEN_BLOCK)}(${disableTracking ? `true` : ``}), `);
}
// 是否纯函数
if (pure) {
push(PURE_ANNOTATION);
}
// 依据是否是块节点和是否是组件选择恰当的 createVNode 辅佐函数
// 这儿最终就便是会生成不同的函数处理函数,能够看下面贴出来的 getVNodeBlockHelper 和 getVNodeHelper 函数
const callHelper = isBlock ? getVNodeBlockHelper(context.inSSR, isComponent) : getVNodeHelper(context.inSSR, isComponent);
push(helper(callHelper) + `(`, node);
// 生成包括 createVNode 调用的参数列表
// genNullableArgs 用于生成可空的参数列表,这个函数便是会移除尾部的控制,然后将剩下的参数列表返回
// 例如 ['div', null, [], null, null] 会生成 ['div', null, []]
// 然后 ['div', null, []] 这个便是一个函数的参数列表,例如 fn('div', null, []) 这样的调用
// 后边会有 genNodeList 函数的讲解
genNodeList(
genNullableArgs([tag, props, children, patchFlag, dynamicProps]),
context
);
// 推入函数的完毕括号
push(`)`);
// 假如是块节点,则需求推入块节点的完毕括号
if (isBlock) {
push(`)`);
}
// 假如存在指令,则推入逗号和生成的指令函数,实质也是经过递归调用 genNode 函数
if (directives) {
push(`, `);
genNode(directives, context);
push(`)`);
}
}
function getVNodeHelper(ssr, isComponent) {
return ssr || isComponent ? CREATE_VNODE : CREATE_ELEMENT_VNODE;
}
function getVNodeBlockHelper(ssr, isComponent) {
return ssr || isComponent ? CREATE_BLOCK : CREATE_ELEMENT_BLOCK;
}
验证代码如下:
const app = createApp({
template: '<div>xxx</div>'
});
生成的代码如下:
const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
return (_openBlock(), _createElementBlock("div", null, "xxx"))
}
}
genCallExpression
一般函数调用自身不是由开发者直接书写的,而是内部处理的,例如运用 v-if 会生成一个注释节点来进行符号(假如节点躲藏,没有代替的节点),这个时分就会生成 _createCommentVNode 函数的调用;
genCallExpression
函数用来生成一般函数调用,详细代码如下:
function genCallExpression(node, context) {
// 获取 push、helper 和 pure
const { push, helper, pure } = context;
// 这一步是生成调用函数名
const callee = isString(node.callee) ? node.callee : helper(node.callee);
// 纯
if (pure) {
push(PURE_ANNOTATION);
}
// 推入函数名和左括号
push(callee + `(`, node);
// 这一步能够理解为生成调用函数的参数列表
genNodeList(node.arguments, context);
// 推入右括号
push(`)`);
}
验证代码如下:
const app = createApp({
template: '<div v-if="true">xxx</div>',
});
生成的代码如下:
const _Vue = Vue
const { createCommentVNode: _createCommentVNode } = _Vue
const _hoisted_1 = { key: 0 }
return function render(_ctx, _cache) {
with (_ctx) {
const { openBlock: _openBlock, createElementBlock: _createElementBlock, createCommentVNode: _createCommentVNode } = _Vue
return true
? (_openBlock(), _createElementBlock("div", _hoisted_1, "xxx"))
: _createCommentVNode("v-if", true)
}
}
genObjectExpression
目标表达式指的是一个目标的键值对,例如咱们运用的 @click=”xxx” 最终的解释会变成 onCLick: xxx,这儿的 onClick 便是一个目标表达式;
还有其他的状况,例如 v-bind 也会生成一个目标表达式;
genObjectExpression
函数用来生成目标表达式,详细代码如下:
function genObjectExpression(node, context) {
// 取出一些辅佐函数
const { push, indent, deindent, newline } = context;
// 取出 properties 特点列表
const { properties } = node;
// 假如没有特点,则直接推入 {},表明空目标
if (!properties.length) {
push(`{}`, node);
return;
}
// 判断是否多行,假如是多行,则需求增加缩进
const multilines = properties.length > 1 || properties.some((p) => p.value.type !== 4);
push(multilines ? `{` : `{ `);
multilines && indent();
// 遍历特点列表
for (let i = 0; i < properties.length; i++) {
const { key, value } = properties[i];
// 生成 key
genExpressionAsPropertyKey(key, context);
push(`: `);
// 生成 value
genNode(value, context);
// 假如不是最终一个,则推入逗号和换行
if (i < properties.length - 1) {
push(`,`);
newline();
}
}
// 假如是多行,则需求削减缩进
multilines && deindent();
// 推入右括号
push(multilines ? `}` : ` }`);
}
验证代码如下:
const app = createApp({
template: '<div @click="() => {}">xxx</div>'
});
生成的代码如下:
const _Vue = Vue
const { } = _Vue
const _hoisted_1 = ["onClick"]
return function render(_ctx, _cache) {
with (_ctx) {
const { openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
return (_openBlock(), _createElementBlock("div", { onClick: () => {} }, "xxx", 8 /* PROPS */, _hoisted_1))
}
}
genArrayExpression
数组表达式和目标表达式相似,只不过是一个数组的形式,可用于自定义指令的参数列表;
genArrayExpression
函数用来生成数组表达式,详细代码如下:
function genArrayExpression(node, context) {
// 内部是调用 genNodeListAsArray 函数
genNodeListAsArray(node.elements, context);
}
function genNodeListAsArray(nodes, context) {
// 判断是否多行,多行就增加缩进
const multilines = nodes.length > 3 || nodes.some((n) => isArray(n) || !isText(n));
context.push(`[`);
multilines && context.indent();
// 仍是运用 genNodeList 来生成数据
genNodeList(nodes, context, multilines);
multilines && context.deindent();
context.push(`]`);
}
验证代码如下:
const app = createApp({
template: '<div v-xxx="[xxx, yyy]">{{i}}</div>',
});
生成的代码如下:
const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, resolveDirective: _resolveDirective, openBlock: _openBlock, createElementBlock: _createElementBlock, withDirectives: _withDirectives } = _Vue
const _directive_xxx = _resolveDirective("xxx")
return _withDirectives((_openBlock(), _createElementBlock("div", null, [
_createTextVNode(_toDisplayString(i), 1 /* TEXT */)
])), [
[_directive_xxx, [xxx, yyy]]
])
}
}
genFunctionExpression
函数表达式指的是需求用函数来处理的状况,表明内容现已是不行控了,例如 v-for、插槽等不行控的内容;
v-if 并不会生成函数表达式,因为 v-if 只是一个条件,最终会生成一个三元表达式;
genFunctionExpression
函数用来生成函数表达式,详细代码如下:
function genFunctionExpression(node, context) {
// 辅佐函数
const {push, indent, deindent} = context;
// 节点的一些特点
const {params, returns, body, newline, isSlot} = node;
// 假如是插槽函数,增加 _withCtx 辅佐函数
if (isSlot) {
push(`_${helperNameMap[WITH_CTX]}(`);
}
// 推入函数的左括号,箭头函数的开头
push(`(`, node);
// 假如参数是一个数组,则递归生成参数列表
if (isArray(params)) {
genNodeList(params, context);
}
// 不然就运用 genNode 生成参数
else if (params) {
genNode(params, context);
}
// 推入函数的右括号,箭头函数的完毕
push(`) => `);
if (newline || body) {
push(`{`);
indent();
}
// 假如有返回值,则生成返回值
if (returns) {
// 假如需求换行,输出 return
// 箭头函数假如没有花括号,能够直接返回,所以这儿需求增加 return
if (newline) {
push(`return `);
}
// 假如返回值是数组,调用 genNodeListAsArray 生成返回值列表的代码
if (isArray(returns)) {
genNodeListAsArray(returns, context);
}
// 不然就调用 genNode 生成返回值
else {
genNode(returns, context);
}
}
// 没有返回值,但存在函数体
else if (body) {
genNode(body, context);
}
// 假如需求换行,增加换行和缩进
if (newline || body) {
deindent();
push(`}`);
}
// 插槽函数,输出插槽函数的完毕符号
if (isSlot) {
push(`)`);
}
}
验证代码如下:
const app = createApp({
template: '<div v-for="i in 10">{{i}}</div>',
});
生成的代码如下:
const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, toDisplayString: _toDisplayString } = _Vue
return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(10, (i) => {
return (_openBlock(), _createElementBlock("div", null, _toDisplayString(i), 1 /* TEXT */))
}), 256 /* UNKEYED_FRAGMENT */))
}
}
genConditionalExpression
条件表达式指的是 v-if、v-else-if、v-else 等条件句子;
genConditionalExpression
函数用来生成条件表达式,详细代码如下:
function genConditionalExpression(node, context) {
// 各种状况的特点
const { test, consequent, alternate, newline: needNewline } = node;
// 辅佐函数
const { push, indent, deindent, newline } = context;
// 假如测验条件是一个字符串文本
if (test.type === 4) {
// 假如字符串内容不是一个简略的标识符,就需求加括号
const needsParens = !isSimpleIdentifier(test.content);
needsParens && push(`(`);
// 上面的 genExpression 函数便是用来生成表达式的
genExpression(test, context);
needsParens && push(`)`);
}
// 假如测验条件是一个复杂的表达式
else {
push(`(`);
// 递归调用 genNode 函数
genNode(test, context);
push(`)`);
}
// 假如需求换行,增加缩进
needNewline && indent();
// 缩进等级增加
context.indentLevel++;
needNewline || push(` `);
// 三元表达式的问号
push(`? `);
// 递归调用 genNode 函数,这是条件为真的状况
genNode(consequent, context);
// 缩进等级削减
context.indentLevel--;
needNewline && newline();
needNewline || push(` `);
// 三元表达式的冒号
push(`: `);
// 假如是嵌套的条件表达式,需求增加缩进等级
const isNested = alternate.type === 19;
if (!isNested) {
context.indentLevel++;
}
// 这儿是条件为假的状况
genNode(alternate, context);
// 假如是嵌套的条件表达式,需求削减缩进等级
if (!isNested) {
context.indentLevel--;
}
// 假如需求换行,履行削减缩进的操作(不带换行)
needNewline && deindent(
true
/* without newline */
);
}
验证代码如下:
const app = createApp({
template: '<div v-if="true">xxx</div>',
});
生成的代码如下:
const _Vue = Vue
const { createCommentVNode: _createCommentVNode } = _Vue
const _hoisted_1 = { key: 0 }
return function render(_ctx, _cache) {
with (_ctx) {
const { openBlock: _openBlock, createElementBlock: _createElementBlock, createCommentVNode: _createCommentVNode } = _Vue
return true
? (_openBlock(), _createElementBlock("div", _hoisted_1, "123"))
: _createCommentVNode("v-if", true)
}
}
genCacheExpression
缓存表达式首要运用于
v-once
指令,这个指令会将节点缓存起来,不会再进行更新;
genCacheExpression
函数用来生成缓存表达式,详细代码如下:
function genCacheExpression(node, context) {
// 辅佐函数
const {push, helper, indent, deindent, newline} = context;
// 这儿是生成缓存的 key
push(`_cache[${node.index}] || (`);
// 假如是 vnode 节点,则需求增加缩进和设置追寻符号
// 这儿设置为 -1,表明不需求追寻
if (node.isVNode) {
indent();
push(`${helper(SET_BLOCK_TRACKING)}(-1),`);
newline();
}
// 设置缓存的值
push(`_cache[${node.index}] = `);
// 仍是递归调用 genNode 函数来生成缓存的值
genNode(node.value, context);
// 启用缓存的追寻符号,并返回缓存的值
if (node.isVNode) {
push(`,`);
newline();
push(`${helper(SET_BLOCK_TRACKING)}(1),`);
newline();
push(`_cache[${node.index}]`);
deindent();
}
// 最终推入右括号
push(`)`);
}
验证代码如下:
const app = createApp({
template: '<div v-once>xxx</div>',
});
生成的代码如下:
const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { setBlockTracking: _setBlockTracking, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, createElementVNode: _createElementVNode } = _Vue
return _cache[0] || (
_setBlockTracking(-1),
_cache[0] = _createElementVNode("div", null, [
_createTextVNode(_toDisplayString("xxx"), 1 /* TEXT */)
]),
_setBlockTracking(1),
_cache[0]
)
}
}
genNodeList
实质上
genNodeList
函数是处理所有 list 类型的数据,在上述的许多函数中都会运用到;
本事例为了能更清晰的看到它的效果,能在genNode
中看到它的调用,会运用 v-for + v-memo 来进行验证;
v-memo 指令的效果不在本文章中进行讲解,需求的能够自行查阅资料;
genNodeList
函数用来生成节点列表,详细代码如下:
function genNodeList(nodes, context, multilines = false, comma = true) {
// 辅佐函数
const {push, newline} = context;
// 直接遍历节点
for (let i = 0; i < nodes.length; i++) {
// 获取当时节点
const node = nodes[i];
// 假如是字符串,则直接推入
if (isString(node)) {
push(node);
}
// 假如是数组,则递归调用 genNodeListAsArray 函数
// genNodeListAsArray 函数在上面现已呈现过了,这儿就不再赘述了
else if (isArray(node)) {
genNodeListAsArray(node, context);
}
// 不然就递归调用 genNode 函数
else {
genNode(node, context);
}
// 假如不是最终一个
if (i < nodes.length - 1) {
// 假如是多行,则推入逗号和换行
if (multilines) {
comma && push(",");
newline();
}
// 不然就推入逗号和空格
else {
comma && push(", ");
}
}
}
}
验证代码如下:
const app = createApp({
template: '<div v-for="i in 10" v-memo="[i]">i</div>',
});
生成的代码如下:
const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, createTextVNode: _createTextVNode, isMemoSame: _isMemoSame, withMemo: _withMemo } = _Vue
return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(10, (i, __, ___, _cached) => {
const _memo = ([i])
if (_cached && _isMemoSame(_cached, _memo)) return _cached
const _item = (_openBlock(), _createElementBlock("div", null, [
_createTextVNode("i")
]))
_item.memo = _memo
return _item
}, _cache, 0), 256 /* UNKEYED_FRAGMENT */))
}
}
总结
本章节首要讲解了genNode
函数,这个函数是用来生成节点的,这个函数会依据节点的类型来调用不同的函数来生成节点,例如文本节点、表达式节点、插值节点等等;
而经过这一章也了解到了巨量的Vue
的模版语法能够书写的方式,其中的许多细节也只要翻源码才知道原来还能够这样玩;
到这儿咱们的模板到AST
的转化,在从AST
的转化到render
函数的代码生成,现已全部完成了;
这一块足足花了五章的内容来进行剖析,信息量是巨大的,一起也算是完成了一个里程碑,收获满满,成就感也满满;
前史章节
- 【源码&库】跟着 Vue3 学习前端模块化
- 【源码&库】在调用 createApp 时,Vue 为咱们做了那些工作?
- 【源码&库】细数 Vue3 的实例方法和特点背后的故事
- 【源码&库】Vue3 中的 nextTick 魔法背后的原理
- 【源码&库】Vue3 的响应式核心 reactive 和 effect 完成原理以及源码剖析
- 【源码&库】跟着 Vue3 的源码学习 reactive 背后的完成原理
- 【源码&库】 Vue3 的依靠搜集,这儿的依靠指代的是什么?
- 【源码&库】 Vue3 的依靠搜集和依靠触发是怎么工作的
- 【源码&库】 Vue3 的组件是怎么挂载的?
- 【源码&库】 Vue3 的组件是怎么更新的?
- 【源码&库】 Vue3 的组件更新核心算法
- 【源码&库】 Vue3 的虚拟DOM生成规矩
- 【源码&库】 Vue3 大局组件注册怎么完成
- 【源码&库】Vue3的模板转化为AST的进程
- 【源码&库】Vue3的AST转化细节全解析
- 【源码&库】Vue3模版解析后的AST转化为render函数的进程