Статьи

Почему вы должны избегать Sass @extend

Около года назад я написал Mixin или Placeholder (моя первая статья здесь, на SitePoint), а затем « Что никто не сказал вам о Sass Extend» . И вот, год спустя, я снова передумал и написал, почему я думаю, что директива @extend от Sass действительно далека от Эльдорадо.

TL; DR: расширение невидимо. Расширение не обязательно помогает вес файла, вопреки поговорке. Расширение не работает через медиа-запросы. Расширение не является гибким. У миксинов нет абсолютно никаких недостатков.

Но сначала я должен дать вам небольшое напоминание. @extend — это функция Sass, целью которой является предоставление селектору A возможности расширять стили из селектора B. При этом селектор A будет добавлен в селектор B, чтобы они оба использовали одни и те же объявления. Например:

 .selector-A { @extend .selector-B; unicorn: true; } .selector-B { rainbow: true; } 

Этот фрагмент Sass даст этот CSS:

 .selector-A { unicorn: true; } .selector-B, .selector-A { rainbow: true; } 

Это почти все, что вам нужно знать о @extend чтобы полностью оценить эту статью, так что теперь, когда мы рассмотрели основы, пришло время двигаться дальше. Есть немало причин, по которым я думаю, что @extend обманчива.

Однако не беспокойтесь, это еще не статья о том, как «Sass @extend может выводить невероятно длинные селекторы, если вы возитесь с этим» . Давайте попробуем копать глубже, не так ли?

Расширение невидимо

Sass это просто инструмент. Он предназначен, чтобы помочь разработчикам писать CSS. Однако в этом случае запись означает гораздо больше, чем просто написание кода. Это также означает поддерживать . Sass — это инструмент, помогающий людям вернуться к таблице стилей и обновить код, не тратя часов на то, чтобы понять, что происходит и где еще находится этот цвет # FA7A55.

Из-за этого Sass, как и любой другой инструмент, должен быть прозрачным. При написании строки Sass мы должны быть в состоянии выяснить, что из этого получится от CSS. Например, если я дам вам это:

 // _mixins.scss @mixin center($max-width) { max-width: $max-width; width: 100%; margin: 0 auto; } // main.scss .container { @include center(1170px); } 

Вы можете сказать мне, просто посмотрев на mixin, что полученный CSS будет:

 .container { max-width: 1170px; width: 100%; margin: 0 auto; } 

Это очень легко понять, потому что вы знаете, что включение миксина берет содержимое миксина, заменяет переменные заданными аргументами и печатает его в той самой позиции, в которую был включен миксин. Это на самом деле то, что круто с миксинами, они очевидны .

И это первая проблема с @extend . Всякий раз, когда вы решаете расширить селектор, вы абсолютно не представляете, что произойдет. Как вы знаете, результат может варьироваться от бездействия до катастрофических побочных эффектов. Расширенный селектор может присутствовать один раз (лучший сценарий) или несколько раз внутри составных селекторов, что было бы ужасно.

Теперь, очевидно, есть случаи, когда вы знаете, что расширенный селектор присутствует один и только один раз во всей таблице стилей, и в этом случае его вполне можно расширить. Это касается, например, помощников. Рассмотреть возможность:

 %clearfix::after { content: ''; display: table; clear: both; } 

Обычно это часть вашего файла _helpers.scss , включенная очень близко к верхней части таблицы стилей. %clearfix не будет частью какого-либо другого селектора. Когда-либо. Таким образом, расширение это довольно прозрачно: расширяющий селектор %clearfix::after поверх таблицы стилей, чтобы присоединиться к %clearfix::after .

В любом случае, это заставляет меня думать, что есть единственная уважительная причина для расширения селектора: это помощник, который существует только один раз во всей таблице стилей. И даже для этого … я думаю, что миксин все еще лучше.

Расширение не обязательно лучше, чем миксины в отношении производительности

Существует старый слух, что @extend лучше, чем mixins, по соображениям производительности, потому что они группируют селекторы, а не дублируют одни и те же объявления CSS снова и снова. Обычно говорят, что:

 %toolicorn { unicorn: rainbow; status: happiness; &::grandeur { level: infinite; } } .wanna-be-a-unicorn { color: hotpink; @extend %toolicorn; } .wanna-be-a-unicorn--too { color: deepskyblue; @extend %toolicorn; } 

… намного лучше для производительности, чем:

 @mixin toolicorn { unicorn: rainbow; status: happiness; &::grandeur { level: infinite; } } .wanna-be-a-unicorn { color: hotpink; @include toolicorn; } .wanna-be-a-unicorn--too { color: deepskyblue; @include toolicorn; } 

На бумаге это очень верно. Первый вывод CSS и короче, и, возможно, более элегантен, чем второй. @extend сами, это версия @extend (285 символов):

 .wanna-be-a-unicorn, .wanna-be-a-unicorn--too { unicorn: rainbow; status: happiness; } .wanna-be-a-unicorn::grandeur, .wanna-be-a-unicorn--too::grandeur { level: infinite; } .wanna-be-a-unicorn { color: hotpink; } .wanna-be-a-unicorn--too { color: deepskyblue; } 

И это версия на миксинах с 304 символами:

 .wanna-be-a-unicorn { color: hotpink; unicorn: rainbow; status: happiness; } .wanna-be-a-unicorn::grandeur { level: infinite; } .wanna-be-a-unicorn--too { color: deepskyblue; unicorn: rainbow; status: happiness; } .wanna-be-a-unicorn--too::grandeur { level: infinite; } 

Для такого небольшого примера разница весьма минимальна, однако она может быстро нарастить с большим содержанием для mixin / placeholder. Итак, еще раз, на бумаге имеет смысл думать, что расширение заполнителей, а не включение миксинов лучше для размера файла.

Однако файлы обычно подаются в виде gzip-файлов с сервера. Gzip основан на DEFLATE и LZ77; быстро поставить, чем больше строка повторяется, тем лучше для сжатия. Если поместить это в контекст, это означает, что различие, вероятно, будет несущественным, когда Gzip перевернет все это.

Это точно такой же спор, как и для дублированных медиа-запросов при помещении их в селекторы. Это не имеет значения, когда Gzip сделал свое дело:

… Мы выяснили, влияют ли на производительность сочетания средств массовой информации с рассеянием, и пришли к выводу, что, хотя в худшем случае разница, в худшем случае, минимальна, в лучшем случае, по сути, вообще отсутствует.
Сэм Ричардс в отношении точки останова

Таким образом, использование @extend вместо mixins из-за дублирования кода недопустимо. Двигаемся дальше.

Расширение невозможно в контексте СМИ

Вы знаете об этом, верно? Пытаясь расширить внешний селектор из директивы @media , Сасс задыхается и говорит:

Вы не можете @ расширять внешний селектор из @media.
Вы можете использовать только @extend селекторы в пределах одной директивы.

Были попытки заполнить это с помощью хакерских решений (включая мое, опубликованное здесь на SitePoint), но это не очень хорошая идея. Вы были бы дураком, чтобы использовать такие вещи в производстве.

Причины, по которым мы не можем распространяться на разные медиа-контексты, на данный момент носят чисто технический характер. Тем не менее, это довольно раздражающее занятие. Чтобы обойти эту проблему, менее худшая идея — обернуть заполнители миксином, чтобы вы могли выбрать либо @extend либо включить, в зависимости от того, @extend ли вы в блоке @media или нет. Я описал эту технику здесь, и это все еще то, что я использую на работе. Быстрое подтверждение концепции:

 // _helpers.scss @mixin clearfix($extend: true) { @if $extend == true { @extend %clearfix; } @else { &::after { content: ''; display: table; clear: both; } } } %clearfix { @include clearfix($extend: false); } // main.scss .container { @include clearfix; } @media (min-width: 42em) { .my-other-selector { @include clearfix($extend: false); } } 

Насколько я знаю, он работает нормально и не вызывает особых сложностей, но в некотором смысле это еще один уровень абстракции. Тем не менее, код может показаться немного сложным для тех, кто начинает работать с Sass.

В любом случае, вы не можете распространять селектор на разные медиа-директивы, и это просто отстой. По этой причине, возможно, стоит перейти к настройке на основе миксинов, где стили могут повторяться (see section Расширение не обязательно лучше, чем миксины в отношении производительности ), но вы можете делать все, что захотите, когда захотите, так, как вы хотите.

Расширение не является гибким

По их собственному определению, миксины делают более мощную функцию, чем @extend . Действительно, они:

Все эти причины делают их предпочтительной, если вы стремитесь к гибкости. Очень легко добавить дополнительный аргумент в миксин, потому что он нужен для того, чтобы делать больше, без какого-либо нарушения API.

С другой стороны, @extend довольно строг. Если вам нужно, чтобы ваш фрагмент кода принимал переменную, необходимо заменить заполнитель на миксин, а затем обновить все правила, расширяющие этот заполнитель, чтобы вместо него включать миксин. Не только довольно утомительно, но, конечно, не очень гибко.

Последние мысли

Так что @extend отстой, не так ли? Я не уверена. Возможно, я просто недостаточно квалифицирован, чтобы увидеть реальный потенциал этой функции, но, похоже, я не единственный, кто понимает, что @extend на самом деле не является убийцей. Действительно, я предлагаю вам прочитать эти статьи, чтобы продвинуться дальше:

Тем не мение. @extend может быть грязным. Мой совет — избегать этого, когда это возможно, и использовать вместо этого миксины, потому что в конце концов это не имеет никакого значения.