首页 > 解决方案 > css-loader css modules 嵌套组件规则

问题描述

假设我通过以下方式使用 scss 模块

A.jsx

import styles from './A.module.scss';
import B from '../B/B.jsx';

const A = props => {
    return <div className={styles.a}>
         <B/>
    </div>;
};

export default A;

B.jsx

import './B.scss';

const B = props => {
    return <div className="b">
    </div>;
};

export default B;

在里面A.module.scss我覆盖B的css规则如下:

.a {
 .b {
    width: 500;
    height: 500;
    background-color: red;
 }
}

那么我为B内部实例覆盖的样式A不会传递给它,显然是因为它使用的是 css 模块。如何使css规则B仅适用于A而不适用于全局?

我想也许我必须为className我想要实现的每个组件传递自定义道具,所以对于这个实例,将自定义className道具传递给B并像这样传递它:

<B className={styles.b}/>

并且B.jsx只需将自定义类应用于组件。不是我最喜欢的解决方案,宁愿不添加自定义类,而只使用自定义选择器.a .b,因为这定义了我想要定位的每个对象的关系BA但我想知道是否有像我正在寻找的更简单的解决方案?

另外,我不确定该className道具是否是一个可行的解决方案,何时B嵌套了多个节点,并且我正在尝试使用我的 css 规则来定位其中的嵌套节点。我不能只为每个组件都拥有className每个节点的道具。

这是说明这种情况的示例:

B.jsx

import './B.scss';

const B = props => {
    return <div className="b">
        <div className"b-inner1">
            <div className"b-inner2">
                <div className"b-inner3">
                </div>
            </div>
        </div>
    </div>;
};

export default B;

例如,在这里我希望使用 来设置节点的样式,className b-inner3这可能只是一个用于帮助B' 组件的基本布局的节点,并且我不希望该组件的用户能够传递className到该节点。

标签: reactjswebpacksasscss-loadercss-modules

解决方案


每个组件(与任何其他功能一样)对实现细节都有一些抽象,它也有一个 API,这个 API 是你和你的组件消费者之间的契约。
如果你没有公开 a classNameor styleprop,那意味着你基本上是在说“这个组件不会保证在 DOM 中始终保持相同的结构或公共类名”。如果有人会覆盖您的课程,他/她将承担风险并可能面临重大变化。

如果您确实想让消费者能够覆盖或添加样式,我认为您有 2(或 3)个主要选项:

  1. AcceptstyleclassNameprops 为您希望允许样式的每个节点:

    const MyComponent = (props) => (
        <div className={`${styles.myStyle1} ${props.publicStyle1}`}>
            <div className={`${styles.myStyle2} ${props.publicStyle2}`}>
                <button
                    className={`${styles.button}
                    ${props.publicStyleButton}`}
                >
                    Click Me
                </button>
            </div>
        </div>
    );
    

    是的,这可能会变得混乱,并且看起来像是一个令人困惑的 API,难以使用和维护。

  2. 公开硬编码的类名:

    const MyComponent = (props) => (
        <div className={`${styles.myStyle1} public-style-1`}>
            <div className={`${styles.myStyle2} public-style-2`}>
                <button
                    className={`${styles.button} public-style-button`}
                >
                    Click Me
                </button>
            </div>
        </div>
    );
    

    这样,您的合同规定您将始终保留这些公共类名称(或重大更改版本)

  3. 另一种与样式不直接相关的方法,通常称为“复合组件”,您可以将其与上述方法之一混合使用:

    const MyButton = ({ text, onClick, ...rest }) => (
        <button
            onClick={onClick}
            className={`${styles.button}`}
            {...rest}
        >
            {text}
        </button>
    )
    
    const MyComponent = (props) => (
        <div className={`${styles.myStyle1} public-style-1`}>
            {children}
        </div>
    );
    
    MyComponent.Button = MyButton;
    
    
    /* usage... */
    
    <MyComponent>
        <MyComponent.Button
            text="Click Me"
            className="consumer-class"
            style={{color: 'green'}}
        />
    </MyComponent>
    

    这将使您可以将组件分解成更小的部分,并为您的消费者提供更多控制权。


推荐阅读