潮州网站制作:在没有造成混乱的情况下扩展Sass

2019.08.12 mf_web

97

将@extend在萨斯指令是一个功能强大的指令,有利于规则和选择之间的关系的共享。但是,如果不仔细实施,可能会产生不良副作用。值得庆幸的是,有许多@extend有效使用的策略可以防止这些副作用,并产生干净,有组织的CSS。潮州网站制作

通过@extend详细检查和探索这些不同的策略,您可以准确地准确预测使用时会发生什么@extend,并在何时使用@mixin和何时使用时做出更明智的决策@extend,以确保最佳组织并限制样式表中未使用的样式。

将@extend在萨斯指令是一个功能强大的指令,有利于规则和选择之间的关系的共享。但是,如果不仔细实施,可能会产生不良副作用。

值得庆幸的是,有许多@extend有效使用的策略可以防止这些副作用,并产生干净,有组织的CSS。

通过@extend详细研究和探索这些不同的策略,您可以:

  • 准确预测您使用时会发生什么@extend,

  • 并在何时使用@mixin和何时使用时做出更明智的决定@extend,以确保最佳组织并限制样式表中未使用的样式。

你使用时会发生什么@extend?

@extend正如文档所示,Sass' 指令不仅仅是将扩展选择器组合在一起。@extend选择器时会发生两件大事:

  • 确定目标“ 简单选择器 ” ;

  • 使用以下内容在目标选择器上形成约束:

    • 正在进行扩展的选择器,

    • 和正在扩展的选择器。

让我们以一个例子来打破这个。请考虑以下代码:

.foo.bar { color: green; }.ancestor .descendant { @extend .bar; } // .bar is being extended
复制
  • 目标简单选择器标识为.descendant。

  • 形成两个约束:

    • .bar(以及扩展它的任何内容)必须使类.foo成为绿色,

    • .descendant必须是...的后代.ancestor。

使用此信息,该@extend指令生成此规则集:

.foo.bar, .ancestor .foo.descendant { color: green; }
复制

目标选择器.descendant现在满足约束要求:它有一个类.foo,它是一个祖先.ancestor。

@extend下面列出了一些常见方案。注意隐式放在目标选择器上的约束(要求)以及如何在输出的CSS中实现这些约束:

// Extending a selector with an ancestor.first { color: green; }.second { @extend .first; }.ancestor .first { background: white; }// CSS.first, .second { color: green; }.ancestor .first, .ancestor .second { background: white; }// Extending a selector with a descendant.first .descendant { color: green; }.second { @extend .first; }// CSS.first .descendant, .second .descendant { color: green; }// Extending a selector within a compound selector.one.two { color: green; }.three { @extend .one; }// CSS.one.two, .two.three { color: green; } // Extending a selector using a complex selector.foo.bar { color: green; }.complex .selector { @extend .bar; }// CSS.foo.bar, .complex .foo.selector { color: green; }// Alternating ancestors.ancestor-1 .foo { color: green; }.ancestor-2 .bar { @extend .foo; }// CSS// Notice how the ancestors switch places to // maintain the general ancestral constraints.ancestor-1 .foo,
.ancestor-1 .ancestor-2 .bar,
.ancestor-2 .ancestor-1 .bar {
  color: green;}// Combinator with common ancestor.foo.bar + .sibling { color: green; }.foo .descendant { @extend .sibling; }// CSS// Notice how .foo is repeated to preserve// constraint of .foo.bar.foo.bar + .sibling,
.foo .foo.bar + .descendant {
  color: green;}
复制

以上示例中有一些要点:

  • 扩展服从选择器约束。。在最后一个例子中,.descendant是兄弟姐妹.foo.bar,而不是.foo。它也是一个后代.foo,既然.foo不能同时是祖先和兄弟,它会输出两次以维持这些各自的约束。

  • 祖先和后代选择者容易发生排列爆炸。。祖先(以形式.ancestor .descendant)可以是父母,祖父母以外的任何东西。当具有祖先的选择器扩展具有祖先的其他选择器时,必须考虑所有这些排列。

  • 扩展是一种单向操作。。当.bar延伸.foo,.bar密切地意识到之间的关系.foo的选择和,而不是周围的其他方法。是的,选择者可以相互对立@extend。

为何使用@extend?

在@extend和@mixin指令是不完全一样的。虽然@mixin可以为多个选择器复制并通过参数自定义的输出规则,但@extend使用上述机制进行规则共享,以实现三个主要目的:

  • 继承可以被扩展选择器覆盖的选择器之间的规则集;

  • 特征或可共享的规则集;

  • 关系选择器之间,当与一定义的关系的选择器经由组合子延伸被保持(例如,>,+,~,等等)。

战略和指导方针 @extend

既然我们知道如何使用@extend以及究竟是什么导致选择器爆炸,那么让我们看看我们可以实现的一些策略,以便在选择器之间正确地共享样式。

使用和扩展%placeholder选择器。

Sass包含一种特殊类型的选择器,称为占位符选择器,它的字面意思是与之一起使用@extend。这些是非常通用的选择器,因为它们:

  • 当然可以扩展 ;

  • 在CSS输出中被忽略,这可以防止不必要的输出;

  • 可以像Sass中的任何其他选择器一样动态声明,这意味着你可以%do-#{$this}在非动态混合中使用它。

当你@extend %placeholders,爆炸性副作用减少,因为你有一个较少的共享选择器与最终的CSS输出结合。

尽可能少地扩展选择器。

通过扩展父(占位符)选择器来考虑以下继承示例:

// Don't do this:%button {
  // … parent button styles}.button {
  @extend %button;
  // … button styles}.button-primary {
  @extend %button;
  // … primary button styles}.button-secondary {
  @extend %button;
  // … secondary button styles}// CSS - we want to avoid this:.button, .button-primary, .button-secondary {
  // button styles}.button-primary { /* … */ }.button-secondary { /* … */ }
复制

当多次扩展相同的选择器时,问问自己,所有这些扩展选择器有什么共同之处?然后,您可以通过使用公共选择器进行重构来限制选择器的扩展频率,例如,而不是,以便唯一的扩展名为。.button.primary.button-primary.button { @extend %button; }

选择性地扩展特征,并考虑混合物的短特征。

使用特征可以很难实现先前的策略,根据定义,可以在许多不相关的选择器之间共享。一般来说,特征没有等级约束 ; 也就是说,它们(通常)与使用它的选择器和其他选择器之间的关系无关。因此,它们可以与@extend或一起使用@mixin。

我(个人)会选择@extend一个有很多规则的特质。该输出CSS 意志集团无关选择与该性状规则集,虽然这不是一个大问题-样式表维护您的发生.sass或.scss文件,而不是你的.css文件,并有几个性能之间的平衡,在一般。

另一方面,我会使用@mixin简洁且经常使用的规则集,例如clearfix。扩展它会导致CSS为这个小特征输出大量选择器。使用它作为mixin将使输出的CSS更具可读性,并且重复是可以的,因为规则集很小。

所以,这里有一些使用特征的一般指导原则:

  • 扩展具有长规则集或不经常使用的特征。。这样可以保存输出的CSS DRY,并有助于在CSS中对规则集进行分组,从而提高浏览器调试的清晰度。

  • Mixin特征更通用,更短且经常使用。。你可以使用@extend这些类型的特征,但是你更容易受到选择器爆炸的风险。

  • 将重复的规则集粘贴在%placeholder特征或特征中@mixin。这只是将SCSS尽可能保持干燥的一般做法。

使用@extend的关系。

这是最重要的用途之一@extend。样式表中选择器之间的关系可能变得非常复杂,并且@extend是完成工作的完美工具 - 您无法@mixin单独表达关系。选择器之间的关系与表示组合子,如>,+,~或白空间祖先组合子。CSS中可以存在许多不同类型的关系,即使不是那么明显的关系:

  • .parent > .child

  • .ancestor .descendant

  • .sibling ~ .sibling

  • .sibling + .adjacent-sibling

  • .sibling + * ~ .non-adjacent-sibling

  • .parent > * > .grandchild

  • .parent > * .non-child-descendant

  • .uncle ~ * > .nephew

  • * + *,任何相邻的兄弟关系,或lobotomized猫头鹰选择器

CSS使我们能够根据选择器与其他选择器的关系为选择器定义样式。这些使用组合器的关系定义选择器称为复选择器,它们比独立的简单或复合选择器部分具有更高的特异性。因此,定义为关系的样式比独立选择器中定义的样式具有更高的普遍性。

以下策略解决了如何扩展关系的深度。

通过占位符而不是类来定义关系。

还记得当你有两个祖先关系(例如.foo .bar {…} .baz .qux { @extend .bar; })时会发生什么?意识到多个扩展的祖先关系容易引起选择器爆炸,我们应该始终定义我们的关系,并保持它们是片面的。还有什么比%placeholder选择器更好地定义关系,以减少CSS输出?

假设您希望按钮在模态中的样式有点不同,并且您希望相邻按钮之间有一点空间。以下是使用占位符的关系:

%button {
  // … button styles
  + %button {
    margin-left: 1rem;
  }}%modal {
  // … modal styles
  %button {
    // … modal-specific button styles
  }}// "Exported" classes.button { @extend %button; }.modal { @extend %modal; }
复制

请注意,定义了两个关系:兄弟%button + %button关系和祖先%modal %button关系。这些关系在占位符中只定义一次,输出的CSS正是我们所期望的:

.button { /* … button styles */ }.button + .button { margin-left: 1rem; }.modal { /* … modal styles */ }.modal .button { /* … modal-specific button styles */ }
复制

在关系中代表每个占位符的一个选择器。

这遵循第一个策略:尽可能多地扩展选择器。在编写标记和CSS选择器时,尽可能少的选择器代表实体将阻止选择器爆炸,即使这意味着您必须向标记添加语义类以消除选择器。

%button { // … button styles }// Avoid this:button, input[type="button"], input[type="submit"], .button {
  @extend %button;}// Try this instead, and add the class .button// for every type of button in the HTML.button {
  @extend %button;}
复制

警惕后代和一般兄弟姐妹组合。

这两个组合子- .ancestor .descendant和.sibling ~ .general-sibling,分别为-是最容易选择的爆炸。为什么?他们的约束不如其他组合者严格。后代可以是孩子,孙子等。一般兄弟可以相邻,一个前面,两个前面等。

如果两个复杂的选择器都具有后代或一般兄弟组合器,则必须在输出的CSS中考虑所有可能性。这就是为什么你应该确保只有extendee或extender有一个后代或一般的兄弟组合,而不是两者。遵循上面关于仅从中扩展的指导%placeholders是强制执行此操作的好方法。

%modal {
  // … modal styles
  // Relationship: a %button that is a descendant of %modal
  %button { /* … modal-button styles */ }}// Don't do this!.modal {
  @extend %modal;
  // This is unnecessary - the relationship
  // is already defined!
  .button { @extend %button; }}// Do this - keep it simple!.modal { @extend %modal; }.button { @extend %button; }
复制

具有修饰符的单独占位符。

通过单独%placeholders表示修改后的组件,您可以扩展而不会产生混淆复合和/或复杂选择器的风险。例如,@extend %button%active;风险很大,但@extend %button-active;不是。

%button {
  // … button styles
  &-active { // same as %button-active
    // … active button styles
  }
  &-disabled {
    // … disabled button styles
  }}.button {
  @extend %button;
  &.active { @extend %button-active; }
  &:disabled { @extend %button-disabled; }}
复制

使用@extend内部类似媒体查询。

你可能在想,“我认为@extend在媒体查询中使用目前是不可能的。”这部分是正确的,但也有些误导。事实是,您只能在同一媒体查询中扩展选择器,并且,出于所有意图和目的,这就足够了。

然而,重要的是你的选择器是有意义的,因为你可以用适当的意图声明它们。如果您尝试在媒体查询中扩展外部选择器,那么这是一个明显的利益冲突 - 一个选择器的意图通常应用,而另一个选择器的意图仅应用于当前媒体上下文。

例如,由于媒体查询限制,下面的SCSS不起作用,更重要的是,因为意图不明确:

%panel {
  display: block;
  float: left;
  width: 100%;
  // … other panel styles}%panel-half { width: 50%; }.panel {
  @extend %panel;
  &.half { @extend %panel-half; }}// This will NOT work and will throw this error:// You may not @extend an outer selector from within @media.// You may only @extend selectors within the same directive.@media (max-width: 700px) {
  .panel-mobile {
    @extend %panel;
    &.half { @extend %panel-half; }
  }}
复制

那么,这里出了什么问题?在定义时%panel,意图是“这个面板应该具有这种样式”,而我们试图导出.panel-mobile为“此面板应仅在此媒体查询上下文中具有此样式。”这是一个明显的错误传达。

解决方案是将extendee意图与扩展器意图相匹配,尤其是在@media查询方面。换句话说,如果您希望扩展程序仅为特定媒体查询携带某些样式,则它应仅在同一@media查询中扩展,或者,也可以使用中断@media查询@at-root。

让我们看看如何使用以上两个建议修改我们的代码:

// Solution 1: Extend within the same media query%panel { /* … */ }%panel-half { /* … */ }// Define extendees with the same intent as extenders@media (max-width: 700px) {
  %panel-mobile { /* … */ }
  %panel-mobile-half { /* … */ }}.panel {
  @extend %panel; 
  &.half { @extend %panel-half; }
复制

以下策略解决了如何扩展关系的深度。

通过占位符而不是类来定义关系。

还记得当你有两个祖先关系(例如.foo .bar {…} .baz .qux { @extend .bar; })时会发生什么?意识到多个扩展的祖先关系容易引起选择器爆炸,我们应该始终定义我们的关系,并保持它们是片面的。还有什么比%placeholder选择器更好地定义关系,以减少CSS输出?

假设您希望按钮在模态中的样式有点不同,并且您希望相邻按钮之间有一点空间。以下是使用占位符的关系:

%button {
  // … button styles
  + %button {
    margin-left: 1rem;
  }}%modal {
  // … modal styles
  %button {
    // … modal-specific button styles
  }}// "Exported" classes.button { @extend %button; }.modal { @extend %modal; }
复制

请注意,定义了两个关系:兄弟%button + %button关系和祖先%modal %button关系。这些关系在占位符中只定义一次,输出的CSS正是我们所期望的:

.button { /* … button styles */ }.button + .button { margin-left: 1rem; }.modal { /* … modal styles */ }.modal .button { /* … modal-specific button styles */ }
复制

在关系中代表每个占位符的一个选择器。

这遵循第一个策略:尽可能多地扩展选择器。在编写标记和CSS选择器时,尽可能少的选择器代表实体将阻止选择器爆炸,即使这意味着您必须向标记添加语义类以消除选择器。

%button { // … button styles }// Avoid this:button, input[type="button"], input[type="submit"], .button {
  @extend %button;}// Try this instead, and add the class .button// for every type of button in the HTML.button {
  @extend %button;}
复制

警惕后代和一般兄弟姐妹组合。

这两个组合子- .ancestor .descendant和.sibling ~ .general-sibling,分别为-是最容易选择的爆炸。为什么?他们的约束不如其他组合者严格。后代可以是孩子,孙子等。一般兄弟可以相邻,一个前面,两个前面等。

如果两个复杂的选择器都具有后代或一般兄弟组合器,则必须在输出的CSS中考虑所有可能性。这就是为什么你应该确保只有extendee或extender有一个后代或一般的兄弟组合,而不是两者。遵循上面关于仅从中扩展的指导%placeholders是强制执行此操作的好方法。

%modal {
  // … modal styles
  // Relationship: a %button that is a descendant of %modal
  %button { /* … modal-button styles */ }}// Don't do this!.modal {
  @extend %modal;
  // This is unnecessary - the relationship
  // is already defined!
  .button { @extend %button; }}// Do this - keep it simple!.modal { @extend %modal; }.button { @extend %button; }
复制

具有修饰符的单独占位符。

通过单独%placeholders表示修改后的组件,您可以扩展而不会产生混淆复合和/或复杂选择器的风险。例如,@extend %button%active;风险很大,但@extend %button-active;不是。

%button {
  // … button styles
  &-active { // same as %button-active
    // … active button styles
  }
  &-disabled {
    // … disabled button styles
  }}.button {
  @extend %button;
  &.active { @extend %button-active; }
  &:disabled { @extend %button-disabled; }}
复制

使用@extend内部类似媒体查询。

你可能在想,“我认为@extend在媒体查询中使用目前是不可能的。”这部分是正确的,但也有些误导。事实是,您只能在同一媒体查询中扩展选择器,并且,出于所有意图和目的,这就足够了。

然而,重要的是你的选择器是有意义的,因为你可以用适当的意图声明它们。如果您尝试在媒体查询中扩展外部选择器,那么这是一个明显的利益冲突 - 一个选择器的意图通常应用,而另一个选择器的意图仅应用于当前媒体上下文。

例如,由于媒体查询限制,下面的SCSS不起作用,更重要的是,因为意图不明确:

%panel {
  display: block;
  float: left;
  width: 100%;
  // … other panel styles}%panel-half { width: 50%; }.panel {
  @extend %panel;
  &.half { @extend %panel-half; }}// This will NOT work and will throw this error:// You may not @extend an outer selector from within @media.// You may only @extend selectors within the same directive.@media (max-width: 700px) {
  .panel-mobile {
    @extend %panel;
    &.half { @extend %panel-half; }
  }}
复制

那么,这里出了什么问题?在定义时%panel,意图是“这个面板应该具有这种样式”,而我们试图导出.panel-mobile为“此面板应仅在此媒体查询上下文中具有此样式。”这是一个明显的错误传达。

解决方案是将extendee意图与扩展器意图相匹配,尤其是在@media查询方面。换句话说,如果您希望扩展程序仅为特定媒体查询携带某些样式,则它应仅在同一@media查询中扩展,或者,也可以使用中断@media查询@at-root。

让我们看看如何使用以上两个建议修改我们的代码:

// Solution 1: Extend within the same media query%panel { /* … */ }%panel-half { /* … */ }// Define extendees with the same intent as extenders@media (max-width: 700px) {
  %panel-mobile { /* … */ }
  %panel-mobile-half { /* … */ }}.panel {
  @extend %panel; 
  &.half { @extend %panel-half; }
  // This WILL work!
  @media (max-width: 700px) {
    &-mobile {
      @extend %panel-mobile;
      &.half { @extend %panel-mobile-half; }
    }
  }}
复制
// Solution 2: Break out of '@media' with '@at-root'%panel {
  display: block;
  float: left;
  // … other panel styles}// Separation of intent%panel-full { width: 100%; }%panel-half { width: 50%; }@media (max-width: 700px) {
  %panel-mobile-full { width: 100%; }
  %panel-mobile-half { width: 50%; }}.panel {
  @extend %panel;
  &.half { @extend %panel-half; }
  @media (max-width: 700px) {
    &-mobile, &-mobile-half {
      // General panel styling intent is maintained
      @at-root (without: media) { @extend %panel; }
    }
    &-mobile {
      @extend %panel-mobile-full;
      &.half { @extend %panel-mobile-half; }
    }
  }}
复制

使用@extend有了:matches()在未来。

有时候,很难遵守指南,你应该@extend尽可能少地选择一个选择器。以HTML标题为例:h1, h2, h3, h4, h5, h6。其中有六个,并且无法使用一个简单的选择器选择它们。一种解决方案是给他们所有的一类.heading,但如果你正在为未来建设,那就有更好的方法。

输入CSS 4的:matches()伪选择器。这个选择器允许你在一定程度上模仿@extend原生CSS中的功能,而不会出现选择器爆炸(我们已经学会了如何避免)。最好的部分是,Sass :matches()与相关的假选择器很好地配合。让我们的标题样式来证明:

// With :matches():matches(%heading) {
  // … heading styles}// Relationship: any heading that is a descendant of %section-foo%section-foo :matches(%heading) { /* … */ }h1, h2, h3, h4, h5, h6 {
  @extend %heading;}.section-foo { @extend %section-foo; }// CSS output - much better than this selector explosion:// .section-foo h1, .section-foo h2, .section-foo h3, .section-foo h4, .section-foo h5, .section-foo h6.section-foo :matches(h1, h2, h3, h4, h5, h6) {
  // … section-foo heading style}
复制

今天,你必须分别使用:-webkit-any()和:moz-any()替代:matches()WebKit浏览器和Firefox,直到它们原生支持:matches()。使用Internet Explorer,你运气不好(这并不奇怪)。在所有现代浏览器中实现CSS 4之前,这是一个很好的指导方针。

结论

该@extend指令无所畏惧,而且功能多样。了解如何@extend工作并牢记上述指导原则将使您能够@extend充分发挥其优势 - 无论您使用哪种导出选择器(类,属性等),都可以减少CSS输出并保持关系完好无损。明智地使用@mixin和@extend指令 - 它们意味着在你组织良好的样式表中共存。

潮州网站制作

最新案例

寒枫总监

来电咨询

400-6065-301

微信咨询

寒枫总监

TOP