CSS Nesting嵌套与@scope规则也太雷同了吧?

这篇文章发布于 2024年03月31日,星期日,20:06,归类于 CSS相关。 阅读 1876 次, 今日 37 次 8 条评论

 

封面占位图

一、居然还有个CSS嵌套

年前有介绍CSS @scope规则,可以实现CSS的选择器内外嵌套,简化选择器的书写:

@scope(nav) {
  ul {
    list-style: none;
    padding: 0;margin: 0;
  }
  li {
    display: inline-block;
  }
  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
    background: skyblue;
  }
}

最近发现,几乎同一时间,现代浏览器还支持了专门用来嵌套书写的CSS Nesting语法,借鉴自Sass/Less/Stylus等预编译语言。

使用示意:

nav {
  ul {
    list-style: none;
    padding: 0; margin: 0;
  }
  li {
    display: inline-block;
  }
  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
    background: skyblue;
  }
}

可以看到和@scope(nav)的语法极为相似,所实现的效果也是一样的,假设有如下所示的HTML代码:

<nav>
  <ul>
    <li><a href="">链接1</a></li>
    <li><a href="">链接2</a></li>
    <li><a href="">链接3</a></li>
  </ul>
</nav>
<p><a>我呢?</a></p>

可以看到,nav元素内的链接有背景色等样式,外部的a元素则依然是默认样式。

嵌套样式渲染效果

并且两种写法的选择器的优先级也是一样的。

以a元素为说明对象,两种写法的选择器优先级均等同于 nav a

这就让我陷入了深深的沉思……为什么要弄两套规则如此相似的东西呢?

二、@scope和Nesting的不同

在我看来,CSS Nesting就是纯粹的语法糖,也就是就是纯粹的语法上的变化,本身并不具备任何新的特性。

@scope则不一样,@scope规则中想要生效的选择器,一定属于@scope的子元素,带有“局部上下文”的性质在里面。

我们可以通过一个例子感受下两者的不同。

假设有两个无需列表<ul>元素:

<ul>
  <li>列表1</li>
  <li>列表2</li>
  <li>列表3</li>
</ul>
<ul>
  <li>列表2-1</li>
  <li>列表2-2</li>
  <li>列表2-3</li>
</ul>

那么,@scope规则中使用:scope伪类(表示ul元素自身)和兄弟选择器,是无法匹配后面的ul元素的。

@scope(ul) {
  :scope + ul {
    color: red;
    font-weight: bold;
  }
}

此时,所有的列表文字样式都是默认状态,如下图所示:

列表样式效果

但若是换成CSS嵌套写法(使用&符号表示祖先选择器):

ul {
  & + ul {
    color: red;
    font-weight: bold;
  }
}

则可以看到后面一个<ul>列表的文字均是红色加粗:

红色加粗

由于CSS Nesting语法中的&符号就是纯粹的选择器替代,因此,上面的CSS写作这样效果也是一样的:

ul {
  & + & {
    color: red;
    font-weight: bold;
  }
}

区别2:完整CSS规则和随意嵌套

如果使用@scope语法,那么,规则中的每一行代码,都应该属于一个完整的规则,而不能是某一条单独的CSS声明。

例如,下面的写法会让整段CSS规则无效:

@scope(ul) {
  border: 1px solid;  

  li {
    color: red;
    font-weight: bold;
  }
}

但是CSS原生的嵌套语法都没有此顾虑,直接上:

ul {
  border: 1px solid;  

  li {
    color: red;
  }
}

可以得到如下截图所示效果,列表有边框,文字是红色:

列表边框和颜色

三、不同规则见的相互嵌套?

首先,CSS @scope范围和CSS Nesting嵌套是可以同时使用的,例如:

@scope(body) {
  ul {
    border: 1px solid;
    li {
      color: red;
    }
  }
}

同样可以让列表出现边框,内容颜色是红色。

列表边框和颜色

两种特性都支持和其他@规则互相嵌套。

CSS @scope范围可以嵌套其他@scope范围,例如下面的写法是合法的:

@scope(body) {
  ul {
    border: 1px solid;
  }

  @scope(li) {
    :scope {
      color: red;
    }
  }
}

也支持和@media媒体查询一起使用,例如:

@scope(ul) {
  :scope {
    border: 1px solid;
  }

  @media(width < 640px) {
    li {
      color: red;
    }
  }
}

和这段代码:

ul {
  & {
    border: 1px solid;
  }

  @media(width < 640px) {
    li {
      color: red;
    }
  }
}

均可以在窄屏下让列表文字颜色红色,如下图所示:

列表边框和颜色

四、究竟什么时候用哪个?

什么时候用范围,什么使用用嵌套呢?

我琢磨了下,似乎绝大多数场景两者是可以随意替换的。

思来想去,总感觉最大的区别反而是语义上的区别。

你如果仅仅是想让书写更加简洁,代码更加装逼,使用嵌套。

如果是单独的某个组件的开发,使用范围。

这么一想,心里舒服多了。

好了,就扯这么多吧,如果你有其他想法,欢迎评论交流。

😘😘😘

(本篇完)

分享到:


发表评论(目前8 条评论)

  1. Tyk_no说道:

    区别2没看懂,“都应该属于一个完整的规则,而不能是某一条单独的CSS声明。”这句话怎么理解?

  2. 沉冰浮水说道:

    错字:无需列表

  3. codehz说道:

    @scope还有一个用法,那就是

    @scope {
    img {
    border: 5px solid black;
    background-color: goldenrod;
    }
    }

    可以直接写style到某个元素里,scope只对当前元素下的元素生效,不会影响到外面去()
    感觉是取代了之前用shadow dom做的hack,现在不需要shadow dom也可以实现可复用组件了

  4. haoliang.wu说道:

    我感觉不同还可以包含 @scope 支持 @scope (scope root) to (scope limit) 这种语法,这种通过 Nesting 语法来实现,就麻烦一些

  5. netfishx说道:

    可以用css nesting尝鲜了,scope目前Firefox不支持

  6. mfk说道:

    没什么用。还不如直接写原来的不嵌套语法。浏览器兼容性更好。

  7. 依韵说道:

    建议本篇再补充下 CSS Nesting 的兼容性。
    查了下似乎Nesting的兼容性更好点,Firefox支持,而Firefox还不支持@scope