当前位置: 首页 > 科技观察

Vue.js设计与实现12-渲染器核心功能:挂载和更新01

时间:2023-03-12 00:34:01 科技观察

1.写在前面这篇文章主要介绍如何实现将虚拟DOM节点转换为真实的DOM树,并最终挂载到挂载点上。它讨论了如何将虚拟节点安装到DOM树和从中卸载,以及如何在元素上设置属性。2、挂载子节点和元素的属性上一篇文章中,当vnode.children的值为字符串时,将其设置为元素的文本内容。当vnode.children的值为数组时,表示它有子节点。通过设置。我们知道vnode是一个虚拟DOM节点,vnode.children是一棵虚拟DOM树,其中的每一个元素都是一个虚拟DOM节点。constvnode={type:"div",//props是标签的属性props:{id:"super"},children:[{type:"p",children:"pingping"}]}以上代码是虚拟DOM树状结构,如果要将虚拟VNode转换成真实DOM,需要挂载和渲染。节点渲染可以通过mountElement函数实现:functionmountElement(vnode,container){constel=createElement(vnode.type);//处理childrenif(typeofvnode.children==="string"){//字符串转换setElementText(el,vnode.children)}elseif(Array.isArray(vnode.children)){//虚拟DOM节点数组需要遍历每个节点挂载vnode.children.forEach(child=>{patch(null,child,el);})}//给标签添加属性if(vnode.props){for(constkeyinvnode.props){//调用setAttribute在元素上设置属性el.setAttribute(key,vnode.props[key]);//你也可以使用DOM对象直接设置属性//el[key]=vnode.props[key];}}插入(el,容器);上面的代码片段中,首先会根据vnode.type的值创建一个DOM节点。当children的值判断为字符串类型时,直接设置为元素的文本内容;当判断children的值为数组时,遍历数组中的虚拟DOM节点,调用patch函数挂载该节点。在mount阶段,patch没有oldvnode,为此传递的第一个参数为null,执行patch函数时,会递归调用mountElement函数完成挂载。patch传递的第三个参数是虚拟节点要挂载的根节点。挂载完成后,需要设置属性进行元素遍历。3、正确设置元素属性如何正确设置元素属性,首先要了解HTMLAttribute和DOMProperties的区别和联系。我们知道HTMLAttribute是定义在一个HTML标签元素上的属性,浏览器会解析它创建一个对应的DOM对象,其中包含很多属性(DOMProperties)。HTMLAttribute的作用是设置相应的DOMProperties的初始值。当值发生变化时,DOMProperties始终存储当前值,因此通过getAttribute获取的初始值也是初始值。HTML属性和DOM属性。但是使用Vue.js的单文件模板不会被浏览器解析,需要框架自己解析。它会影响添加DOM属性的方式。浏览器会解析普通HTML元素的代码,并分析HTMLAttributes来设置合适的DOMProperties。但是使用Vue.js的单文件模板不会被浏览器解析,需要框架自己解析。那么,我们看看在Vue.js中是如何实现的:constrenderer=createRenderer({//创建元素createElement(tag){returndocument.createElement(tag);},//设置元素的文本节点setElementText(el,text){el.textContent=text;},//用于将指定元素添加到指定父节点insert(el,parent,achor=null){parent.insertBefore(el,anchor);}//执行操作与属性设置封装相关,传值作为渲染器选项class"){//使用el.className设置clas是因为其性能高于setAttribute和el.classListel.className=nextValue||"";}elseif(type==="boolean"&&nextValue===""){el[key]=true;}else{el[key]=nextValue;}}else{el.setAttribute(key,nextValue);}}})在上面的代码中,shouldSetAsProps函数用于分析属性是否应该设置为DOMProperties属性,以布尔值形式返回。当返回真值时,意味着应该将其设置为DOM属性,否则应该使用setAttribute函数设置该属性。设置属性时,需要先设置元素的DOMProperties,当其值为空字符串时,需要修正为true。在mountElement函数中,只需要调用patchProps函数传递参数:functionmountElement(vnode,container){constel=createElement(vnode.type);//处理childrenif(typeofvnode.children==="string"){//将字符串转为标签的文本内容setElementText(el,vnode.children)}elseif(Array.isArray(vnode.children)){//虚拟DOM节点数组需要遍历每个节点挂载vnode.children.forEach(child=>{patch(null,child,el);})}//给标签添加属性if(vnode.props){for(constkeyinvnode.props){patchProps(el,key,null,vnode.props[key])}}insert(el,container);}在上面的代码片段中,mountElement函数将检查每个vnode.props中的属性,并调用patchProps函数来设置DOM属性。4.卸载操作在前面两节中,我们讨论了如何将虚拟DOM挂载到挂载点上,这是通过createRenderer函数结合mountElement实现的。卸载操作发生在更新阶段,即初始挂载完成后,后续渲染触发更新。//第一次挂载renderer.render(vnode,document.querySelector("#app"));//再次挂载新的vnode触发更新。传null时,执行卸载前的操作在初始渲染之后,如果在后续渲染时将null作为新的vnode传递,则意味着需要卸载所有当前渲染的内容。在上一篇文章中,使用innerHTML设置为空作为清空容器元素内容的解决方案是有缺陷的,因为它不会移除绑定到DOM元素的事件处理程序。对此,需要根据vnode对象获取与其关联的真实DOM元素,并使用原生DOM操作方法将其移除。函数卸载(vnode){constparent=vnode.el.parentNode;如果(父母){parent.removeChild(vnode.el);}}unmount函数接收一个虚拟节点作为参数,将该节点对应的真实DOM元素从Removed父元素中移除。注意:当新旧vnode的描述内容不同时,即vnode的属性。重置为空,最后将新元素挂载到容器中。当然,即使新旧vnode的描述内容相同,也要判断两者类型是否相同。一个vnode可以描述普通的标签或组件。不同类型的vnode需要使用不同的安装或修补方法。functionpatch(n1,n2,container){if(n1&&n2.type!==n1.type){卸载(n1);n1=空;}const{类型}=n2;if(typeoftype==="string"){if(!n1){mountElement(n2,container);}else{patchElement(n1,n2);}}elseif(typeoftype==="object"){//n2.type为object类型,描述组件}elseif(type==="xxx"){//处理其他类型的vnode}}5.最后写挂载子节点,只需要递归调用patch函数即可实现挂载,并且节点属性的设置取决于所设置属性的特性。unmount操作时,直接使用innerHTML清空容器元素存在很多问题。为此,封装了一个新的卸载函数unmount。