CSS原生嵌套终于来了!我用它重构了组件的样式代码

2026-08-22 0 1,008

说到写CSS,过去最烦的就是一条条写选择器,尤其是组件样式里,父元素、子元素、伪类、伪元素,动不动就一大串。以前只能借助Sass、Less这些预处理器,现在好消息来了——CSS原生嵌套规范已经正式在Chrome 120、Edge 120、Safari 17.2、Firefox 117里默认支持了。这意味着不需要任何构建工具,直接在浏览器里写嵌套代码。上个星期我把自己维护的组件库样式用原生嵌套重写了一遍,删掉了上千行重复选择器,今天来聊聊具体怎么做,以及有哪些坑。

一、原生嵌套到底长什么样?

先看一个最直接的对比。以前写卡片组件样式像这样:

.card {
    background: white;
    border-radius: 8px;
}
.card .card-title {
    font-size: 18px;
}
.card .card-title:hover {
    color: #333;
}
.card .card-footer {
    border-top: 1px solid #eee;
}

用上原生嵌套之后,可以这样写:

.card {
    background: white;
    border-radius: 8px;

    .card-title {
        font-size: 18px;

        &:hover {
            color: #333;
        }
    }

    .card-footer {
        border-top: 1px solid #eee;
    }
}

是不是跟Sass几乎一样?区别在于这个语法现在被浏览器直接支持。注意那个&符号依然有效,表示父选择器的引用。在嵌套里表示当前匹配的元素。

二、嵌套的选择器类型

1. 直接子代

以前写 .menu > .menu-item,嵌套后可以这样:

.menu {
    > .menu-item {
        display: inline-block;
    }
}

2. 连接伪类

伪类是最常用的。原先写 .button:hover,现在在嵌套里用&连接:

.button {
    background: blue;

    &:hover {
        background: darkblue;
    }
}

3. 群组选择器

如果想让多个元素共享样式,可以这样:

.list,
.grid {
    display: flex;
    gap: 10px;
}

但在嵌套里,我想给.list.grid同时设置子元素的样式,还不想破坏当前嵌套层级,可以这么写:

.list {
    + .grid {
        margin-top: 20px;
    }
}

不过这是相邻兄弟,跟群组没关系。记住:嵌套只是语法糖,最后解析出的选择器依然是有效的CSS。

三、最常用的用法:修饰符和状态

我重构最多的就是“按钮”组件。以前写多态按钮,各种依赖基础类再追加状态类。用嵌套以后,把所有状态都收进一个作用域里。

.btn {
    padding: 8px 16px;
    border: 1px solid transparent;

    &.btn-primary {
        background: blue;
        border-color: darkblue;
    }

    &.btn-outline {
        background: transparent;
        border-color: gray;
    }

    &:disabled {
        opacity: 0.5;
        cursor: not-allowed;
    }

    .icon {
        margin-right: 4px;
    }
}

这样写的最大好处是,一眼就能看出所有跟 .btn 相关的样式都在一块,不用上下翻文件。

四、不得不提的@nest规则

虽然&能解决大部分场景,但有些需求必须用@nest。比如你想让一个子元素在父元素处于某个状态时出现特殊样式,但这种状态不能直接写在子元素的选择器里。举个例子,当父容器有 .is-active 类时,改变内部图标的颜色。

.card {
    .icon {
        color: gray;

        @nest .card.is-active & {
            color: blue;
        }
    }
}

这里&代表被嵌套的当前元素(.icon),前面加上 .card.is-active,最终产生选择器 .card.is-active .icon。如果不使用@nest,这种“父元素状态影响子元素”的关系很难在纯嵌套里写出来。不过事实上,用&的另一种写法也能实现:在 .card 里写 &.is-active .icon。但 @nest 更灵活,可以让你把这个逻辑放在更局部的位置。

五、重写一个“列表组件”的例子

为了让你们看到实际重构的潜力,这里展示一个列表组件。以前的CSS是这样的:

.list {
    list-style: none;
    margin: 0;
    padding: 0;
}
.list .list-item {
    padding: 12px 8px;
    border-bottom: 1px solid #f0f0f0;
}
.list .list-item:hover {
    background: #fafafa;
}
.list .list-item:last-child {
    border-bottom: none;
}
.list .item-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
}
.list .item-content {
    flex: 1;
}
.list .item-content .title {
    font-weight: 600;
}
.list .item-content .desc {
    color: #999;
}

重构后:

.list {
    list-style: none;
    margin: 0;
    padding: 0;

    .list-item {
        padding: 12px 8px;
        border-bottom: 1px solid #f0f0f0;

        &:hover {
            background: #fafafa;
        }

        &:last-child {
            border-bottom: none;
        }
    }

    .item-avatar {
        width: 40px;
        height: 40px;
        border-radius: 50%;
    }

    .item-content {
        flex: 1;

        .title {
            font-weight: 600;
        }

        .desc {
            color: #999;
        }
    }
}

感觉就像在写Sass,但是编译后依然是普通CSS,没有额外负担。每次改动样式,只需要找到对应的父级块,思路非常顺。

六、注意事项和坑

坑1:嵌套不能无限深

虽然规范没有明确限制深度,但浏览器解析会有一个最大深度(比如Chrome是256)。大多数组件写个五六层完全没问题,但深度太多除了性能,可读性也会下降。所以还是建议最多嵌套4-5层。

坑2:不支持循环引用

.a {} 里面再写 .a .b 是可以的,但不能写 .a { & .a {}} 这种把父选择器全部重复一遍的,虽然技术上能解析,但容易让选择器变得冗余。而且浏览器可能会把它解析成 .a .a 导致匹配不到预期元素。我一般只在描述子状态时才用 &

坑3:媒体查询不能在嵌套里冒泡

在Sass里我们可以在嵌套内写 @media,它会自动把媒体查询提到外层。但CSS原生嵌套目前不支持这种“冒泡”行为。@media 可以直接写在嵌套规则里,但是它只能作用于那个规则块,不会像Sass那样把父选择器带出来。也就是说,你不能这样写:

.card {
    @media (max-width: 600px) {
        display: block;
    }
}

现实是,这个 @media 会被解析成 @media (max-width: 600px) { .card { ... } }吗?其实规范里面有定义,它等价于:

@media (max-width: 600px) {
    .card {
        display: block;
    }
}

嗯,实际上还是能用的,但需要注意其作用域是嵌套内的规则,不是整个父子上下文。测试下来这种“媒体查询跟随当前选择器”的行为跟预处理器一样。之前看规范说支持,用起来还正常,不过最好还是避免嵌套内直接用 @media,毕竟实际浏览器解析可能有些雷。

坑4:和CSS预处理器混用要小心

如果你项目本来就用Sass,那么在Sass代码里写CSS原生嵌套可能会导致Sass处理冲突。建议两种方式不要混合,要么全用Sass,要么纯CSS。我现在新项目直接用原生嵌套,就不再引入Less或Sass了,减少依赖。

七、既然浏览器支持,那还需要构建工具吗?

如果你不考虑老浏览器,现在核心浏览器都已经支持,完全可以直接写原生CSS。当然,如果还要兼容旧版本,建议还是用PostCSS插件去编译嵌套语法。不过今天主要聊的是原生特性,目的是让你知道有这么个东西,并且很快会普及。我在Vue、React项目里都试过,直接在 .vue 或 .jsx 里写嵌套CSS,浏览器控制台完全无报错。

八、总结

CSS原生嵌套让我这种写惯了Sass的人感到极度舒适,代码结构清晰了,而且省去了预处理器的编译步骤。虽然还有个别限制,但整体上完全够用。如果你最近也在重构样式,不妨把组件里的CSS改成嵌套试试,这种“返璞归真”的体验真的挺爽。特别是现在浏览器更新越来越快,CSS新特性一个接一个,前端终于变得更简单了。

CSS原生嵌套终于来了!我用它重构了组件的样式代码
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请发送邮件1506151422@qq.com联系,将会及时下架删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 css CSS原生嵌套终于来了!我用它重构了组件的样式代码 https://www.taomawang.com/web/css/2588.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务