Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛[BUG] antd-styles性能问题: useStyles暴露出的钩子,在组件每次render的时候,都会重新执行styles的计算。 #158

Open
guaizi149 opened this issue Jul 26, 2024 · 21 comments

Comments

@guaizi149
Copy link

guaizi149 commented Jul 26, 2024

尤其是在表格场景,会导致表格非常卡顿。

useTheme()方法每次返回的都是一个可变的theme。
调试了下代码,发现很多地方的context值都没有用memo包裹,最后追踪到antd到theme导出useToken的方法,在每次render的时候也会变化。

image
@arvinxx
Copy link
Collaborator

arvinxx commented Jul 26, 2024

没懂,都memo过了,怎么会重新计算?

@guaizi149
Copy link
Author

image 上图中的theme来自于useTheme,而useTheme返回值如下: image 可以看到这里的返回值是没有用useMemo包裹的,所以每次都会变化。如果把这里用useMemo包裹也不行,因为styledTheme每次render都会变化,就这样一层层往上找,,最后追踪到antd到theme导出useToken的方法,在每次render的时候也会变化。

@arvinxx
Copy link
Collaborator

arvinxx commented Jul 29, 2024

你的顶层 ConfigProvider 是在哪的?理论上 ConfigProvider 那一层配置过以后应该不会再 re-render 的,所以下层的 useTheme 拿到的值应该都是固化的。 你要不试试看

  const theme = useTheme();

  useWhyDidYouUpdate('Theme', { theme });

看看这个变不变,我之前测下来这都不会 rerender 的

如果会rerender,给一个复现 demo ,我再测一测

@guaizi149
Copy link
Author

image 用performance录制,点击按钮,更新count,可以看到Test组件的渲染中调用了updateMemo的hooks,说明useStyles的memo并没有起作用。

@guaizi149
Copy link
Author

@arvinxx 麻烦问下,这个测下来是确实有性能影响,还是我理解有误?

@arvinxx
Copy link
Collaborator

arvinxx commented Aug 6, 2024

@guaizi149 还没来记得看,
image

给下权限?我打不开

@guaizi149
Copy link
Author

guaizi149 commented Aug 6, 2024

@guaizi149
Copy link
Author

有更新吗?

@guaizi149 guaizi149 changed the title 🐛[BUG] useStyles暴露出的钩子,在组件每次render的时候,都会重新执行styles的计算。 🐛[BUG] antd-styles性能问题: useStyles暴露出的钩子,在组件每次render的时候,都会重新执行styles的计算。 Sep 5, 2024
@arvinxx
Copy link
Collaborator

arvinxx commented Sep 10, 2024

@guaizi149 本周会看下

@zhangtianwei1998
Copy link

我也遇到了这个问题

@Col0ring
Copy link

+1

@Wxh16144
Copy link
Member

https://github.com/guaizi149/codesandbox-template-vite-react/tree/main

这个仓库可以打开吧? @arvinxx

2024/10/24 update

看了你的最新提交 ca88a9e 代码。 如图:

image

你的 <App /> 组件中有一个 state, 并且每次点击按钮都会更新这个 state。

<App /> 组件中有一个 <Test /> 组件。虽然他没有任何 props,但是 <App /> state 变化 re-render 时,<Test /> 也会 re-render。

可以看看这个文章 Before You memo() 了解更多。

如果你不想让 <Test /> 重新渲染,可以使用 React.memo 来包裹 <Test /> 组件。

其次 App 组件中有个 ThemeProvider,同样他也会在每次 App 组件 re-render 时重新渲染。但是 antd-style 是用了 React.memo 包裹的,所以他不会重新渲染。

在用 React.memo 之前可以考虑将 ThemeProvider 提升到外层,App 作为 ThemeProvider 的 children 传递,减少不必要的 re-render。

同理 Test 组件也可以用 children 传递,减少不必要的 re-render。


稍等我再看看 antd-style 通过 createStyles 创建的 useStyles hooks 有没有问题。

就目前看到 props 传递复合类型时是有问题的

@guaizi149
Copy link
Author

看到 props 传递复合类型时是有

Test组件重新渲染是没有问题的。现在的问题是Test重新render的时候,const { styles } = useStyles()这个钩子重新执行了updateMemo,说明useStyles的memo没有起作用,具体原因可以看下开头的叙述。
image

@Wxh16144
Copy link
Member

const { styles } = useStyles()这个钩子重新执行了updateMemo,说明useStyles的memo没有起作用,

createStyles 创建的 hook 是有点问题, 我还在看 ant-desing/cssinjs 逻辑。 感觉上面一层也有点问题。另外你说的 updateMemo 我咋没找到相关代码呢。 是你提供复现仓库吗。 具体我也在看,头大

@guaizi149
Copy link
Author

@Wxh16144 提供的仓库,直接使用performance录制就可以看到updateMemo的调用链。

其实就是我第一条回复中的截图,第二个红框,每次render的时候都执行了。
image

我之前调试过,发现很多地方的context值都没有用memo包裹,我全部使用useMemo包裹后,最终追踪到某一个context值依赖antd到useToken返回值也会在每次render的时候变化。但我刚刚试了最新版本的antd,useToken的返回值已经在每次render的时候不变了。

@arvinxx
Copy link
Collaborator

arvinxx commented Nov 14, 2024

但我刚刚试了最新版本的antd,useToken的返回值已经在每次render的时候不变了。

@guaizi149 这是不是意味着如果在 antd-style 里一路 memo 下来,你的问题就解了?

@guaizi149
Copy link
Author

@arvinxx 不好意思,刚看到。 我当时是一路memo下来,最后找到antd的useToken也变化,现在已经不变了。其中context一定要保持不变,不然是不行的。

@arvinxx
Copy link
Collaborator

arvinxx commented Nov 26, 2024

@guaizi149 所以现在如果再 memo 的话是不是能解决你的问题?

@guaizi149
Copy link
Author

@arvinxx 之前修改的demo已经被我删掉了。。 之前在表格场景,尤其是虚拟表格,性能影响很严重。现在表格我们已经更换了其他技术栈,所以就没继续研究了~

@Wxh16144
Copy link
Member

Wxh16144 commented Dec 4, 2024

尤其是虚拟表格

+1,我的虚拟表方案用的是 react-data-grid,但是看了他们的代码也没发现啥问题,主要是单元格频繁的 mount 和 unmount,单元格中有 antd 组件就会卡, 每个组件都要走 antd cssinjs 那边 好几次,卡没办法解决。

回到 antd-style 来, 其代码也没问题,唯一问题可能就是创建的 useStyles 的方法依赖了外部传进来的变量,如果是引用类型,其每次都会 re-render。 但是场景比较小。 我有一个PR是讲内容进行浅比较。但是也收效甚微。 无奈

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants