Products
GG网络技术分享 2025-03-18 16:08 0
先简单复习一下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