React进阶 - 14(说一说”虚拟DOM“中的”Diff算法“)

本章内容

上一节我们初步了解了 React中的”虚拟 DOM“ ,本节我们来说一说”虚拟DOM“中的”Diff算法“

一、了解 Diff 算法

上一篇中,我们有讲到:当 state或者 props数据变化时会生成新的 ”虚拟DOM“,然后”旧虚拟DOM“和”新虚拟DOM“进行比对。那么怎么进行比对呢?答案是”使用 Diff 算法“。

  • ”Diff算法“:我们把两个 ”JS对象“比对的算法叫做 ”Diff 算法“
问:1、”虚拟DOM“ 什么时候被比对
答:当”数据“发生变化的时候,新旧”虚拟DOM“才会进行比较


问2、那什么时候”数据“会发生变化
答:state 或者 props 改变时(代码中使用了 setState() 时,数据发生变化)


问3、为什么 React 的 setState() 设计成”异步“呢
答:为了提高 React 底层的性能。比如说如果我们在间断很短的时间内调用 setState() ,
如果设计成 ”同步“,那么就有三次更新比对”虚拟DOM“的过程,
如果设计成”异步“,就可以合并成”1次“,只做一次”虚拟DOM“的比对,然后去更新一次DOM,避免了性能的消耗

问4、新旧”虚拟DOM“如何进行比对呢
答:采用”同层比较“的方式。首先从最顶层开始,如果一致,就会去比较第二层,以此类推。
如果顶层比较时,不一致,则会直接将”原始DOM“进行全部替换。

这样”比较“和”替换“的暴力方式,看着好像挺浪费性能的(一层不一致就全部替换,很多DOM都没被复用),但由于”同层比较“的算法简单,因此比较的速度很快,性能一下就被提升了

二、key 值的重要性

在之前的案例中,我们循环渲染列表时,会在循环项上廷加一个 key。那为什么或者有必要添加这个 key呢?答案是:有必要!!!因为在”虚拟DOM“的”Diff算法“中,这个 key为循环的每一项添加了一个卫衣标识,可以有效提高”虚拟DOM“的比对性能

  • 假设我们现在有个数组,里面有 5 个数据项。当界面第一次循环渲染时,这5个数据会被映射成 5个”虚拟DOM“节点,生成一个小的”原始虚拟 DOM树“
    在这里插入图片描述

  • 当数据变化时,会生成一个”新的虚拟DOM“
    在这里插入图片描述

  • 然后”新旧虚拟 DOM“进行比对(理想状态)
    在这里插入图片描述

  • 如果此时每一项数据都没有设置 key值,那么节点和节点的关闭就不确定,需要一个一个的去比较。相反,如果设置了 key值,那么每一项被唯一标识。这样我们就可以将”相同key标识的项“去比对,这无疑加快了比较速度
    在这里插入图片描述

三、为什么不建议使用 index 做 key 值

指的注意的是,上面提升”性能“的关键点是”同样的节点取了相同的名字“,如果说使用 index作为 key值,我们不能保证相同的节点拥有同样的名字

  • 打开我们的 TodoList案例,在列表循环时,使用 index作为 key值,然后运行打开浏览器
// TodoList.js 文件
import React, { Component, Fragment } from "react";
import TodoItem from "./TodoItem";

class TodoList extends Component{
  constructor(props) {
    super(props)
    this.deleteData = this.deleteData.bind(this)
    this.addListData = this.addListData.bind(this)
    this.changeInputValue = this.changeInputValue.bind(this)
    this.state = {
      inputValue: '', 
      list: []
    } 
  }

  render() {  
    return (
      <Fragment>
        <div>
          请输入要进行的事项:
          <input value={this.state.inputValue} onChange={this.changeInputValue} />
          <button onClick={this.addListData}> 提交 </button>
        </div>

        <ul> {this.getTodoItem()} </ul>
      </Fragment>
    )
  }

  getTodoItem() {
    return this.state.list.map((item, index) => {
      // 1、使用 index 作为 key 值
      return <TodoItem key={index} content={item} index={index} deleteFn={this.deleteData}></TodoItem>
    })
  }

  deleteData(index) {
    this.setState((prevState) => {
      const list = [...prevState.list]
      list.splice(index, 1)
      return {list}
    })

  }

  addListData() {
    this.setState((prevState) => ({
      list: [...prevState.list, prevState.inputValue],
      inputValue: ''
    }))
  }

  changeInputValue(e) {
    const value = e.target.value
    this.setState(() => ({inputValue: value})) 
  }
}

export default TodoList


  • 在输入框中输入一些内容,观察界面效果
此时:

Oli --> key: 0
qdywxs --> key: 1
Oli and qdywxs --> key: 2

在这里插入图片描述

  • 删除 Oli后
此时:

qdywxs --> key: 0
Oli and qdywxs --> key: 1

  • 所以说,使用 index作为 key值,这就造成了同一节点,前后 key值不一样,那么这两个节点就无法建立联系,也就起不到根据key 值快速比对内容的效果了

  • 假如我们使用唯一的key值,节点的标识是稳定的,也就在比对过程中 key有了重要作用

到此,本章内容结束!

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值