Vue3.2 编译原理分析:AST树语义分析(一)

757 阅读6分钟

image.png

前言

transform阶段是对AST树的升华,可以让其在generate阶段更好的产生的渲染函数,这里我们只关心内置的transform,用户的transfomr不去看,内置的transform如下:

image.png

Vue的提供了十四个transform,但是在浏览器平台上最多只会执行十三个,常态下只会执行十二个,有一个是兼容模式下才会执行。接下来就是看看这十三个的处理逻辑。

分析v-if/v-else-if/v-else指令

image.png

分析这三个指令主要有三个流程,第一步是创建条件分支,首先是v-if,除了创建分支以外,还会创建一个ifNode,这被称为v-if根,而v-if的条件分支会自动的放入这个根中。

image.png

v-else-ifv-else在创建分支之前,它会从自己的位置往上去找离自己最近的v-if根,因为这两个指令必须在v-if之后使用,如果没有找到就会报错,在这过程中,如果遇到字符串节点并且去掉空格之后长度是0的会将其移除。

image.png

在找到之后需要做一个检验,因为v-else可以在v-else-if之后也可以在v-if之后,但是v-else-if必须要在v-if,所以如果v-if根中的条件分支的最后一个不存在条件表达式(v-else不需要条件表达式),说明是v-if后面跟着v-else了,不能再跟着v-else-if,会直接报错。

image.png

前面的校验通过之后,会将v-else-ifv-else在AST树中的node移动到v-if根中,其实是先移除自己在AST树中的节点,再创建条件分支,在添加进v-if根中之前会判断分支的key是否重复,重复报错。

image.png

第二个流程是去执行processCodegen,这倒是非常简单,就是给多个相同等级添加不同的key,而后返回一个退出函数。

第三个流程就是去执行processCodegen返回的退出函数,去创建属于分支的codegen,退出函数的执行时机是要处理完分支里面的子元素。

v-ifcodegen是正常的在v-if根上,而v-else-ifv-else则是放在v-ifcodegen上,如果三个都存在,那么存放的关系是:v-else放在v-else-if上,v-else-if放在v-if上。所以v-if是直接去创建codegen,其他两个在创建codegen的时候,需要去拿到自己前面的codegen

image.png

进入createCodegenNodeForBranch中,v-ifv-else-if带条件走if,而v-elseelse。前面也说到了,三个指令的codegen节点的存放关系,所以v-ifv-else-if需要产生一个条件表达式对象,v-else不需要。v-if/v-else-if/v-else指令分析完毕。

分析v-for指令

v-for的语义分析和v-if的流程差不多,主要是靠transformFor,里面是去调用processFor,我们先去看它的实现。

image.png

确认v-for指令的表达式存在,执行parseForExpression去解析它。

image.png

首先我们要知道几个正则表达式,假设我们的表达式长这样:(item, name, index) in|of listforAliasRE会匹配出三部分,分别是:(item, name, index)in|oflist stripParensRE匹配是 (item, name, index)中的括号,得出里面的内容item, name, index forIteratorRE 匹配item, name, index中的 ,name,index

image.png

进入parseForExpression,会先用forAliasRE匹配出前半部和后半部,后半部也就是list,会产生一个srouce对象。放到result中(这是表达式解析结果的汇总)

image.png

这里是要拿到(item, name, index)nameindex,这两个东西是可选参数,这里匹配是否存在,存在才会进入它们两个的处理流程。匹配结果interatorMatch[1]name, interatorMatch[2]index

image.png

处理name,获取name在表达式中的位置,通过createAliasExpression创建别名表达式对象。放到result中。

image.png

image.png

处理indexitem,和处理name一样,这里不再说明。 都是放入result中,最后把result返回回去。

image.png

回到processFor中,通过parseForExpression返回的result创建forNode。和ifNode一样,会替换AST树中的自身。之后去执行processCodegen

image.png

函数开始,拿到和创建一系列东西,为后面的流程和退出函数做准备。

image.png

创建forNodecodegen,返回退出函数,在processFor中也返回一退出函数,在后面合适的时机执行。

image.png

这里我们直接跳到执行退出函数的时候,processFor的退出函数中会scopes减一,表示一个vFor处理完成。去执行prcessCodegen的退出函数。

image.png

image.png

childrenBlock是后面用来保存codegen的,slotOutlet是用来存储找到的插槽出口,没有就是nullneedFragmentWappervIf中的一样。

image.png

处理的流程主要分为两大类,插槽出口和普通元素,如果是插槽出口,需要判断v-for是不是在<slot />上,如果在<slot />上,只需要注入key即可。如果是<template v-for><slot /></template,那么就会是有多个文本或者元素,需要为每一个children产生一个Fragment

image.png

如果是普通元素,那就可以直接使用childrencodegen,但是需要把它们标记为块。

image.png

最后因为v-for是使用renderList渲染,需要处理一下参数,但可能会带有v-memo指令,也需要做额外的处理。如果带有v-memo指令,则需要加入一些复合表达式对象。比如(renderList)函数参数、缓存校验等。

image.png

没有带有v-memo指令,那就只需要加入renderList形参对象即可。v-for分析完毕。

分析v-memo

image.png

既然前面提到了v-memo,那就接着去看v-memo的语义分析。v-memo的是靠transformMemo,处理非常简单,验证指令存在且缓存中不存在当前节点,就会添加缓存,剩下的处理在退出函数中。

image.png

退出函数主要做的有两个工作,一是把非组件的子树变为block,而后就是在codegen外包一层函数withMemov-memo处理完成。

分析expression

表达式一共分为两种,一种是插值表达式:{{exp}},还有一种就是元素上的指令表达式,比如:v-bind:arg="arg",这些都交给transformExpression

image.png

首先是插值,它会去执行processExpression,其实这个函数也没啥好看的,那么一大段代码,居然都是非浏览器环境下执行,在浏览器环境下执行只有一点一点。

image.png

进行简单的表达式验证,并且返回。

image.png

处理指令表达式时,不要处理v-onv-for,它们会经过特殊处理,v-on包装成内联语句。而v-for会变成renderList渲染。也是去执行processExpression进行处理。如果arg存在并且不是静态的也需要处理。表达式处理完成。

结尾

本篇文章就先说v-forv-ifv-memo以及expression的语义分析,其他的在后面再说。敬请期待。