为什么我的样式不起作用?

4,405 阅读4分钟

关于

  • 今天被人问了一个关于react中的样式问题,一瞬间脑袋没反应上来好像还回答错了,有点尴尬水一篇文章记录一下。
  • 问题描述:在一个react父子组件demo中,实际效果与书写的样式不太一样。

问题复现

直接上代码描述问题:

  1. Parent.js
import React from 'react';
import Child from './Child'
import './Parent.less'

function Parent() {
  return (
    <div className="parent">
        <Child/>
        <div className='component'>parent</div>
    </div>
  );
}
export default Parent;
  1. Parent.less
.parent{
  background-color: blue;
  .component{
    color: white;
  }
}
  1. Child.js
import React from 'react';
import './Child.less'

function Child() {
    return (
        <div className="child">
            <div className='component'>Child</div>
        </div>
    );
}

export default Child
  1. Child.less
.child{
  background-color: red;
  .component{
    color: black;
  }
}

大概看一下代码,是有一个Parent的父组件,蓝底白字。还有一个Child的子组件,红底黑字。 那么实际渲染出的样式是什么样子的呢。如下图:

实际看到的效果确实蓝底白字红底白字,为什么与写的代码有出入呢。

究其原因

  • 为什么子组件的字体颜色不是黑色确是白色?

打开调试工具,看到子组件被渲染成一个<div class="component">Child</div> 但是样式却被父组件的样式给覆盖变成了白色,

原因:这是因为在w3c 规范中,CSS 始终是「全局的」。在传统的 web 开发中,最为头痛的莫过于处理 CSS 问题。因为全局性,明明定义了样式,但就是不生效,原因可能是被其他样式定义所强制覆盖。

  • 为什么同样.parent .component.child .component是父级覆盖子级?

这就要涉及到浏览器渲染原理css的浏览器解析原则则

浏览器渲染

  1. 浏览器将获取的HTML文档解析成DOM树。
  2. 处理CSS标记,构成层叠样式表模型CSSOM(CSS Object Model)
  3. DOMCSSOM合并为渲染树(rendering tree)将会被创建,代表一系列将被渲染的对象。
  4. 渲染树的每个元素包含的内容都是计算过的,它被称之为布局layout。浏览器使用一种流式处理的方法,只需要一次绘制操作就可以布局所有的元素。
  5. 将渲染树的各个节点绘制到屏幕上,这一步被称为绘制painting

需要注意的是,以上五个步骤并不一定一次性顺序完成,比如DOM或CSSOM被修改时,亦或是哪个过程会重复执行,这样才能计算出哪些像素需要在屏幕上进行重新渲染。而在实际情况中,JavaScript和CSS的某些操作往往会多次修改DOM或者CSSOM。

css的浏览器解析原则

看一个例子:

.nav h3 span {font-size: 16px;}

在我们不知道规则的情况下,我们是这样猜测的,按照常人的思维从左到右。先是找到.nav,然后向下匹配所有的h3span标签。如果在向下匹配的过程中,没有匹配上的则回溯到上一级继续匹配其他子叶结点。

但实际上,CSS选择器读取顺序是从右到左

如果是这样的规定的话,还是上面的例子就变成了,先找到所有的span标签,然后找span标签是h3的,然后再延着h3往上寻找,这时候发现一个选择器的类名为.nav就把这个节点加入结果集;如果一直往上找直到html标签都没找到的话,就放弃这条线,换到另一个span进行寻找。

那么来看我们的这个Demo中的结构

<div class="parent">
    <div class="child">
        <div class="component">Child</div>
    </div>
    <div class="component">parent</div>
</div>

浏览器先找到.component往上寻找,发现了.child .component 这时候渲染出样式为黑色,然后接着向上寻找发现了.parent .component发现存在这个CSS规则,所以这时候颜色变成了白色

如何变成正确的颜色

  • 问题找到了,是因为样式覆盖了,那么如何解决这个问题了。这里我们采用了CSS Modules方案。

  • 什么是CSS Modules? 把CSS划分模块,自动为类名后面生成一个hash值保证类名全局唯一。

  • CSS Modules的使用

  1. 使用create-react-app创建项目,修改webpack.config.js

  1. 在组件中使用
// parent.js
import styles from './Parent.less'

<div className={styles.parent}>
   <Child/>
   <div className={styles.component}>
    css modules parent
   </div>
</div>  

//child.js
import styles from './Child.less'
<div className={styles.child}>
    <div className={styles.component}>
        css modules child
     </div>
</div>

配置完成之后发现样式类名变成了hash值,这样即保证了类名的唯一不会存在覆盖的问题

最后