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 <GlobalStyle theme={parseThemeData(theme)} />;
};

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 (
        <C theme={data} />
    );
  });
}
// 根据cssList得到的组件列表
const components = getThemedComponents(cssList);

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

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 (
      <StyleSheetManager
        target={getStyleSheetTarget(
          `hzero_style_sheet_injection_section${index}`
        )}
      >
        <C theme={data} />
      </StyleSheetManager>
    );
  });
}

我们再测试一下性能。

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

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


一大堆的style标签.png

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335