Block Tree in React: 在 React 中实现 Block Tree

1,175 阅读1分钟

Block Tree in Vue3

Vue3 中通过编译找到静态节点实现了 Block Tree 的优化,想在 React 中实现当然不能想编译,要想 runtime,所以我们来看看 runtime-core 源码中的是怎样渲染静态节点的:

const patch: PatchFn = (
  n1,
  n2,
  container,
  anchor = null,
  parentComponent = null,
  parentSuspense = null,
  isSVG = false,
  optimized = false
) => {
  // ...
  const { type, ref, shapeFlag } = n2
  switch (type) {
    // ...
    case Static:
      if (n1 == null) {
        mountStaticNode(n2, container, anchor, isSVG)
      } else if (__DEV__) {
        patchStaticNode(n1, n2, container, isSVG)
      }
      break
  // ...
}

patch 是一个入口,针对 type 不同进行渲染,Static 节点通过 mountStaticNode 进行 mount,如果是开发环境为了实现热更新需要进行 update

const mountStaticNode = (
  n2: VNode,
  container: RendererElement,
  anchor: RendererNode | null,
  isSVG: boolean
) => {
  // static nodes are only present when used with compiler-dom/runtime-dom
  // which guarantees presence of hostInsertStaticContent.
  ;[n2.el, n2.anchor] = hostInsertStaticContent!(
    n2.children as string,
    container,
    anchor,
    isSVG
  )
}

/**
 * Dev / HMR only
 */
const patchStaticNode = (
  n1: VNode,
  n2: VNode,
  container: RendererElement,
  isSVG: boolean
) => {
  // static nodes are only patched during dev for HMR
  if (n2.children !== n1.children) {
    const anchor = hostNextSibling(n1.anchor!)
    // remove existing
    removeStaticNode(n1)
    // insert new
    ;[n2.el, n2.anchor] = hostInsertStaticContent!(
      n2.children as string,
      container,
      anchor,
      isSVG
    )
  } else {
    n2.el = n1.el
    n2.anchor = n1.anchor
  }
}

这就相当于直接跳过了静态节点的更新,放到 React 中可以实现吗?

Block Tree in React

当然可以!通过 shouldComponentUpdate 跳过更新,我们可以写一个 Static 组件,其 shouldComponentUpdate 始终返回 false,跳过它和它子节点的更新

export default class Static extends React.Component {
  shouldComponentUpdate() {
    return false
  }
  
  render() {
    return this.props.children
  }
}

使用的时候就可以这样:

export default function App() {
  const [count, setCount] = useState(0)
  return (
    <div>
      <Static>
        <h1>Static Title</h1>
      </Static>
      <div>{count}</div>
      <button onClick={() => setCount(count + 1)}>add one!</button>
    </div>
  )
}

实现 Block Tree 以减少不必要的更新

Difference

当然这样对于 Vue3 在编译时做的事交给用户来做,用户增加了心智负担,而且代码还乱,得不偿失,非常不建议这样使用 React,但这种方式我们也可以看到其实非常符合 React 的风格 🐶

好了,第一次写水 b 文章,就这样吧~