当前位置: 首页 > Web前端 > vue.js

说说v-if的实现

时间:2023-03-31 23:20:22 vue.js

先说说v-if我们举个栗子

v-if块的内容

v-else-if块的内容

v-else块的内容

最终呈现在页面上DOM元素只会是三个p元素之一。所以为什么?Vue进行如下转换模板--->ast--->render函数,最后根据生成的render函数生成对应的DOM,这里不再展开。Vue在生成ast和render函数时,会解析v-if等指令。生成AST的部分是将template模板转换成对应的ast语法树,这部分源码在compiler/parser/index.js下。/*匹配if属性,分别处理v-if、v-else和v-else-if属性*/functionprocessIf(el){/*取出v-if属性*/constexp=getAndRemoveAttr(el,'v-if')if(exp){/*有一个v-if属性*/el.if=exp/*在el的ifConditions属性中加入{exp,block}*/addIfCondition(el,{exp:exp,block:el})}else{/*没有v-if属性*/if(getAndRemoveAttr(el,'v-else')!=null){el.else=true}constelseif=getAndRemoveAttr(el,'v-else-if')if(elseif){el.elseif=elseif}}}/*向el的ifConditions属性添加条件*/functionaddIfCondition(el,condition){if(!el.ifConditions){el.ifConditions=[]}el.ifConditions.push(condition)}这段代码做的是:如果元素中有v-if,取出v-if的condition加入el.ifConditions,如果有v-else,v-else-if属性会将相应的标志位置设置为true。因为我们在el.ifConditions下只挂了v-if的条件,而v-else和v-else-if还没有处理,所以接下来需要添加。if(currentParent&&!element.forbidden){if(element.elseif||element.else){/*当前ele有v-else或v-elseif属性时,需要处理if属性。元素中必须有v-if属性*/processIfConditions(element,currentParent)}}/*处理if条件*/functionprocessIfConditions(el,parent){//查找当前元素的前一个兄弟元素constprev=findPrevElement(parent.children)if(prev&&prev.if){addIfCondition(prev,{exp:el.elseif,block:el})}elseif(process.env.NODE_ENV!=='production'){警告(`v-${el.elseif?('else-if="'+el.elseif+'"'):'else'}`+`用于元素<${el.tag}>没有相应的v-if.`)}}当当前ele有v-else或v-elseif属性时,需要处理if属性。它的上级兄弟元素中必须有一个v-if属性,然后将当前元素的else-if上的表达式添加到前一个元素的ifConditions上。转换后的ast树{type:1,tag:'div',plain:false,children:[{type:1,tag:'p',children:[{text:'contentofv-ifblock',type:3}]if:'value==1',ifConditions:[{exp:"value==1",block:{type:1,tag:'p',children:[{text:'v-ifblockcontent',type:3}],if:'value==1',ifConditions:[],plain:true}},{exp:"value==2",块:{type:1,tag:'p',children:[{text:'contentofv-else-ifblock',type:3}],elseif:'value==2',plain:true}},{exp:undefined,block:{type:1,tag:'p',children:[{text:'contentofthev-elseblock',type:3}],else:true,plain:true}}]}]}ast树中的转换已经完成完成了,接下来就是在刚才的ast树上挂载相应的属性了。生成相应的渲染函数。生成渲染函数的源代码在compiler/codegen/index.js下。functiongenElement(el:ASTElement):string{*******************************elseif(el.for&&!el.forProcessed){/*processingv-for*/returngenFor(el)}elseif(el.if&&!el.ifProcessed){/*processingv-if*/returngenIf(el)}***************************}其中genIf函数为v-ifqizhon的处理函数genIf(el:any):string{/*markbit,避免递归处理*/el.ifProcessed=truereturngenIfConditions(el.ifConditions.slice())}/*处理if条件*/functiongenIfConditions(conditions:ASTIfConditions):string{if(!conditions.length){return'_e()'}constcondition=conditions.shift()if(condition.exp){return`(${condition.exp})?${genTernaryExp(condition.block)}:${genIfConditions(conditions)}`}else{return`${genTernaryExp(condition.block)}`}//v-ifwithv-once应该生成类似(a)?_m(0):_m(1)/*v-ifandv-的代码once同时存在应使用三元运算符,例如(a)?_m(0):_m(1)*/functiongenTernaryExp(el){返回el.once?genOnce(el):genElement(el)}}genIfConditions函数递归处理el上ifConditions收集的if,else,else-if属性,最后生成render函数with(this){return_c('div',{attrs:{"id":"app"}},[(value==1)?_c('p',[_v("v-if块的内容")]):(value==2)?_c('p',[_v("v-else-if块的内容")]):_c('p',[_v("v-else块的内容")])])}最后,在执行render函数时,它vue中会根据变量绑定的值来决定创建模板的哪一部分