虚拟DOM和diff算法
createEmement 创建组件
createElement
是另一种创建 react 元素的方法,但是相对麻烦,并且难于维护所以了解即可
import React from 'react'
import ReactDOM from 'react-dom'
// 参数1: 标签名
// 参数2: 标签属性
// 参数3: 标签内部的子节点
const jsx = React.createElement(
'div',
{
className: 'top', title: 'hello' },
'你好啊'
)
// <div class="top" title="hello">你好啊</div>
ReactDOM.render(jsx, document.querySelector('#app'))
JSX转换过程
- JSX 是 createElement() 方法的语法糖 (语法糖:更加直观、简洁、友好)
- JSX 语法会被
@babel/preset-react
插件编译为 createElement 方法
△JSX 对程序员更方便直观、createElement方法运行起来更方便 - createElement 又会被转化为 React元素,React元素是一个对象,能够描述UI结构
// 核心
{
type: 'div',
props: [
{
className: 'top'},
{
children: 'Hello JSX'}
]
}
虚拟DOM
虚拟DOM: 本质上是个 js 对象,用来描述页面UI (React 元素就是虚拟DOM)
创建时:
- React组件配合 state 创建一个虚拟DOM树
- 根据虚拟DOM树,生成一个真正的 DOM 树,再渲染到页面中
更新时:
- 当 state 或者 props 变化时,生成一个新的虚拟DOM树
- 新旧虚拟 DOM 树进行对比(diff算法),找到新旧虚拟DOM的差异点
- 将差异点更新到页面上
diff算法
tree diff
: 按照树的层级进行比较叫做 tree diff,如果该节点不存在,则整个删除,不再继续比较component diff
: 每一层中组件的对比叫做 component diff
△ 如果前后组件类型相同,暂时不需要更新
△ 如果前后组件类型不同,则需要更新element diff
:如果两个组件类型相同,则需要对比组件中的元素,叫做 element diff
△ 如果元素不同,则需要更新
△ 如果元素相同,则不需要更新
递归实现diff算法
实现思路:
- 按照面向过程的方式来写代码
- 当发现有雷同代码时,拆分观察,找异同
- 规整代码
1) 按照面向过程的方式编写代码
// 声明一个空对象,用来保存找出的不同节点
var result = {
}
// 遍历新树
for (var key in newTree) {
if (typeof newTree[key] !== 'object') {
if (newTree[key] !== oldTree[key]) {
result[key] = newTree[key]
}
} else if (typeof newTree[key] === 'object') {
result[key] = {
}
for (var k in newTree[key]) {
if (typeof newTree[key][k] !== 'object') {
if (newTree[key][k] !== oldTree[key][k]) {
result[key][k] = newTree[key][k]
}
}
}
let tmpKeyArr = Object.keys(result[key])
tmpKeyArr.length == 0 ? delete result[key] : ''
}
}
console.log(result)
2)规整代码,封装函数,进行自调
注意: 内层函数调用时传入的参数
function diff (newNode, oldNode) {
var result = {
}
for (var key in newNode) {
if (typeof newNode[key] !== 'object') {
if (newNode[key] !== oldNode[key]) {
result[key] = newNode[key]
}
} else if (typeof newNode[key] === 'object') {
result[key] = diff(newNode[key], oldNode[key])
let tmpKeyArr = Object.keys(result[key])
tmpKeyArr.length == 0 ? delete result[key] : ''
}
}
return result
}
var res = diff(newTree, oldTree)
console.log(res)
Object.keys(obj):
获取obj对象的所有属性,并保存到一个数组中
let arr = Object.keys({a:'Hello', b:'World'}) ; arr = ['a', 'b']
delete Obj.a;
删除 obj 对象中的 a 属性