Products
GG网络技术分享 2025-03-18 16:08 56
先简单复习一下Vue的编译过程,整个过程分为三个步骤:
第一步是parse解析阶段,前面的章节已经讲过parse方法的具体实现,该方法主要是将template编译成易于处理的抽象语法树(ast)。
第二步是optimize优化阶段,该阶段主要是将template中的静态节点在ast中标记出来,方便后续生成虚拟Dom的时候进行复用。
第三步是generate生成代码阶段,也是我们这章要学习的部分。该阶段是将ast树转换成能生成对应虚拟Dom的render函数。

这一章我们主要讨论generate阶段,看看抽象语法树是如何转换成render函数的。
我们先看下最终生成的代码代表的是什么意思:
with(this){return_c("div",[_v("编译")])}从生成的代码可以看出,render函数是以with开头的,我们先了解下with是如何使用的。
constobj={a:3}with(obj){console.log(a)// 3console.log(this.a)// undefinedconsole.log(b)// 报错}借用mdn上的一句话:with语句扩展一个语句的作用域链。本来访问a的时候是访问的全局作用域,加了with之后,访问a的作用域则是obj。with使用起来相对比较简单。回到Vue的render函数中,可以知道render函数里的作用域就是Vue的实例vm,所以render执行的时候,访问变量实质上就是访问Vue实例上的属性。至于为什么要使用with语法,可以看下尤大大的一篇回答。
还是那段编译生成的代码,在with内部的代码中有_c,_v这样的方法。由于this是指向Vue实例的,所以访问_c的时候其实访问的是vm._c。但是_c、_v这些方法是哪里定义的呢?
with(this){return_c("div",[_v("编译")])}这就要追溯到Vue构造函数与原型的章节了,在src/core/instance/render.js文件中找到renderMixin方法,这个方法是在我们学习Vue原型设计的时候提到的:
exportfunctionrenderMixin(Vue:Class<Component>){// install runtime convenience helpersinstallRenderHelpers(Vue.prototype)Vue.prototype.$nextTick=function(fn:Function){...}Vue.prototype._render=function():VNode{...}}在./render-helpers找到installRenderHelpers函数:
exportfunctioninstallRenderHelpers(target:any){target._o=markOncetarget._n=toNumbertarget._s=toStringtarget._l=renderListtarget._t=renderSlottarget._q=looseEqualtarget._i=looseIndexOftarget._m=renderStatictarget._f=resolveFiltertarget._k=checkKeyCodestarget._b=bindObjectPropstarget._v=createTextVNodetarget._e=createEmptyVNodetarget._u=resolveScopedSlotstarget._g=bindObjectListenerstarget._d=bindDynamicKeystarget._p=prependModifier}installRenderHelpers主要任务就是挂载了渲染需要用到的函数!所以调用_v就是调用createTextVNode方法,从字面意思我们就可以知道,其实它就是创建一个文本的Vnode节点。但是这里并没有定义_c,那么_c在哪呢?还记得数据初始化环节吗,同样是在src/core/instance/render.js文件中,不过是initRender方法:
exportfunctioninitRender(vm:Component){...vm._c=(a,b,c,d)=>createElement(vm,a,b,c,d,false)vm.$createElement=(a,b,c,d)=>createElement(vm,a,b,c,d,true)...}_c同样是创建Vnode节点的函数。所以render函数就是由一堆生成Vnode节点函数的集合。接下来我们看下具体是如何使用这些函数的。
generate过程的代码都是在src/compiler/codegen目录中,里面只有两个文件,相对来说比较少。在index.js中找到generate函数:
exportfunctiongenerate(ast:ASTElement|void,options:CompilerOptions):CodegenResult{conststate=newCodegenState(options)constcode=ast?(ast.tag==="script"?"null":genElement(ast,state)):"_c("div")"return{render:`with(this){return${code}}`,staticRenderFns:state.staticRenderFns}}里面的核心实现是调用了genElement方法:
if(el.staticRoot
Demand feedback