当前位置: 首页 > Web前端 > HTML

petite-vue-源码分析-v-for重新渲染工作原理

时间:2023-03-28 16:58:45 HTML

在《petite-vue源码剖析-v-if和v-for的工作原理》中我们了解了v-for在静态视图中的工作原理,这里我们将详细了解v-for在更新渲染功能时的工作原理.逐行解析//file./src/directives/for.ts/*[\s\S]*表示识别几个空格字符和非空格字符,默认是贪心模式,即`(item,index)invalue`将匹配整个字符串。*改为[\s\S]*?是惰性模式,即`(item,index)invalue`只会匹配`(item,index)`*/constforAliasRE=/([\s\S]*?)\s+(?:in)\s+([\s\S]*?)///用于移除`(item,index)`中的`(`和`)`conststripParentRE=/^\(|\)$/g//匹配`item,index`中的`,index`,然后可以提取value和index独立处理constforIteratorRE=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/typeKeyToIndexMap=Map//为了便于理解,我们假设只接受`v-for="valinvalues"`形式,所有输入参数都是有效的,删除了入参有效性和解构等代码exportconst_for=(el:Element,exp:string,ctx:Context)=>{//通过正则表达式提取`in`两边的子表达式字符串在表达式字符串中constinMatch=exp.match(forAliasRE)//保存下一轮遍历分析的模板节点constnextNode=el.nextSibling//插入锚点,移除element与来自DOM树的`v-for`constparent=el.parentElement!constanchor=newText('')parent.insertBefore(anchor,el)parent.removeChild(el)constsourceExp=inMatch[2].trim()//获取`value`in`(item,index)invalue`letvalueExp=inMatch[1].trim().replace(stripParentRE,'').trim()//Get`(item,index)invalue`in`item,index`letindexExp:字符串|未定义letkeyAttr='key'letkeyExp=el.getAttribute(keyAttr)||el.getAttribute(keyAttr=':key')||el.getAttribute(keyAttr='v-bind:key')if(keyExp){el.removeAttribute(keyExp)//将表达式序列化,比如`value`变成`"value"`,这样就不会参与了随后的表达式计算()//获取`item,index`中的itemindexExp=match[1].trim()//获取`item,index`中的index}letmounted=false//false表示第一次渲染,true表示重新渲染-renderingletblocks:Block[]letchildCtxs:Context[]letkeyToIndexMap:KeyToIndexMap//用来记录key和index的关系,当重新渲染发生时,元素会被重用constcreateChildContexts=(source:unknown):[Context[],KeyToIndexMap]=>{constmap:KeyToIndexMap=newMap()constctxs:Context[]=[]if(isArray(source)){for(leti=0;i{constdata:any={}data[valueExp]=valueindexExp&&(data[indexExp]=index)//为每个子项创建一个单独的项elementScopeconstchildCtx=createScopedContext(ctx,data)//key表达式在对应的子元素范围内运行constkey=keyExp?evaluate(childCtx.scope,keyExp):索引map.set(key,index)childCtx.key=keyreturnchildCtx}//为每个子元素创建一个块对象constmountBlock=(ctx:Conext,ref:Node)=>{constblock=newBlock(el,ctx)block.key=ctx.keyblock.insert(parent,ref)returnblock}ctx.effect(()=>{constsource=evaluate(ctx.scope,sourceExp)//计算`(item,index)initems`中items的真实值constprevKeyToIndexMap=keyToIndexMap//生成一个新的作用域并计算`key`、`:key`或`v-bind:key`;[childCtxs,keyToIndexMap]=createChildContexts(source)if(!mounted){//为每个子元素创建块对象,解析子元素的后代插入DOM树blocks=childCtxs.map(s=>mountBlock(s,anchor))mounted=true}else{//更新渲染逻辑!!//删除根据key更新后不存在的元素for(leti=0;i

`}})//ReactfunctionApp(){constitems=[...]return(items.map(item=>{if(item.type==='span'){return()}else{return(
)}}))}由于petite-vue对重复键进行了优化,React会对重复键petite进行相同的判断和操作-vue是即时移动元素,而React是计算后移动元素,对于旧视图到123新视图到312,petite-vue会移动元素3次,而React只会移动元素2次。DOM节点的增删改操作我们已经了解了,关于事件绑定、属性和v-modal的说明,后面再看源码吧!《Petite-Vue源码剖析》小册子《Petite-Vue源码剖析》结合实例从在线渲染、响应式系统、沙箱模型逐行解读源码,并详细分析了响应式中使用JS引擎的SMI优化依赖清理算法系统。绝对是Vue3源码入门前的绝佳敲门砖。喜欢的话记得转发和欣赏哦!