企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## 虚拟dom(virtual dom vdom) 1. vdom是vue和React的核心 2. vdom比较独立,使用也比较简单 ### 问题 1. vdom是什么,为什么存在vdom? 2. vdom如何应用,核心api是什么? 3. 介绍一些diff算法 #### 问题详细 * [ ] vdom是什么,为什么存在vdom? 1. 什么是vdom * virtual dom,虚拟dom * **用js模拟dom结构** * **dom变化的对比,放在js层来做**(图灵完备语言(判断,循环,递归,高复杂逻辑)(html,css都不是)) * 提高**重绘性能** * **dom操作非常昂贵** * **将dom对比放在js层,提高效率** js模拟dom ~~~ { tag:'ul', attrs:{ id :'list'}, children:[{ { tag:'li', attrs:{className:'item'}, children:['Item1'] } }], } ~~~ **浏览器最耗费性能的是dom操作,js操作一万遍没关系,dom操作一遍都不行,现在浏览器执行js是非常快的,所有vdom非常有价值** 2. 设计一个需求场景 将数据展示成一个表格,随便修改一个信息,表格跟着变化 3. 用jQuery来实现 ~~~ //jquery比较高效的实现 function render(data){ var $container = $('#container'); $container.html(''); //拼接table var $table = $("<table>") $table.append($("<tr><td>...")); data.forEach(function(item){ $table.append($('<tr><td>'+item.name+...)) }) //渲染到页面(执行一次渲染,属于比较高效的) $container.append($table); } ~~~ dom操作是最昂贵的 ~~~ var div = document.createElement('div'); var item ,result = ''; for(item in div){ result +="|" +item; } console.log(result) ~~~ 能看到**dom节点属性数量非常非常多**,是一个**非常复杂的属性节点**,侧面反映出dom的复杂性。js的模拟会更加简单一些,**应尽量少的做dom操作,尽量用js来代替**。 4. 遇到的问题 * **dom操作是“昂贵的”,js运行效率高**(js都能用到后端做server语言执行) * **尽量减少dom操作,不是“推倒重来”** * **项目越复杂,影响越严重** * vdom可解决这个问题 * [ ] vdom如何应用,核心api是什么 1. 介绍**snabbdom** (开源vdom的库),vdom是一类技术实现,能实现vdom的库很多 ~~~ var container = document.getElementById("container"); var vnode = h('div#container.two.classes', {on:{click:someFn}}, [h('span',{style:{fontWeight:'bold'}},'this is bold'), 'and this is just noraml text', h('a',{props:{href:'/foo'}},'I talk you place') } ); //第一个参数是真实的dom节点 patch(container,vnode); var newVnode = h('div#container.two.classes', {on:{click:anotherEventHandler}}, [h('span',{style:{fontWeight:'normal',fontStyle:'italic'}},'this is now italic type'), 'and this is still just noraml text', h('a',{props:{href:'/bar'}},'I talk you place') } ); patch(vnode,newVnode); ~~~ **h()函数生成vnode节点,模拟真实的dom节点(vnode)** vnode:js模拟的node(dom节点),单个的节点 vdom:js模拟的dom **patch(container,vnode)**://第一个参数是真实的dom节点 **在第一次渲染时,将全部的数据源全部渲染出来**(把vnode表述的dom节点全部塞给container) **patch(vnode,newVnode);**//第二次渲染 新的vnode和旧的vnode进行对比 进行**复杂的对比**,**只找出需要更新的部分进行更新** 2. 重做之前的demo 3. 核心api h()函数和patch()函数 ~~~ snabbdom.js snabbdom-class.js snabbdom-props.js snabbdom-style.js snabbdom-eventlisteners.js snabbdom-h.js vdom技术实现是很复杂的 var snabbdom = window.snabbdom;//全局对象 //定义patch var patch = snabbdom.int([ snabbdom_classs, snabbdom_props, snabbdom_style, snabbdom_eventlisteners, ]); var h = snabbdom.h; var container = document.getElementById("container") //生成vnode var vnode = h("ul#list",{},[ h('li.item',{},"Item 1"), h('li.item',{},"Item 2"), ]) //执行pactch patch(container,vnode); document.getElementById("btn-chang").addEventListener('click',function(){ var newVnode = h("ul#list",{},[ h('li.item',{},"Item 1"), h('li.item',{},"Item b"), h('li.item',{},"Item 3"), ]); //执行pactch 找出差异,根据差异渲染 patch(vnode,newVnode); }); ~~~ ~~~ var data = [{},{}]; data.unshifit({name:'',age:'',address}) var contariner = document.getElementById("container") var vnode; function render(data){ var newVnode = h(‘table’,{}, data.map(function(item){ var tds = []; var i; for(i in item){ if(item.hasOwnProperty(i)){ tds.push(h('td',{},item[i]+'')); } } return h('tr',{},tds); }); //存储当前vnode结果 vnode = newVnode; ); if(vnode){ //比较渲染 patch(vnode,newVnode); }else{ //初次渲染 patch(container,vnode); } } render(data); document.getElementById("btn-chang").addEventListener('click',function(){ data[1].age=10; data[2].address="bjing" //re-render //执行pactch 找出差异,根据差异渲染 render(data) }); ~~~ ### 核心api 1. h()函数 * h('<标签名>',{...属性},[...子元素..]) * h('<标签名>',{...属性},'...') 2. patch()函数 * patch(container,vnode); * patch(vnode,newVnode); ### 问题解答 如何使用?应用snabbdom的用法来举例 * 核心api:h函数 patch函数:初次渲染,再次渲染 ***** ### 介绍一下diff算法 1. 什么是diff算法 linux命令 diff:文本文件的异同 git diff判断两个版本文件的差异 git add之前,git status 再看一下文件差异 git diff diff算法不是vue和react提出的,很早之前就存在的,以前就用来对比文件字符串等 **diff:对比两个虚拟dom节点** 2. 去繁就简 diff算法非常复杂,实现难度大,源码来很大 去繁就简,讲明白核心流程,不关系细节 面试官也不清楚细节 去繁就简后依然难度很大 3. vdom为何用diff算法 * **dom操作是非常消耗性能的,因此尽量减少dom的操作** * **找出本次dom必须更新的节点来更新,其他的不更新** * **这个“找出”的过程,就需要diff算法** 真正用处:**找出前后两个vnode(虚拟dom节点)之间的真正差异,然后更新差异,其他的不更新** 4. diff算法实现流程 * patch(container,vnode); vnode怎么生成真正的dom元素? ~~~ function createElement(vnode){ var tag = vnode.tag; var attrs = vnode.attrs; var children = vnode.children||[]; if(!tag){ return null}; var elem = document.createElement(tag); var attrName for(attrName in attrs){ if(attr.hasOwnProperty(attrName)){ elem.setAttribute(attrName,attrs[attrName]); } } children.forEach(function(childnode){ //递归 给elem添加子元素 elem.appendChild(createElement(childVnode)); }) //返回真实的dom元素 return elem } ~~~ * patch(vnode,newVnode); ~~~ function updateChildren(vnode,newVnode){ var children = vnode.children||[]; var newChildren = newVnode.children||[]; children.forEach(function(child,index){ var newChild = newChildren[index]; if(newChild == null){ return; } if(child.tag === newChild.tag){ //两个tag一样,进行深层对比 递归 updateChildren(child,newChild) }else{ //dom元素替换 replaceNode(child,newChild) } } function replaceNode(vnode,newVnode){ var elem = vnode.elem;//真实的dom节点 var newElem = createElement(newVnode)//虚拟dom变成真实dom //替换 } ~~~ #### 不仅仅是以上的内容 1. 节点的**新增和删除** 2. 节点**重新排序** 3. 节点**属性、样式、事件绑定** 4. 如何极致压榨性能(无底洞,代码是为了压榨性能,不是可读性;性能很优,但可读性太差) ### diff实现过程 1. patch(container,vnode)和patch(vnode,newVnode); 2. createElement 3. updateChildren ### 问题解答 1. 什么是diff算法,是linux(git)的基础命令(对比文本文件,字符串),**在vdom中用于对比js对象**(对比节点和节点的不同) 2. vdom中应用diff算法是为了**找出需要更新的节点** 3. diff实现patch(container,vnode)和patch(vnode,newVnode); 4. 核心逻辑,createElement和updateChildren ***** ### 总结 1. vdom是什么?为何会存在vdom * virtual dom,虚拟dom * 用js模拟dom结构 * DOM操作非常“昂贵” * 将DOM对比操作放在js层,提高效率 2. vdom如何应用,核心api是什么 * snabbdom的做法 * 核心api:h函数(生成vnode节点),patch函数(将vnode节点渲染到页面) 3. 介绍一下diff算法 * 是linux(git)的基础命令(对比文本文件,字符串),**在vdom中用于对比js对象**(对比节点和节点的不同),为了**找出需要更新的节点** * 实现:patch(container,vnode)和patch(vnode,newVnode); * 核心逻辑,createElement和updateChildren