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

Vue.js设计与实现学习总结(第三章)组件渲染原理虚拟DOM

时间:2023-03-27 10:13:16 JavaScript

Renderer虚拟DOM:使用Javascript对象来描述真实的DOM结构。例如:constvNode={//标签名称tag:'div',//标签属性prop:{onClick(){alert('click')}},//子节点children:[{tag:'span',children:'helloworld'}]}使用渲染器将虚拟DOM变为真实DOM并渲染到浏览器页面,只查找和更新变化的内容,如图1所示:因此,它是需要写一个渲染器将上面的虚拟DOM渲染成真实的DOM:/遍历vNode.props给DOM对象添加属性和事件for(constkeyinvNode.props){if(/^on/.test(key)){//如果key在header上,说明是一个事件,注册对应的事件el.addEventListener(key.substr(2).toLocaleLowerCase(),vNode.props[key])}}//处理childrenif(typeofvNode.children=='string'){//for字符串表示它是e的文本子节点lementlettext=document.createTextNode(vNode.children)el.appendChild(text)}elseif(Array.isArray(vNode.children)){//递归调用Render函数渲染子节点,使用当前元素el作为挂载点for(letitemofvNode.children){Render(item,el)}}//向挂载点添加元素root.appendChild(el)}渲染器的Render实现思路一般是三步:创建元素:把vNode.tag作为标签名,创建一个DOM元素,并为元素添加属性和事件:遍历vNode.props对象,如果key以on字符开头,则表示是一个事件,截取该字符on然后调用addEventListener绑定事件处理器来处理children:如果children是一个数组,则递归调用Render继续渲染。注意新创建的元素要作为挂载点(父节点);如果children是一个字符串,则使用createTextNode函数创建一个文本节点并设置它将组件添加到新创建的元素中组件本质上是一组DOM元素的封装。你可以定义一个函数来表示一个组件(或者使用一个对象。给对象添加一个函数来返回一个虚拟DOM的值)。函数的返回值是组件要渲染的内容constMyComponent=function(){return{tag:'div',props:{onClick:()=>alert('component')},children:'clickme'}}这样就可以使用虚拟DOM描述组件了:constvNode={tag:MyComponent}所以在renderer渲染的时候需要判断tag的类型:functionRender(vNode,container){if(typeofvNode.tag==='string'){//解释vNode描述的是tag元素mountElement(vNode,container)}elseif(typeofvNode.tag==='function'){//解释vNode描述的是组件mountComponent(vNode,container)}}如果是字符串,可以使用mountElement函数渲染,内容同上Render函数functionmountElement(vNode,root){//使用vNode.tag作为tagname创建DOM对象constel=document.createElement(vNode.tag)//遍历vNode.props为DOM对象添加属性和事件for(constkeyinvNode.props){if(/^on/.test(key)){//如果key使用oncardheader表示是事件,注册对应的事件el.addEventListener(key.substr(2).toLocaleLowerCase(),vNode.props[key])}}//processchildrenif(typeofvNode.children=='string'){//表示它是元素的文本子节点的字符串lettext=document.createTextNode(vNode.children)el.appendChild(text)}elseif(Array.isArray(vNode.children)){//递归调用Render函数渲染子节点,使用当前元素el作为挂载点for(letitemofvNode.children){//递归时,它还是需要判断tag的类型,所以调用RenderRender(item,el)}}//将元素添加到挂载点root.appendChild(el)}如果是函数,使用mountComponent函数渲染:functionmountComponent(vNode,container){//调用组件函数获取组件要渲染的内容(虚Quasi-DOM)constsubtree=vNode.tag()//递归调用Render渲染subtrssRender(subtree,container)}