styled-components复杂切换场景下的性能问题及解决

背景

为了对antd项目进行升级,并且希望能够提供一种灵活的主题切换方式。有多套主题可以切换,每套主题下面的每个组件主题可以单独选配切换。

为了实现这样复杂的切换,我们选择了styled-components作为工具。进行了css可变属性的提取,全局样式的创建及传值。在组件内动态的传值以决定每一个组件的样式。

起初的做法是每个组件抽出一个css模板,然后将其汇总之后创建全局样式:


export const GlobalStyle = createGlobalStyle`
  ${spinStyle}
  ${Button}
  ${Menu}
  ${PaginationStyle}
  ${Slider}
  ${inputStyle}
  ${RadioStyle}
  ${selectStyle}
  ${AlertStyle}
  ${ComRadioStyle}
  ${tableStyle}
  ${switchStyle}
  // ...
`;

const RootStyle = ({ theme }) => {
  return ;
};

export default RootStyle;

这样,当外部传入的数据发生变化,样式会被重新编译,然后被插入style标签中。
这种模式下,样式不多的情况下没有问题,但是问题就在于我们的不止做了antd的样式,还有另外一套UI的样式。
他存在于另外一个工程,同样导出一个全局样式。两个UI合起来的组件数共有60+。这样只要主题数据发生变化,样式就会被重新编译,然后styled-components会去查找替换更改的部分,非常消耗性能。

那么如何去优化呢?

姿势一:

既然现在每次数据发生了变化所有样式都会重新编译,那么能不能只让数据变化的组件去更新样式二其它样式不变呢?当然可行的。

  • 首先要做的就是数据的处理:我们不能每次返回一个全新的数据了,我们要先找出变动的组件,然后将其数据(也就是一个js对象)替换为新数据,其它组件数据(对象引用)保持不变。

  • 每个组件的都导出一个全局样式。

  • 提供一个辅助组件,用于决定当前组件是否需要更新。

所以我们的处理是这样的:

 // Radio.style.js
export default createGlobalStyle`
  // 样式实现在这里
`;

// index.js
const cssList = [
  { name: "input", css: inputStyle },
  { name: "alert", css: alertStyle },
  { name: "anchor", css: AnchorStyle },
  { name: "radio", css: radioStyle },
  { name: "list", css: listStyle },
 // ...
];
// 遍历数据,生成一个组件列表
function getThemedComponents(list) {
  return list.map(({ name, css: C }) => ({ theme }) => {
    const [data, setData] = useState(theme);
    useEffect(() => {
      setData(prev => prev[n] !== theme[n] ? theme : prev); // 利用了stateHook 引用不变时跳过更新的特性
    }, [theme]);
    return (
        
    );
  });
}
// 根据cssList得到的组件列表
const components = getThemedComponents(cssList);

const RootThemeStyle = ({ theme }) => {
  return (
    <>
      {components.map((Comp, index) => (
        // eslint-disable-next-line react/no-array-index-key
        
      ))}
    
  );
};

export default RootThemeStyle;

两个工程都采用这套模式之后,性能确实得到的改善,由原来的的20秒卡顿变成了12秒卡顿。但是仍然是无法接受。


样式组件拆分1-容器未拆分.png

如图,主要有两个耗能点,每个5秒,合起来10秒。

那么还可以怎么优化呢?

姿势二:

目前,所有的样式文件都被styled-components注入到了一个style标签内,这个标签里的内容实在太多了。每次某一段css发生了变化,都要在里面进行查找替换,极其消耗性能。

我们如果对styled-components样式注入的容器进行拆分,性能是否能够得到提升?

进行一下尝试吧。

优化1-分三段-代码.png

测试记录:


分三段-性能.png

激动人心。性能又提升了一半还要多。

但是!!! 时间还是有点多。
既然拆分可以提升性能。那么拆的更细呢?那么最细是什么程度?当然是每个组件创建一个style标签。所以我们直接更改辅助组件。


function getThemedComponents(list) {
  return list.map(({ name, css: C }) => ({ theme, index }) => {
    const [data, setData] = useState(theme);
    useEffect(() => {
      setData(prev => prev[n] !== theme[n]) ? theme : prev);
    }, [theme]);
    return (
      
        
      
    );
  });
}

我们再测试一下性能。

终极优化-----完全打成碎片.png

可以看到,耗时居然变成了0.3秒。几乎点击了之后,主题立刻就切换过来了。
我们的dom结构变成了这样的


一大堆的style标签.png

至此,这个性能问题得到了解决。

你可能感兴趣的:(styled-components复杂切换场景下的性能问题及解决)