前言
经过前面的编译成AST、AST树优化,我们得到了一个便于产生渲染函数的AST树。渲染函数在生成的过程中其实是字符串,会根据AST树的情况拼接进不同的语句,在最末尾才会把它变成一个函数
在正式开始了解如果产生渲染函数之前,我们需要了解几个东西,首先是助手函数,在AST树优化阶段我们见到了很多助手函数,这些助手函数在某些特定时刻进行注入,比如toDisplayStrng,它是在解析到插值或者变量的情况下进行注入,它的工作是帮助渲染函数将插值或者变量解析后变成字符串后交给渲染函数,后面我们还会遇到很多助手函数。
第二个是渲染函数的模式,在前面两篇文章中提到过,渲染函数存在两种模式,分别是module和function,而在浏览器平台只会是function模式,简单说明一下两者的区别,moudle模式是使用ES6 module进行导入,function模式是使用解构的方式导入,且在function模式下会使用with块,两个模式都会是一个函数,这个函数就是渲染函数。
最后一个就是渲染函数的参数,渲染函数一共有五个实际参数:_ctx, _cache, $props, $setup, $data, $options,分别是上下文、缓存对象、props对象、setup数据对象(setup函数返回的对象)、data数据对象(data函数返回的对象)、Vue实例对象。
语句静态提升
调用generate方法开始生成渲染函数,第一个流程是genFunctionPremble进行静态提升,
(位置在
compiler-core/src/codegen.ts中generate方法)
在dom结构中会有一些节点是动态的,在渲染函数中则会将他们单独提出来,避免在后面的更新重复声明,并且这是一个“纯净”的会注入一个标志/*#__PURE__*/,这表示如果没有用到这个静态节点,可以进行tree-shaking。
首先会把Vue包放到单独的一个放到单独的一个变量中,如果出现静态节点,那么就会引入一些操作静态节点方法,并且为了不会方法名起冲突,会给这些方法名起别名(aliasHelper方法),拼接除了的字符串如下:
const _Vue = Vue
const {xxx:_xxx} = _Vue
往下走,会进入genHoists函数,这里是对静态节点进行生成声明代码,他会循环静态节点列表hoists生成代码,会先生成头,也即是const hoisted_{i + 1} = (这里i是个变量,每生成一个就加一)。
中间的代码会进入genNode方法中生成,当进入的静态节点是字符串可以直接拼接,或者是vue定义的唯一类型(例如:Symbol('Text'))则需要用到助手函数创建,但是里面主要还是调用不同的方法对不同的静态节点进行生成代码,这里就简单说明几个,其他的可以自行到代码中查看。
比如插值的代码是交给genInterpolation进行生成的,它的工作是将插值用toDisplayString助手函数包起来,并且在包关闭括号之前,回去生成插值变量的代码吗,生成的代码如下:
_toDisplayString('value')
再比如复合表达式代码生成是交给genCompoundExpression,但是复合表达式包含了表达式和字符串,(例如:{{value}}是多少),这里要分开解析,字符串直接拼接,表达式需要再次进入genNode解析。生成的代码如下:
const _hoistsed_x = _toDisplayString(value) + '是多少'
生成函数主体
做完静态提升,便开始生成函数主体,首先是函数的开头的:函数的声明和形参的设置,这里是要区分SSR和CSR以及平台,而在CSR浏览器平台下,形参只会设置_ctx和_cache,生成的代码如下:
function render(_ctx, _catch) {
引入在前面分析结构时用到的助手函数,在函数模式下常量应该声明在with块中,并且应该重命名避免和用户变量起冲突。
确认并引入资产,这里只会去找在结构中用到custom directives、component以及filter(开启了v2兼容),这部分的任务交给了genAssets。genAssets内部除了会根据资产使用对应的助手函数生成对应的代码,还会根据SFC文件名推断隐式组件自引用。
这里需要注意的一点,temps是在SSR才会有,CSR始终都是零,temps是用来生成临时变量。
接着开始生成VNode表达式,也就是将codegenNode交给genNode,一样是根据节点类型进行循环调用genNode生成代码,这里不过多做解释。
最后进行收尾,将前面打开的大括号闭合回去,最后将生成完的代码字符串返回回去即可,当然,现在的渲染函数还只是一堆字符串,如果加小括号执行会报错,现在需要用new Function将字符串转换为函数后执行,返回的东西就是渲染函数(运行时已编译),渲染函数就这样生成了,但这只是客户端渲染的渲染函数,服务端渲染我们以后在分析。
总结
整个生成渲染的流程其实非常简单的,因为前面的transform就已经把所有的生成代码需要的东西都准备好了,所以在生成渲染函数阶段只需要根据transform之后的AST树生成即可,以上仅为个人的分析,还是希望各位哥哥姐姐能指导指导。有说错或者遗漏的欢迎在评论区讲解,谢谢。