CSS-live.ru

Если вы знаете Sass — вы знаете и ES2015

Перевод статьи If You Know Sass, You Know ES2015 с сайта una.im, опубликовано на css-live.ru с разрешения автора — Юны Кравец.

Если вы знаете Sass — вы знаете и ES2015!… ну не то чтобы совсем, но на удивление есть много общего! Изучая последнюю спецификацию ECMAScript, ES2015 (ранее известную как ES6), я наткнулась на несколько функций, котрые тут же узнала по Sass. И я продолжана находить всё больше и больше общего. Множество идей здорово переносятся из разработки на Sass, и если вы хоть немного с ней знакомы, то, возможно, вы намного ближе к пониманию современного JavaScript, чем думаете, и наоборот.

sass-js

Язык Sass и ES2015

Язык Sass был написан очень системно. Изначально он был выпущен в 2006 и написан на Ruby, имитируя его синтаксис и некоторые из его языковых решений. Из числа других особенностей, в версии 3.0.0 появились точки с запятой и фигурные скобки, что сделало Sass схожим (и совместимым) с CSS. Это изменение, плюс расширяемая функциональность ядра Sass под названием SassScript, означали, что теперь появился надёжный язык программирования, теперь еще и совместимый с CSS. Оформление стало гораздо мощнее и двери программирования распахнулись перед более широкой аудиторией разработчиков и дизайнеров. К сожалению разработчиков Sass по-прежнему считают «ненастоящими разработчиками». Надеюсь, эта статья преодолеет такое предубеждение.

Una Kravets (@Una)твит.

Чем больше я узнаю о фичах ES6, тем больше нахожу сходства с @SassCS.

(т.е. объекты map, классы, интерполяция, конструкторы)

Я сошла с ума?

Каждое решение языка Sass тщательно обдумывается, предпочитая настраиваемости ясность (причина, по которой в Sass до сих пор нет пользовательских свойств, в отличие от PostCSS). Новая спецификация ECMAScript 2015 (ES2015), вышедшая в июне 2015, привнесла множество новых и крайне полезных фич в JavaScript, что тоже сделало его чище и проще в использовании.

Хотя большинство браузеров всё ещё и не поддерживают ES2015, можно воспользоваться транспилером вроде Babel, чтобы преобразовать код в совместимый ES5. Этап предварительной компиляции (да, я знаю, что он в них не совсем одинаковый) — лишь начало сходств двух языков.

Теперь в ES2015 реализовано множество фич Sass, с которыми вы, сами того не ведая, уже знакомы как Sass-разработчик, и наоборот — вам знакомо многое в Sass, если вы JS-разработчик.

1. Интерполяция строк-шаблонов

Благодаря этой фиче ES2015 я впервые поняла, насколько похожи эти две спецификации. Интерполяция строк практически идентична (что доставило мне кучу радости). По существу, ES2015 ввёл потрясающую новую фичу использования строк как шаблонов на основе обратных кавычкек.

Ого. Определение интерполяции на Dictionary.com полный отстой. В данном случае, это по сути способ ссылаться на строковую переменную с учетом её настоящего значения.

Это значит, что больше не придётся использовать внешний шаблонизатор для базовых манипуляций со строками и статической DOM (в большинстве случаев). И это здорово! Ведь код становится единообразнее.

Выглядит это примерно так  — вместо:

"First Name: " + fName + "\nLast Name: " + lName

Можно просто написать:

`First Name: ${fName}
Last Name: ${lName}

В точности, как и в Sass! Учтите, что в Sass переменные выглядят так: $varName, а поскольку $ уже занят, то для обозначения интерполяции он использует #.

//.js

`variable: ${varName}`
//.scss

"variable: #{$varName}"

Интерполяция строк очень удобна при написании миксинов Sass и написании любого рода вывода в DOM (т.е. через CSS-свойство content). В дальнейшем примере мы увидим (ассоциативные массивы), почему это так важно и распространено в разработке на Sass.

2. Значение по умолчанию для аргумента

Значение по умолчанию для аргумента особенно полезно, когда дело доходит до создания гибких функций (и миксинов, в случае с Sass!). Раньше я часто использовала их в Sass, чтобы предопределить значение по умолчанию для аргумента, когда оно не указано в функции или миксине, давая возможность написать собственное значение или пропустить его, полагаясь на значение по умолчанию. А теперь всё это есть и в JavaScript!

Теперь при создании экземпляра фукнции можно установить переменной значение по умолчанию прямо в списке аргументов (как и в Sass):

//.js

function sayHello(recipient = "красотка") {
  return "Привет, " + recipient;
}

sayHello();
sayHello("солнышко");

/*консоль*/

> Привет, красотка
> Привет, солнышко

//.scss

@mixin showOutline($color: #f00) {
  outline: 1px solid $color;
}

.one {
  @include showOutline()
}

.two {
  @include showOutline(#ccc)
}

/*вывод css*/

.one {
  outline: 1px solid #f00;
}

.two {
  outline: 1px solid #ccc;
}

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

3. Оставшиеся параметры

Как и в случае с аргументами функций/миксинов, оставшиеся параметры в ES2015 похожи на типы аргументов arglist в Sass! При написании функции, оставшиеся параметры (как и arglist) также должны стоять в конце серии аргументов.

Оставшиеся параметры создаются при использовании оператора расширения (…) в определении функции (про него есть статья на нашем сайте — прим. перев.). Вы можете припомнить оператор расширения по перебору элементов в массиве, но при использовании в определении функции он ведёт себя немного иначе.

Вы используете списки аргументов (arglist-ы) или оставшиеся параметры, когда на входе может оказаться неизвестное количество аргументов. Например, в Sass это обычно применяется для теней или градиентов. В JavaScript оставшиемся параметры обозначаются в виде ... перед аргументом, а в Sass — после аргумента.

//.js

function PrintMe(firstArg, ...theRest) {
  console.log(firstArg, theRest);
}

PrintMe('Привет', 'ты', 'выглядишь', 'мило');

/* консоль */
> Привет ["ты", "выглядишь", "мило"]
//.scss

@mixin funCircle($size, $gradient...) {
  width: $size;
  height: $size;
  border-radius: 50%;
  background: radial-gradient($gradient);
}

.entrancing {
  @include funCircle(50px, blue 10%, red 80% ,pink);
}
/*вывод css*/

.entrancing {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background: radial-gradient(blue 10%, red 80%, pink);
}

В обоих случаях значения списка аргументов/оставшегося параметра собираются и возвращаются все вместе в нужный момент, ничего не поломав.

4. Цикл «for-of»

Новая структура перебора для массива в ES2015 весьма схожа со структурой перебора в Sass. ES2015 ввёл цикл for-of. Возможно вы спросите, чем это отличается от for-in?

Итератор цикла for-of ссылается не на индекс массива, а на его значение. Он гибче и позволяет перебирать данные (значения в массиве), а не свойства объекта (как делает for-in). Из-за этого ограничения, одна из проблем for-in — его нельзя прерывать или вернуться из него.

С Sass любой перебор работает как for-of, а не как for-in, будь то ассоциативный массив, список или список списков. Хотя синтаксис и отличается (each-in против for-of), механизм перебора тот же:

//.js

let colorArray = ["красный", "желтый", "зелёный"];

for (let color of colorArray) {
  console.log(color);
}
//.scss

$colorList: "красный", "желтый", "зелёный";

@each $color in $colorList {
  @debug $color;
}

Если использовать for (let color in colorArray) (in вместо of) , то результат будет 1, 2, 3, а не красный, жёлтый, зелёный. В этом и состоит наибольшая разница в работе этих циклов.

Оператор @debug в Sass (показанный выше для краткости) очень похож на console.log, хотя таким образом вы могли также вернуть (@return) значения. Однако, эти циклы обычно используются для генерирования множественных классов и стилей (в следующем разделе я покажу пример для этого случая)

5. Ассоциативные массивы

Последняя спецификация ECMAscript вводит новую структуру данных: ассоциативные массивы, или объекты map! Эти объекты отлично подходит для конфигурации и позволяют обращаться к своим элементам в паре key : value. Чем они отличаются от объектов? Хороший вопрос. С объектами, ключи всегда приводятся к строкам. С объектами map, значениям любого типа необязательно приводиться к строкам. Объекты по-прежнему следует использовать при сочетании типов значений и когда вы знаете все значения до процесса выполнения, в остальных случаях весьма полезными окажутся объекты map.

Ассоциативные массивы также можно перебирать! Так что для получания значения (что также выполнимо в Sass!) можно использовать вышеописанный синтаксис (в цикле for-of). Вот простой пример:

//.js

let colorMap = new Map();

colorMap.set("первичный", "красный");
colorMap.set("вторичный", "жёлтый");
colorMap.set("третичный", "зелёный");

for (let [key, value] of colorMap) {
  console.log(`key: ${key}, value: ${value}`);
}

/*консоль*/

> key: первичный, value: красный
> key: вторичный, value: жёлтый
> key: третичный, value: зелёный
//.scss

$colorMap: (
  "primary": "red",
  "secondary": "yellow",
  "tertiary": "green"
);


@each $key, $value in $colorMap {
  .color--#{$key} {
    color: #{$value};
  }
}
/*вывод css*/

.color--primary {
  color: red;
}

.color--secondary {
  color: yellow;
}

.color--tertiary {
  color: green;
}

Возможно вы заметили, что в примере с Sass я использую такой синтаксис интерполяции (т.е. #{$value} вместо простого $value). Причина в том, что с последним Sass выведет строку в результат: (т.е. color: "green" вместо color: green). В CSS это не работало бы.

Sass предоставляет ещё несколько приятных мелочей для ассоциативных массивов, например, функция map-get, которая обычно используется для ссылки на их значения, и map-has-key, которая весьма полезна для отладки.

//.scss

@if map-has-key($colorMap, $color) {
    $color: map-get($colorMap, $color);
} @else {
  @if type-of($color) != color {
    @error "Недопустимое название цвета: `#{$color}`.";
  }
}

sassmaps1

Функции ассоциативных массивов в Sass поразительно надёжные.

В Javascript также есть методы объекта map: get и set! Просто вместо get() используется map-get():

//.js

var colorMap = new Map();
colorMap.set("первичный", "красный");

colorMap.get("первичный");
// красный

Можно воспользоваться Sass (как и JS), чтобы удалять из ассоциативных массивов, объединять их, сжимать и т.д. С ними можно здорово организовать код, от информации об объекте до цветов и z-индексов.

6. Расширения классов

И ещё одна важная штука, ES2015 вводит классы (ну, не настоящие классы, а немного синтаксического «сахара» над прототипами, которые позволяют делать вид, будто мы пишем классы). С классами пришли extends! (Звучит знакомо?)

Их поведение весьма схоже с директивой @extend в Sass. В Javascript extends позволяют создать новый класс, который наследет методы и свойство их родительского класса. (Кроме того, метод super () позволяет обращаться к методам родителя нового класса). Например, в случае class Corgi extends Dog) Corgi может обратиться к методу bark() Dog с помощью super.bark().

Можно также запустить это в конструкторе для создания экземпляра со всеми родительскими методами. Таким образом extends работает как @extend!

Запутались? Это проще показать:

//.js

class Dog {
  constructor(name) {
    this.name = name;
  }

  bark(words) {
    console.log(`Гав!!! ${words} Гав!`);
  }
}

class Corgi extends Dog {
  constructor() {
    super();
  }

  waddle() {
   // Корги ковыляет
  }
}

//Стив - это корги
let steve = new Corgi;

//Стив может лаять, как собака
steve.bark('Я люблю бекон!')

//И Стив тоже может ковылять!

/*console*/


> Гав!!! Я люблю бекон! Гав!
//.scss

.plain-link {
  font-weight: 600;
  text-decoration: none;
  border-bottom: 2px dotted currentColor;
}

.pink-link {
  @extend .plain-link;
  color: hotpink;
}

.blue-link {
  @extend .plain-link;
  color: blue;
}
/*вывод css*/

.plain-link, .pink-link, .blue-link {
  font-weight: 600;
  text-decoration: none;
  border-bottom: 2px dotted currentColor;
}

.pink-link {
  color: hotpink;
}

.blue-link {
  color: blue
}

Здесь показано основное различие между миксинами @extend и @include в Sass. С @extend мы захватываем все стили из .plain-link и делимся ими с каждым элементом, которые расширяют этот класс — похоже на то, как методы JS извлекаются из родительского класса в примере с JavaScript. Затем можно добавить уникальные свойства (в случае с Sass это цвет; а в JavaScript — метод waddle), недоступные в родительских, но доступные в дочерних классах, которые расширяют их.

Специально для любопытных, здесь я подробно написала про @extend и о том, как я систематически использую их в Sass.

Хотя некоторые сравнения и натянуты, есть вполне реальные сходства в действии между Sass и JavaScript, особенно с некоторыми новыми фичами ES2015.

Надеюсь, вам понравилась эта статья и вы узнали что-то новое!

P.S. Это тоже может быть интересно:

5 комментариев

    1. В какой-то мере оно так, наверное, все современные языки заимствуют удобные «плюшки» друг у друга. Но то именно языки программирования, а тут, казалось бы, чисто оформительский инструмент, от программирования далекий — но как раз его привычные приемы оказываются на удивление похожи на приемы программирования, причем новые (и наоборот).

      Всё-таки здорово умеет Юна подмечать такие вот неожиданные аналогии в совершенно разных областях, в другой статье (по ссылке в последнем курсивном абзаце, про @extend) она тоже этим активно пользуется:). Даже если, как признает она сама, аналогия выходит натянутой, сам по себе такой свежий взгляд с неожиданной стороны бывает очень ценен.

      1. Справедливо, но в тот же SASS эти плюшки пришли из языков программирования, так что можно сразу смотреть на первоисточник.

        1. Насколько я понял, один из тезисов Юны — перестать противопоставлять Sass и «настоящие ЯП» и рассматривать его как очень узкоспециализированный, но де-факто тоже ЯП. По-моему, в наш век переменны пользовательских свойств в CSS, стилей в JS, декларативных компонентов в разметке и др. новшеств, всё сильнее размывающих и так зыбкие грани между дизайном, версткой и программированием… в таком подходе что-то есть:)

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.