Skip to content

40 行代码实现 React 的 diff 算法核心逻辑

ts
// Placement表示对应的元素需要插入页面,Deletion表示元素需要被删除
type Flag = 'Placement' | 'Deletion'

interface FiberNode {
  key: string
  flag?: Flag
  index?: number
}

type FiberNodeList = FiberNode[]

function diff(before: FiberNodeList, after: FiberNodeList): FiberNodeList {
  let lastPlacedIndex = 0
  const result: FiberNodeList = []

  const beforeMap = new Map<string, FiberNode>()
  before.forEach((node, i) => {
    node.index = i
    beforeMap.set(node.key, node)
  })

  for (let i = 0; i < after.length; i++) {
    const afterNode = after[i]
    afterNode.index = i
    const beforeNode = beforeMap.get(afterNode.key)

    if (beforeNode) {
      // 可以复用旧节点
      beforeMap.delete(beforeNode.key)

      const oldIndex = beforeNode.index as number
      if (oldIndex < lastPlacedIndex) {
        afterNode.flag = 'Placement'
        result.push(afterNode)
        continue
      } else {
        lastPlacedIndex = oldIndex
      }
    } else {
      // 创建新节点
      afterNode.flag = 'Placement'
      result.push(afterNode)
    }
  }

  beforeMap.forEach(node => {
    node.flag = 'Deletion'
    result.push(node)
  })

  return result
}

// 更新前
const before = [{ key: 'a' }]

const after = [{ key: 'd' }]

diff(before, after) // 输出 [{ key: 'd', flag: 'Placement' }, { key: 'a', flag: 'Deletion' }]
// Placement表示对应的元素需要插入页面,Deletion表示元素需要被删除
type Flag = 'Placement' | 'Deletion'

interface FiberNode {
  key: string
  flag?: Flag
  index?: number
}

type FiberNodeList = FiberNode[]

function diff(before: FiberNodeList, after: FiberNodeList): FiberNodeList {
  let lastPlacedIndex = 0
  const result: FiberNodeList = []

  const beforeMap = new Map<string, FiberNode>()
  before.forEach((node, i) => {
    node.index = i
    beforeMap.set(node.key, node)
  })

  for (let i = 0; i < after.length; i++) {
    const afterNode = after[i]
    afterNode.index = i
    const beforeNode = beforeMap.get(afterNode.key)

    if (beforeNode) {
      // 可以复用旧节点
      beforeMap.delete(beforeNode.key)

      const oldIndex = beforeNode.index as number
      if (oldIndex < lastPlacedIndex) {
        afterNode.flag = 'Placement'
        result.push(afterNode)
        continue
      } else {
        lastPlacedIndex = oldIndex
      }
    } else {
      // 创建新节点
      afterNode.flag = 'Placement'
      result.push(afterNode)
    }
  }

  beforeMap.forEach(node => {
    node.flag = 'Deletion'
    result.push(node)
  })

  return result
}

// 更新前
const before = [{ key: 'a' }]

const after = [{ key: 'd' }]

diff(before, after) // 输出 [{ key: 'd', flag: 'Placement' }, { key: 'a', flag: 'Deletion' }]