目录
什么是虚拟DOM
虚拟dom是相对于浏览器所渲染出来的真实dom而言的. 在vue和react技术出现之前,如果想要修改页面展示的内容,只能通过遍历查询DOM树的方式查找要修改的dom元素进行操作,从而达到更新ui的目的.
这种方式相当消耗计算资源,因为每次查找DOM元素,几乎要遍历整棵DOM树,所以就建立了一个与DOM树相对应的虚拟DOM对象,简单理解就是javascript对象
以对象嵌套的方法来表示DOM树的层级结构,那么每次DOM的更改就变成了js对象属性的增删改查,这样一来查找对象属性的方法就比查找DOM树的性能开销要小
简单来说,虚拟DOM就是用js对象来模拟真实DOM,然后通过render方法,将它渲染成真实DOM
什么是render函数
简单来说,在vue中我们使用模板HTML语法来组建页面,而使用render函数我们可以用js语言来构建DOM, 因为vue是虚拟DOM,所以在拿到template模板时也要转译成VNode的函数,而用render构建DOM,就免去了转译的过程
当使用render函数描述虚拟DOM时,vue提供了一个函数,官网给它起了一个名字叫createElement,还约定它的简写叫 h
createElement核心方法
createElement 接受type
, props
,children
三个参数创建一个虚拟标签元素DOM的方法。
function createElement(type, props, children) {
return new Element(type, props, children);
}
为了提高代码高度的复用性,我们将创建虚拟DOM元素的核心逻辑代码放到Element
类中。
class Element {
constructor(type, props, children) {
this.type = type;
this.props = props;
this.children = children;
}
}
注意:将这些参数挂载到该对象的私有属性上,这样在new时也会有这些属性。
render核心方法
render方法接受一个虚拟节点对象参数,其作用是:将虚拟DOM转换成真实DOM
function render(eleObj) {
let el = document.createElement(eleObj.type); // 创建元素
for(let key in eleObj.props) {
// 设置属性的方法
setAttr(el, key, eleObj.props[key])
}
eleObj.children.forEach(child => {
// 判断子元素是否是Element类型,是则递归,不是则创建文本节点
child = (child instanceof Element) ? render(child) : document.createTextNode(child);
el.appendChild(child);
});
return el;
}
元素设置属性
在给元素设置属性的公共方法中接受三个参数:node
,key
,value
分别表示给那个元素设置属性、设置的属性名、以及设置属性的值。
function setAttr(node, key, value) {
switch(key) {
case 'value': // node是一个input或者textarea
if(node.tagName.toUpperCase() === 'INPUT' || node.tagName.toUpperCase() === 'TEXTAREA') {
node.value = value;
} else { // 普通属性
node.setAttribute(key, value);
}
break;
case 'style':
node.style.cssText = value;
break;
default:
node.setAttribute(key, value);
break;
}
}
递归设置子属性
判断子元素是否是Element
元素类型,是则递归
,不是则创建文本节点。
注意:我们只考虑了元素类型和文本类型两种。
eleObj.children.forEach(child => {
// 判断子元素是否是Element类型,是则递归,不是则创建文本节点
child = (child instanceof Element) ? render(child) : document.createTextNode(child);
el.appendChild(child);
});
真实DOM渲染到浏览器上
function renderDom(el, target) {
target.appendChild(el);
}
上诉步骤完成后,就可以将这几个方法导出去供其他使用即可
export {
createElement,
render,
Element,
renderDom
}
DOM Diff
Dom diff 则是通过JS
层面的计算,返回一个patch对象,即补丁对象,在通过特定的操作解析patch
对象,完成页面的重新渲染。
Patch 打补丁
当我们通过Diff算法获取补丁,然后通过patch
打补丁来进行更新DOM,从而更新页面视图
DOM-diff 的过程:
- 用JS对象模拟DOM(虚拟DOM)
- 把此虚拟DOM转成真实DOM并插入页面中(render)
- 如果有事件发生修改了虚拟DOM,比较两棵虚拟DOM树的差异,得到差异对象(diff)
- 把差异对象应用到真正的DOM树上(patch)