前言
原文再续,书接上一回,上次讲到optimize函数标记了静态节点,这里就来讲generate发生的事情。还是这张图:
graph LR A[template 模板]-->|parse|B[AST 抽象语法树]-->|optimize|C[标记静态节点]-->|generate|D[render]-->|return|E[虚拟 DOM]
Vue 组件调用render函数返回模板对应的虚拟 DOM,也就是VNode,上回的都是 AST 节点的事情,generate做的是,把 AST 节点变成render函数。
入口函数
这个函数在 src/compiler/codegen/index.ts,结构也是很简单。
ts- export function generate(
- ast: ASTElement | void,
- options: CompilerOptions
- ): CodegenResult {
- const state = new CodegenState(options)
- const code = ast
- ? ast.tag === 'script'
- ? 'null'
- : genElement(ast, state)
- : '_c("div")'
- return {
- render: `with(this){return ${code}}`,
- staticRenderFns: state.staticRenderFns
- }
- }
state是一些进行数据转换的函数,下面遇到再说,这里主要关注标签和文本是怎么被渲染的。with(this){return ${code}}这里的this很明显是指 Vue 组件实例,genElement中生成了render的主要代码。
生成 render 函数
先来看genElement:
ts- export function genElement(el: ASTElement, state: CodegenState): string {
- // v-if、v-for 等指令的处理,节点静态提升等等...
- {
- let code
- if (el.component) {
- // 组件的处理,以后再说...
- code = genComponent(el.component, el, state)
- } else {
- let data
- const maybeComponent = state.maybeComponent(el)
- if (!el.plain || (el.pre && maybeComponent)) {
- data = genData(el, state)
- }
- // check if this is a component in <script setup> ...
- const children = el.inlineTemplate ? null : genChildren(el, state, true)
- code = `_c(${tag}${
- data ? `,${data}` : '' // data
- }${
- children ? `,${children}` : '' // children
- })`
- }
- // module transforms
- for (let i = 0; i < state.transforms.length; i++) {
- code = state.transforms[i](el, code)
- }
- return code
- }
- }
data = genData(el, state),genData这个函数返回一个对象的 JSON 字符串,记录了模板上往标签上写的各种属性和事件监听,例如:
html- <div
- data="test"
- ></div>
data就是:
json- {attrs:{"data":"test"}}
genChildren遍历子元素,最终对于标签、注释、文本 3 种类型的子元素分别调用genElement(递归回去,继续遍历子节点)、genComment、genText。
ts- export function genText(text: ASTText | ASTExpression): string {
- return `_v(${
- text.type === 2
- ? text.expression // no need for () because already wrapped in _s()
- : transformSpecialNewlines(JSON.stringify(text.text))
- })`
- }
ts- export function genComment(comment: ASTText): string {
- return `_e(${JSON.stringify(comment.text)})`
- }
如果有这样子的标签:
html- <div data="test"><span>123</span></div>
genElement生成这样子的函数片段:
ts- _c('div',{attrs:{"data":"test"}},[_c('span',[_v("123")])])
其中,_c是一个生成标签形式 VNode 的函数,而_v处理文本节点,上文的_e自然是处理注释节点的函数。至于具体是什么,下次再说。
总结
本篇文章介绍了模板编译三大阶段的最后一个阶段——generate阶段。
在genElement和genChildren的递归调用下,generate把模板对应的 AST 节点转换为render函数。
0 条评论未登录用户
Ctrl or + Enter 评论
