ant-design / antd-style

css-in-js library with antd v5 token system
https://ant-design.github.io/antd-style/
MIT License
187 stars 29 forks source link

🧐[问题] createInstance 的隔离性存在问题? #114

Open tommytroylin opened 9 months ago

tommytroylin commented 9 months ago

🧐 问题描述

最近在使用 antd-style 结合 antd v5 进行组件二次开发定制

发现 createInstance 的行为与我的理解不一致,故留下此问题

按照我的理解。createInstance 会创建一套完全独立的样式 token 作用域(只依赖 antd Token)。来保证组件库混用时不会被污染。

但是实测行为不一致

💻 示例代码

import { ConfigProvider, Space, theme } from 'antd';
import { ThemeProvider, createInstance } from 'antd-style';

const instanceA = createInstance({ prefixCls: 'a', key: 'antd-a' });

const instanceB = createInstance({ prefixCls: 'b', key: 'antd-b' });

const useInstanceAStyles = instanceA.createStyles(() => {
  return {};
});

const useInstanceBStyles = instanceB.createStyles(() => {
  return {};
});

function AntdStyledComponent() {
  const { token: antdRawToken } = theme.useToken();
  const { theme: antdStyleThemeA } = useInstanceAStyles();
  const { theme: antdStyleThemeB } = useInstanceBStyles();
  return (
    <Space direction="vertical">
      <Space>
        <div>antdRaw FontSize:</div>
        <div>{antdRawToken.fontSize}</div>
      </Space>
      <Space>
        <div>instanceA FontSize:</div>
        <div>{antdStyleThemeA.fontSize}</div>
      </Space>
      <Space>
        <div>instanceB FontSize:</div>
        <div>{antdStyleThemeB.fontSize}</div>
      </Space>
    </Space>
  );
}

function App() {
  return (
    <ConfigProvider theme={{ token: { fontSize: 12 } }}>
      <AntdStyledComponent />
    </ConfigProvider>
  );
}

function App2() {
  // 使用默认导出的 ThemeProvider
  return (
    <ThemeProvider>
      <ConfigProvider theme={{ token: { fontSize: 12 } }}>
        <AntdStyledComponent />
      </ConfigProvider>
    </ThemeProvider>
  );
}

function App3() {
  // 使用 instanceA 导出的 ThemeProvider
  return (
    <instanceA.ThemeProvider>
      <ConfigProvider theme={{ token: { fontSize: 12 } }}>
        <AntdStyledComponent />
      </ConfigProvider>
    </instanceA.ThemeProvider>
  );
}

export default App3;

🚑 其他信息

App1 结果 符合预期

antdRaw FontSize:
12
instanceA FontSize:
12
instanceB FontSize:
12

App2 结果 instanceB 收到影响。不符预期?

antdRaw FontSize:
12
instanceA FontSize:
14
instanceB FontSize:
14

App3 即使使用不同的 instance 的 ThemeProvider ,结果 instanceB 依然收到影响。不符预期?

antdRaw FontSize:
12
instanceA FontSize:
14
instanceB FontSize:
14
arvinxx commented 9 months ago

Ok 我研究确认下

tommytroylin commented 9 months ago

@arvinxx 简单排查了下

问题在 https://github.com/ant-design/antd-style/blob/master/src/functions/createInstance.ts#L84 这里若 createInstance 的时候不传递 styled.ThemeContext

https://github.com/ant-design/antd-style/blob/master/src/factories/createUseTheme.ts#L20 默认使用了 @emotion/react 的同一个 ThemeContext,最终导致没有隔离

有两种改法,具体看 api 的设计。

  1. 若 createInstance 不默认依赖 emotion 的 ThemeContext。则用户不传递时改成创建一个新的。内部创建自己默认实例时主动去传递 emotion ThemeContext。改造会符合个人理解的 api 语义。但是对当前所有配合 emotion 使用的用户来说是个潜在的 break change

  2. 若 createInstance 需要依赖 emotion 的 默认ThemeContext。那需要文档更新,在编写组件库要隔离的时候 必须传入类似 { styled: { ThemeContext: createContext({}) }} 配置

个人倾向是第一种。看看怎么改比较合适。我现在先在项目中绕过了

arvinxx commented 7 months ago

后续应该会选择第一种,有计划可能会在下个大版本中移除 @emotion/react 的依赖。