本专栏旨在使用经典面试题,对知识的进行巩固加强,主要包括了JS基础、CSS基础、React、Redux、浏览器、工程化共6个部分。由于作者本人技术有限,所以这个专栏都是参考一些大神文章,整理出来的笔记,如有雷同,那真的是参考大佬的了。
题目:请简述什么是虚拟 DOM 和 diff 算法(react)?
这个题目其实可以延伸很大,对于我这种小菜鸟来说,暂时只是浅尝即止,对于里面深层次算法不探究,只学习如何构建即可。
- 首先什么是虚拟 DOM,虚拟 DOM其实就是一个 JS 对象,它长这个样子:
{
tag:s='div',
props:{class:'cxk'},
children:[
'是兄弟就努力砍',
{
tag:'button',
props:{class:'btn'},
children:[
'点击得屠龙宝刀'
]
}
]
}
希望得到的效果大概是这样的
- 其次这个虚拟 DOM 和真正的DOM在React中是怎么关联起来的呢? 我们通过在 react 中返回一段代码,看 react 是如何通过虚拟DOM最后渲染出来的。
- 首先,我们在React中会用到 jsx 语言来编译返回的类似 html 的代码
function render() {
return (
<div>
Hello Virtual DOM
<ul>
{
['first', 'second'].map(i => {
<li>{i}</li>
})
}
</ul>
</div>
)
}
// 编译之后
function render() {
return fn(
'div',
null,
'Hello Virtual DOM',
fn(
'ul',
null,
// 这里需要特殊处理
['first', 'second'].map(i => {
fn(
'li',
null,
i
)
})
)
)
}
- 这里的 fn 是用作将这 jsx 编译好的代码转变成虚拟DOM的函数,这个 fn 函数是在 babel 上定义的,然后我们来编写一下这个 fn 函数
// 编译函数传入三个以上参数,分别是标签名,属性,和children,
// 其中 children 一般情况都是一个对应的 fn() 函数,但是也有可能是一个数组函数,所以需要解构一次
// 返回一个对象,这个对象就是VD
function fn(tag, props, ...children) {
return {
tag,
props: props || {},
children: [].concat.apply([], children) || []
}
}
注意,这里在处理 children 的时候,有的时候这个 children 是一个数组,但是我们希望得到的 children 的最终结果是一个单层的数组,所以就需要对传入的 children 参数进行解构。 对于方法
[].concat.apply([], children)这个方法引起了对于 apply 用法的一些小思考,后续写文章记录
- 处理完成后,就可以得到我们前文所看到的虚拟 DOM 了。
tag: 'div',
props: {},
children: [
'Hello Virtual DOM',
{
tag: 'ul',
props: {},
children:[
{
tag: 'li',
props: {},
children;['first']
},
{
tag: 'li',
props: {},
children;['second']
},
]
}
]
}
- 这个时候,我们只需遍历这个对象,然后调用相应的方法即可创建出一课真实的 DOM 树了。
// 创建dom元素
function ownCreateElement(vdom) {
// 如果vdom是字符串或者数字类型,则创建文本节点,比如“Hello World”
if (typeof vdom === 'string' || typeof vdom === 'number') {
return doc.createTextNode(vdom);
}
const {tag, props, children} = vdom;
// 1. 创建元素
const element = document.createElement(tag);
// 2. 属性赋值
setProps(element, props);
// 3. 创建子元素
// appendChild在执行的时候,会检查当前的this是不是dom对象,因此要bind一下
children.map(vdom => ownCreateElement(vdom)).forEach(ele => element.appendChild(ele).bind(element));
return element;
}
// 属性赋值
function setProps(element, props) {
for (let key in props) {
element.setAttribute(key, props[key]);
}
}
这样,我们在 react 的 render 中写下一行代码,就会通过 jsx编译,babel 设置 fn,然后得到对应的虚拟DOM--js 对象,然后渲染成真实的 DOM 树,并在合适的时间挂载到浏览器中。
小结:
- 虚拟DOM是一个 JS 对象
- 是兄弟就点我
- 没了。