利用css in js 优化树及深层递归组件的渲染性能

895 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情

求点赞,求关注,求转发

背景

我们在使用树的时候,大致会利用一个activeId和树里面的组件的id做比较,以确定哪个子节点被选中了。

大致代码如下

然后我们观察console输出,会发现,在每一次点击的时候,都会引起重新渲染。

这是因为即使我们使用了memo,但是因为把checkedkey 传递到了每一个子组件里,那么这个子组件在memo计算的时候,都会因为props发生了变化而重新渲染。

那么有没有什么办法可以减少这种渲染而达到我们的预期的选中后改变样式的效果呢?

css

我们考虑,如果直接操作css是不是开销会变小。因为css的重绘和重排的开销是远远小于react dom的变化的。

在上面demo种的例子,如果我们通过控制class的变化,或者style的变化,而把对应节点的颜色变掉,就解决了重新渲染的问题。

为了标记每一个不同的子元素,我们给每个子元素加一个data-id,作为后续查找的依据。然后通过data-id的变化来改变对应元素的样式。

image.png

css in js

这里我们使用 styled-components来作为我们的css in js 包。

首先安装

 npm install styled-components

然后我们需要定义一个style 组件,这里为了显示效果,会加一个!important,实际开发中,应该使用classs,不会使用内联样式。

import styled from "styled-components";

const TreeDemo = memo(function () {


 ...
 
     
  const Container = styled.div`
    color: black;
    [data-id='${checkedKey}']{
      color: red !important;
    }
  `;
 
 ...
 return (
    <Container>
      <Tree
        data={data}
        setCheckedKey={setCheckedKey}
      ></Tree>
    </Container>
  );

这里定义了一个Container元素,它的子元素 [data-id='${checkedKey}']为空色。这就跟我们的变量进行了绑定,每次变量变化的时候,这里的css都会发生变化。

然后在我们的子元素中,移除跟checkKey相关的代码。在子元素上加入data-id的属性。

子元素代码如下:


const Tree = memo(function ({ data,setCheckedKey}) {
  const changeCheckedKey = useCallback(
    (id) => {
      setCheckedKey(data.id);
    },
    [setCheckedKey]
  );
  return (
    <div
    data-id={data.id}
      style={{
        paddingLeft: 20,
        lineHeight: "20px",
        color:'black'
      }}
    >
      <div onClick={changeCheckedKey}>{data.label}</div>
      {data.children &&
        data.children.map((child) => (
          <Tree
            key={child.id}
            setCheckedKey={setCheckedKey}
            data={child}
          ></Tree>
        ))}
    </div>
  );
});

效果验证

因为码上掘金不支持外部包的直接导入,所以这里是用了codeSandBox

可以看到,这时子元素不会重新rerender

原理

我们看下元素的class

image.png

image.png 初始化的时候,会给一个默认的class值,而每次变化的时候,对应classname变化,同时在header里插入一条新的class

image.png

image.png

所以每次变化的实际是最外层的class,对元素的其他属性和dom是无影响的,在框架中使用无副作用。

css in js的缺点

大家可以参考《我们为什么弃用css in js》这篇文章,大体的问题就是运行时的渲染性能。但是如果跟dom性能作对比的话, 我主观觉得还是css in js 会占优一些。当然这句话说的时候,我没有做性能测试,如果有同学做了,也可以评论给我。