问题

  • Qiankun + Vite 作为子应用时,样式隔离无效

  • 即使配置了 experimentalStyleIsolation: true 也无效

  • 解决方案:使用 postcss-prefix-selector 插件为样式添加前缀

配置插件

  • 在子应用 vite.config.ts 中配置插件(官方示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    import prefixer from 'postcss-prefix-selector';

    ...

    export default defineConfig({
    ...
    css: {
    postcss: {
    plugins: [
    prefixer({
    prefix: '[data-qiankun-app]',
    transform(prefix, selector, prefixedSelector, filePath, rule) {
    if (selector.match(/^(html|body)/)) {
    return selector.replace(/^([^\s]*)/, `$1 ${prefix}`);
    }

    if (filePath.match(/node_modules/)) {
    return selector; // Do not prefix styles imported from node_modules
    }

    const annotation = rule.prev();
    if (annotation?.type === 'comment' && annotation.text.trim() === 'no-prefix') {
    return selector; // Do not prefix style rules that are preceded by: /* no-prefix */
    }

    return prefixedSelector;
    },
    }),
    ],
    }
    },
    ...
    });
  • 在子应用 main.tsx 中添加 data-qiankun-app 属性(下面是示例)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    import { createRoot } from 'react-dom/client';
    import {
    QiankunProps,
    qiankunWindow,
    renderWithQiankun,
    } from 'vite-plugin-qiankun/dist/helper';
    import App from './App.tsx';
    import './index.css';

    const render = (container?: HTMLElement) => {
    const app =
    container || (document.getElementById('root') as HTMLDivElement);

    /**
    * 添加属性,用于样式隔离
    * 注意:这里的属性名要和 postcss-prefix-selector 插件中的 prefix 保持一致
    */
    app.setAttribute('data-qiankun-app', 'true');
    // container && container.setAttribute('data-qiankun-app1', 'true');

    createRoot(app).render(<App />);
    };

    /** Qiankun 生命周期钩子 */
    const qiankun = () => {
    renderWithQiankun({
    bootstrap() {},
    async mount(props: QiankunProps) {
    render(props.container);
    },
    update: (props: QiankunProps) => {},
    unmount: (props: QiankunProps) => {},
    });
    };

    if (qiankunWindow.__POWERED_BY_QIANKUN__) qiankun();
    else render();
  • 下面是一个示例,可以在控制台中看到样式被添加了前缀,实现了样式隔离

    1
    2
    3
    4
    5
    6
    7
    8
    import React from 'react';
    import './App.css';

    const App = () => {
    return <div className="app">Hello, Qiankun!</div>;
    };

    export default App;
    1
    2
    3
    4
    5
    6
    7
    8
    .app {
    color: red;
    }

    // 在控制台中被转换成
    [data-qiankun-app] .app {
    color: red;
    }
  • 实现跟 qiankun experimentalStyleIsolation 类似的效果

组件库样式隔离

  • 上面样式对自己写的类名生效,但是对于引入的组件库样式隔离可能无效无效

  • antd ConfigProvider 可以给组件添加前缀

    1
    2
    3
    4
    5
    6
    import { ConfigProvider } from 'antd';
    import App from './App';

    <ConfigProvider prefixCls="app">
    <App />
    </ConfigProvider>;
    1
    2
    3
    .app-btn {
    color: red;
    }

具体实现参考 qiankun-multiple-subapp