这篇“vue3编译优化的内容有哪些”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“vue3编译优化的内容有哪些”文章吧。 vue3编译优化有:1、引入了 patchFlag,用来标记动态内容;在编译过程中会根据不同的属性类型打上不同的标识,从而实现了快速diff算法。2、Block Tree。3、静态提升,是将静态的节点或者属性提升出去。4、预解析字符串化,当连续静态节点超过10个时,会将静态节点序列化为字符串。5、函数缓存;开启cacheHandlers选项后,函数会被缓存起来,后续可直接使用。本文主要来分析 Vue3.0
编译阶段做的优化,在 patch
阶段是如何利用这些优化策略来减少比对次数。
由于组件更新时依然需要遍历该组件的整个 vnode
树,比如下面这个模板:
statictext
statictext
{{message}}
statictext
statictext
statictextstatictext{{message}}statictextstatictext整个 diff 过程如图所示:可以看到,因为这段代码中只有一个动态节点,所以这里有很多 diff 和遍历其实都是不需要的,这就会导致 vnode 的性能跟模版大小正相关,跟动态节点的数量无关,当一些组件的整个模版内只有少量动态节点时,这些遍历都是性能的浪费。对于上述例子,理想状态只需要 diff 这个绑定 message 动态节点的 p 标签即可。Vue.js 3.0
通过编译阶段对静态模板的分析,编译生成了 Block tree
。Block tree
是一个将模板基于动态节点指令切割的嵌套区块,每个区块内部的节点结构是固定的,而且每个区块只需要以一个 Array
来追踪自身包含的动态节点。借助 Block tree
,Vue.js 将 vnode 更新性能由与模版整体大小相关提升为与动态内容的数量相关,这是一个非常大的性能突破。由于 diff
算法无法避免新旧虚拟 DOM
中无用的比较操作,Vue.js 3.0
引入了 patchFlag
,用来标记动态内容。在编译过程中会根据不同的属性类型打上不同的标识,从而实现了快速 diff
算法。PatchFlags
的所有枚举类型如下所示:
exportconstenumPatchFlags{ TEXT=1,//动态文本节点 CLASS=1
左侧的 template
经过编译后会生成右侧的 render
函数,里面有 _openBlock
、_createElementBlock
、_toDisplayString
、_createElementVNode
(createVnode
) 等辅助函数。
letcurrentBlock=null function_openBlock(){ currentBlock=[]//用一个数组来收集多个动态节点 } function_createElementBlock(type,props,children,patchFlag){ returnsetupBlock(createVnode(type,props,children,patchFlag)); } exportfunctioncreateVnode(type,props,children=null,patchFlag=0){ constvnode={ type, props, children, el:null,//虚拟节点上对应的真实节点,后续diff算法 key:props?.["key"], __v_isVnode:true, shapeFlag, patchFlag }; ... if(currentBlock&&vnode.patchFlag>0){ currentBlock.push(vnode); } returnvnode; } functionsetupBlock(vnode){ vnode.dynamicChildren=currentBlock; currentBlock=null; returnvnode; } function_toDisplayString(val){ returnisString(val) ?val :val==null ?"" :isObject(val) ?JSON.stringify(val) :String(val); }
此时生成的 vnode 如下:此时生成的虚拟节点多出一个 dynamicChildren
属性,里面收集了动态节点 span
。我们之前分析过,在 patch
阶段更新节点元素的时候,会执行 patchElement
函数,我们再来回顾一下它的实现:
constpatchElement=(n1,n2)=>{//先复用节点、在比较属性、在比较儿子 letel=n2.el=n1.el; letoldProps=n1.props||{};//对象 letnewProps=n2.props||{};//对象 patchProps(oldProps,newProps,el); if(n2.dynamicChildren){//只比较动态元素 patchBlockChildren(n1,n2); }else{ patchChildren(n1,n2,el);//全量diff } }
我们在前面组件更新的章节分析过这个流程,在分析子节点更新的部分,当时并没有考虑到优化的场景,所以只分析了全量比对更新的场景。而实际上,如果这个 vnode
是一个 Block vnode
,那么我们不用去通过 patchChildren
全量比对,只需要通过 patchBlockChildren
去比对并更新 Block
中的动态子节点即可。
由此可以看出性能被大幅度提升,从 tree
级别的比对,变成了线性结构比对。我们来看一下它的实现:
constpatchBlockChildren=(n1,n2)=>{ for(leti=0;i
接下来我们看一下属性比对的优化策略:
constpatchElement=(n1,n2)=>{//先复用节点、在比较属性、在比较儿子 letel=n2.el=n1.el; letoldProps=n1.props||{};//对象 letnewProps=n2.props||{};//对象 let{patchFlag,dynamicChildren}=n2 if(patchFlag>0){ if(patchFlag&PatchFlags.FULL_PROPS){//对所props都进行比较更新 patchProps(el,n2,oldProps,newProps,...) }else{ //存在动态class属性时 if(patchFlag&PatchFlags.CLASS){ if(oldProps.免费云主机、域名class!==newProps.class){ hostPatchProp(el,'class',null,newProps.class,...) } } //存在动态style属性时 if(patchFlag&PatchFlags.STYLE){ hostPatchProp(el,'style',oldProps.style,newProps.style,...) } //针对除了style、class的props if(patchFlag&PatchFlags.PROPS){ constpropsToUpdate=n2.dynamicProps! for(leti=0;i
总结: vue3
会充分利用 patchFlag
和 dynamicChildren
做优化。如果确定只是某个局部的变动,比如 style
改变,那么只会调用 hostPatchProp
并传入对应的参数 style
做特定的更新(靶向更新);如果有 dynamicChildren
,会执行 patchBlockChildren
做对比更新,不会每次都对 props 和子节点进行全量的对比更新。图解如下:静态提升是将静态的节点或者属性提升出去,假设有以下模板:
hello {{name}} {{age}}
编译生成的 render
函数如下:
exportfunctionrender(_ctx,_cache,$props,$setup,$data,$options){ return(_openBlock(),_createElementBlock("div",null,[ _createElementVNode("span",null,"hello"), _createElementVNode("span",{ a:"1", b:"2" },_toDisplayString(_ctx.name),1/*TEXT*/), _createElementVNode("a",null,[ _createElementVNode("span",null,_toDisplayString(_ctx.age),1/*TEXT*/) ]) ])) }
我们把模板编译成 render
函数是这个酱紫的,那么问题就是每次调用 render
函数都要重新创建虚拟节点。开启静态提升 hoistStatic
选项后
const_hoisted_1=/*#__PURE__*/_createElementVNode("span",null,"hello",-1/*HOISTED*/) const_hoisted_2={ a:"1", b:"2" } exportfunctionrender(_ctx,_cache,$props,$setup,$data,$options){ return(_openBlock(),_createElementBlock("div",null,[ _hoisted_1, _createElementVNode("span",_hoisted_2,_toDisplayString(_ctx.name),1/*TEXT*/), _createElementVNode("a",null,[ _createElementVNode("span",null,_toDisplayString(_ctx.age),1/*TEXT*/) ]) ])) }
静态提升的节点都是静态的,我们可以将提升出来的节点字符串化。 当连续静态节点超过 10
个时,会将静态节点序列化为字符串。假如有如下模板:
static static static static static static static static static static
开启静态提升 hoistStatic
选项后
const_hoisted_1=/*#__PURE__*/_createStaticVNode("staticstaticstaticstaticstaticstaticstaticstaticstaticstatic",10) const_hoisted_11=[_hoisted_1] exportfunctionrender(_ctx,_cache,$props,$setup,$data,$options){ return(_openBlock(),_createElementBlock("div",null,_hoisted_11)) }
假如有如下模板:
v=event.target.value">
编译后:
const_hoisted_1=["onClick"] exportfunctionrender(_ctx,_cache,$props,$setup,$data,$options){ return(_openBlock(),_createElementBlock("div",{ onClick:event=>_ctx.v=event.target.value },null,8/*PROPS*/,_hoisted_1)) }
每次调用 render
的时候要创建新函数,开启函数缓存 cacheHandlers
选项后,函数会被缓存起来,后续可以直接使用
exportfunctionrender(_ctx,_cache,$props,$setup,$data,$options){ return(_openBlock(),_createElementBlock("div",{ onClick:_cache[0]||(_cache[0]=event=>_ctx.v=event.target.value) })) }
以上就是关于“vue3编译优化的内容有哪些”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注云技术行业资讯频道。
本文小编为大家详细介绍“jQuery中怎么添加自定义方法”,内容详细,步骤清晰,细节处理妥当,希望这篇“jQuery中怎么添加自定义方法”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。 一、什么是jQuery自定义方法jQuery自定…