当学习成为习惯,知识就成为常识。感谢您的关注、点赞、收藏和评论。有新视频和文章会第一时间发到微信公众号。欢迎关注:李永宁lyn的文章已收录到github仓库liyongning/blog。欢迎收看星空。前言上一篇文章Vue源码解读(八)——编译器浅析详细讲解了编译器的第一部分,如何将html模板字符串编译成AST。今天带来编译器的第二部分,优化AST,也就是通常所说的静态标记。目标深入理解编译器的静态标记过程源码解读入口/src/compiler/index.js/***在此之前所做的所有事情只有一个目的,就是构建平台特定的编译选项(options),如web平台**1.将html模板解析成ast*2.静态标记ast树*3.将ast生成渲染函数*并将静态渲染函数放在code.staticRenderFns数组*代码中。render是一个动态渲染函数*在以后渲染的时候,执行渲染函数得到vnode*/exportconstcreateCompiler=createCompilerCreator(functionbaseCompile(template:string,options:CompilerOptions):CompiledResult{//将模板解析成AST,并在每个节点的ast对象上设置该元素的值的所有信息,如标签信息、属性信息、槽信息、父节点、子节点等。//具体属性见start和end,处理开始和结束标签的两种方法constast=parse(template.trim(),options)//优化,遍历AST,为每个节点做静态标记//标记每个节点是否为静态节点,然后进一步标记静态根节点//以便在后续的更新过程中可以跳过这些静态节点//标记静态根,用于生成渲染函数阶段,生成一个静态根节点If(options.optimize!==false){optimize(ast,options)}//从AST生成渲染函数,生成代码如下,例如:code.render="_c('div',{attrs:{"id":"app"}},_l((arr),function(item){return_c('div',{key:item},[_v(_s(item))])}),0)"constcode=generate(ast,options)return{ast,render:code.render,staticRenderFns:code.staticRenderFns}})optimize/src/compiler/optimizer.js/***优化:*遍历AST,标记每个节点是静态节点还是动态节点,然后标记静态根节点*,这样在后续的更新过程中就不需要关注这些节点了*/exportfunctionoptimize(root:?ASTElement,options:CompilerOptions){if(!root)return/***options.staticKeys='staticClass,staticStyle'*isStaticKey=function(val){returnmap[val]}*/isStaticKey=genStaticKeysCached(options.staticKeys||'')//平台保留标签isPlatformReservedTag=options.isReservedTag||no//遍历所有节点,为每个节点设置静态属性,识别是否为静态节点markStatic(root)//进一步标记静态根,一个节点需要是静态根节点,如下条件://的节点本身是静态节点,有子节点,而子节点不只是文本节点,它被标记为静态根//静态根节点不能只有静态文本子节点,因为回报太低。一直更新就好markStaticRoots(root,false)}markStatic/src/compiler/optimizer.js/***在所有节点上设置static属性,识别是否是静态节点*注意:如果有子节点的话是动态节点,父节点也算动态节点*@param{*}node*@returns*/functionmarkStatic(node:ASTNode){//使用node.static来判断节点是否是静态的nodenode.static=isStatic(node)if(node.type===1){/***不要将组件的槽内容设置为静态节点,这样可以避免:*1.组件无法改变槽节点*2.静态插槽内容在热重载时失败*/if(!isPlatformReservedTag(node.tag)&&node.tag!=='slot'&&node.attrsMap['inline-template']==null){//递归终止条件,如果该节点既不是平台保留标签&&也不是slot标签&&也不是内联模板,则end直接return}//遍历子节点,递归调用markStatic标记静态属性这些子节点为(leti=0,l=node.children.length;我<我;i++){constchild=node.children[i]markStatic(child)//如果子节点是非静态节点,更新父节点为非静态节点if(!child.static){node.static=false}}//如果节点有v-if,v-else-if,v-else等指令,依次标记block中节点的staticif(node.ifConditions){for(leti=1,l=node.ifConditions.length;i
