С помощью встроенных стилей можно получить все программные возможности JavaScript, что дает нам преимущества в виде предварительного процессора CSS (переменные, примеси и функции), а также помогает решить множество проблем, возникающих в CSS, таких как конфликт пространства имен и применения стилей.


Чтобы получить больше информации о проблемах CSS, решаемых в JavaScript, вы можете посмотреть презентацию «React CSS в JS» (React CSS in JS), а для того чтобы изучить улучшение производительности с помощью Aphrodite, прочитайте статью Inline CSS at Khan Academy: Aphrodite. Если же вы хотите узнать больше о лучших практиках CSS в JavaScript, ознакомьтесь с руководством Airbnb (Airbnb’s styleguide).


Здесь речь пойдет об использовании встроенных стилей JavaScript для создания компонентов, позволяющих решить основные проблемы дизайна, о которых я рассказывал ранее в статье «Прежде чем осваивать дизайн, необходимо ознакомиться с основами» (Before you can master design, you must first master the fundamentals).


Мотивирующий пример


Начнем с простого примера: создание и стилизация кнопки. Как правило, компонент и связанные с ним стили находятся в одном файле: Button и ButtonStyles. Причина в том, что они относятся к одной и той же вещи — к представлению (view). Однако в примере я разбил код на несколько составляющих, чтобы сделать его более наглядным.


Рассмотрим элемент кнопки:


...

function Button(props) {
  return (
    <input
      type="button"
      className={css(styles.button)}
      value={props.text}
    />
  );
}

Ничего необычного — просто React-компонент. Свойство className — вот где Aphrodite вступает в игру. Функция CSS принимает объект styles и преобразует его в CSS. Объект styles создается с помощью функции StyleSheet.create Aphrodite ({...}). Вы можете посмотреть результат StyleSheet.create ({...}) на странице Aphrodite (Aphrodite playground).


Ниже приведена таблица стилей кнопок:


...

const gradient = 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)';

const styles = StyleSheet.create({
  button: {
    background: gradient,
    borderRadius: '3px',
    border: 0,
    color: 'white',
    height: '48px',
    textTransform: 'uppercase',
    padding: '0 25px',
    boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .30)',
  },
});

Простая миграция и низкая кривая обучения — вот преимущества Aphrodite. Свойства border-radius преобразуются в borderRadius, а значения становятся строками. Псевдоселекторы, медиазапросы и определения шрифтов — все работает. Кроме того, автоматически добавляются вендорные префиксы.


В результате получаем:


image
Простая миграция и низкая кривая обучения — преимущества Aphrodite


Давайте рассмотрим на этом примере, как Aphrodite может использоваться для создания базовой системы визуального проектирования. Сосредоточимся на таких основных принципах дизайна, как типографика и интервал.


Основной принцип № 1. Типографика


Начнем с фундаментальной основы дизайна — типографики. Для начала необходимо определить ее константы. В отличие от Sass или Less, константы для Aphrodite могут быть в файлах JavaScript или JSON.


Определение констант типографики


При создании констант используйте семантические имена для переменных. Например, для названия одного из размеров шрифта необходимо имя, описывающее его роль, наподобие displayLarge, а не h2. Аналогично, чтобы обозначить одно из значений жирности, берите название вроде semibold, описывающее эффект, а не значение 600.


export const fontSize = {
  // heading
  displayLarge: '32px',
  displayMedium: '26px',
  displaySmall: '20px',
  heading: '18px',
  subheading: '16px',

  // body
  body: '17px',
  caption: '15px',
};

export const fontWeight = {
  bold: 700,
  semibold: 600,
  normal: 400,
  light: 200,
};

export const tagMapping = {
  h1: 'displayLarge',
  h2: 'displayMedium',
  h3: 'displaySmall',
  h4: 'heading',
  h5: 'subheading',
};

export const lineHeight = {
  // heading
  displayLarge: '48px',
  displayMedium: '48px',
  displaySmall: '24px',
  heading: '24px',
  subheading: '24px',

  // body
  body: '24px',
  caption: '24px',
};

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


Более подробно о вертикальном ритме вы можете прочитать в статье «Почему вертикальный ритм — это важная практика типографики?» (Why is Vertical Rhythm an Important Typography Practice?).


image
Используйте калькулятор для определения высоты линии


Существует целая наука о выборе значений для строк и размеров шрифта. Для создания потенциальных размерных вариантов могут использоваться математические соотношения. Несколько недель назад я опубликовал статью «Типографика может создать или разрушить дизайн: выбор шрифта» (Typography can make or break your design: a process for choosing type), в которой подробно изложена методология. Для определения размеров шрифтов используйте модульную шкалу (Modular Scale), а чтобы установить высоту линий, можно применять калькулятор вертикального ритма (vertical rhythm calculator).


Определение компонента заголовка


После того как мы определили константы типографики, необходимо создать компонент, который будет использовать эти значения. Цель такого компонента — обеспечить согласованность в разработке и реализации заголовков кодовой базы.


import React, { PropTypes } from 'react';
import { StyleSheet, css } from 'aphrodite/no-important';
import { tagMapping, fontSize, fontWeight, lineHeight } from '../styles/base/typography';

function Heading(props) {
  const { children, tag: Tag } = props;
  return <Tag className={css(styles[tagMapping[Tag]])}>{children}</Tag>;
}

export default Heading;

export const styles = StyleSheet.create({
  displayLarge: {
    fontSize: fontSize.displayLarge,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.displayLarge,
  },
  displayMedium: {
    fontSize: fontSize.displayMedium,
    fontWeight: fontWeight.normal,
    lineHeight: lineHeight.displayLarge,
  },
  displaySmall: {
    fontSize: fontSize.displaySmall,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.displaySmall,
  },
  heading: {
    fontSize: fontSize.heading,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.heading,
  },
  subheading: {
    fontSize: fontSize.subheading,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.subheading,
  },
});

Компонент Heading — это функция, которая принимает тег как свойство и возвращает его со связанным стилем. Это стало возможным благодаря тому, что ранее мы определили сопоставления тегов в файле констант.


...
export const tagMapping = {
  h1: 'displayLarge',
  h2: 'displayMedium',
  h3: 'displaySmall',
  h4: 'heading',
  h5: 'subheading',
};

В нижней части файла компонента мы определяем объект styles. Здесь используются константы типографики.


export const styles = StyleSheet.create({
  displayLarge: {
    fontSize: fontSize.displayLarge,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.displayLarge,
  },

  ...
});

А компонент Heading будет применяться следующим образом:


function Parent() {
  return (
    <Heading tag="h2">Hello World</Heading>
  );
}

При таком подходе мы снижаем вероятность возникновения неожиданной изменчивости в системе типов. Устраняя потребность в глобальных стилях и стандартизируя заголовки кодовой базы, мы избегаем проблем с различными размерами шрифтов. К тому же подход, который мы использовали при построении компонента Heading, можно применить и для построения компонента Text основного тела.


Основной принцип № 2. Интервал


Поскольку в дизайне интервал управляет вертикальным и горизонтальным ритмом, он играет первостепенную роль в создании системы визуального проектирования. Здесь, как и в разделе типографики, для начала необходимо определить константы интервала.


Определение интервальных констант


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


const spacingFactor = 8;
export const spacing = {
  space0: `${spacingFactor / 2}px`,  // 4
  space1: `${spacingFactor}px`,      // 8
  space2: `${spacingFactor * 2}px`,  // 16
  space3: `${spacingFactor * 3}px`,  // 24
  space4: `${spacingFactor * 4}px`,  // 32
  space5: `${spacingFactor * 5}px`,  // 40
  space6: `${spacingFactor * 6}px`,  // 48

  space8: `${spacingFactor * 8}px`,  // 64
  space9: `${spacingFactor * 9}px`,  // 72
  space13: `${spacingFactor * 13}px`, // 104
};

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


Золотое сечение(1:1.618)
8.0 x (1.618 ^ 0) = 8.000
8.0 x (1.618 ^ 1) = 12.94
8.0 x (1.618 ^ 2) = 20.94
8.0 x (1.618 ^ 3) = 33.89
8.0 x (1.618 ^ 4) = 54.82
8.0 x (1.618 ^ 5) = 88.71

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


const spacingFactor = 8;
export const spacing = {
  space0: `${computeGoldenRatio(spacingFactor, 0)}px`,  // 8
  space1: `${computeGoldenRatio(spacingFactor, 1)}px`,  // 13
  space2: `${computeGoldenRatio(spacingFactor, 2)}px`,  // 21
  space3: `${computeGoldenRatio(spacingFactor, 3)}px`,  // 34
  space4: `${computeGoldenRatio(spacingFactor, 4)}px`,  // 55
  space5: `${computeGoldenRatio(spacingFactor, 5)}px`,  // 89
};

function computeGoldenRatio(spacingFactor, exp) {
  return Math.round(spacingFactor * Math.pow(1.618, exp));
}

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


Например, добавим marginBottom к компоненту Button.


import { spacing } from '../styles/base/spacing';

...

const styles = StyleSheet.create({
  button: {
    marginBottom: spacing.space4, // adding margin using spacing constant
    ...
  },
});

Это работает в большинстве случаев. Однако что произойдет, если мы решим изменить свойство кнопки marginBottom в зависимости от ее расположения?


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


import React, { PropTypes } from 'react';
import { spacing } from '../../base/spacing';

function getSpacingSize(size) {
  return `space${size}`;
}

function Spacing(props) {
  return (
    <div style={{ marginBottom: spacing[getSpacingSize(props.size)] }}>
      {props.children}
    </div>
  );
}

export default Spacing;

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


Это работает за счет таких компонентов, как кнопки, инпуты и карточки (cards), которые могут нуждаться в margin, заданном переменными. Например, для кнопки формы может потребоваться большее поле, чем для кнопки панели навигации.


Вы, наверное, заметили, что в приведенных примерах используется только marginBottom. Это связано с тем, что определение всех вертикальных полей в одном направлении позволяет избежать их сбрасывания, а также дает возможность отслеживать вертикальный ритм дизайна. Узнать об этом больше вы можете из статьи Гарри Роберта «Описание одностороннего поля» (Single-direction margin declarations).


Также вы можете использовать интервальные константы, которые были определены как отступы.


import React, { PropTypes } from 'react';
import { StyleSheet, css } from 'aphrodite/no-important';
import { spacing } from '../../styles/base/spacing';

function Card(props) {
  return (
    <div className={css(styles.card)}>
      {props.children}
    </div>
  );
}

export default Card;

export const styles = StyleSheet.create({
  card: {
    padding: spacing.space4}, // using spacing constants as padding

    background: 'rgba(255, 255, 255, 1.0)',
    boxShadow: '0 3px 17px 2px rgba(0, 0, 0, .05)',
    borderRadius: '3px',
  },
});

С одинаковыми интервальными константами для полей и для отступов можно добиться большей визуальной согласованности в дизайне.


Результат выглядит следующим образом:


image
С одинаковыми интервальными константами для полей и для отступов можно добиться большей визуальной согласованности в дизайне


Теперь, когда у вас есть понимание CSS в JavaScript, можете смело экспериментировать. Попробуйте включить встроенные стили JavaScript в свой следующий проект. Думаю, вы по достоинству оцените возможность решать все проблемы стиля и представления (view), работая в одном контексте.

Поделиться с друзьями
-->

Комментарии (140)


  1. spmbt
    31.05.2017 16:43
    +1

    Aphrodite — это, получается, исток более мощного формата, чем JSX, некого файла с расширением (в будущем) *.JS_CSSX.

    Почему так?

    Сначала в React придумали написать HTML в файлах *.JS. Получилось, как всегда, трудночитаемо, хотя старались сделать максимум читабельности, как в Aphrodite. Следующим шагом был *.JSX. А CSS нормально уживался в JSX, потому что это — те же теги. Но когда появляется желание внести в инлайновые стили переменные (а оно появляется, и решают его через переменные Less/Sass/Stylus), появляется закономерно Aphrodite. Дальше смотрим, и видим, что что-то не то, как и ранее с HTML в JS. Дальше кто-то финансирует *.JSX, какая-то конторка, её стараются поругать, назвать костылями, неосиливателями своего же нативного кода (компоненты на *.JS). Но больше тех, кто молча уминает с удовлетворением.

    И это — над React, не его часть. Вот, к примеру, есть плагин JSX для Vue.


    1. kafeman
      31.05.2017 19:50

      JSX — достаточно удобное решение, при условии, что в него перестанут пихать бизнес-логику (идеально — исключительно функциональные stateless-компоненты, что достигается использованием redux или flux).

      Вероятно, можно было бы избежать такого количества хейта, назвав технологию не JSX, а HTMLX.


      1. TheShock
        31.05.2017 20:49
        -1

        в него перестанут пихать бизнес-логику (идеально — исключительно функциональные stateless-компоненты, что достигается использованием redux или flux).

        И куда ее тогда пихать, если компоненты — без логики, а редакс-слой в идеале тоже логики не содержит, только редьюс? Или редакс — только для приложений без логики?


        1. kafeman
          31.05.2017 21:11
          +1

          В контексте связки React + Redux, есть «компоненты», которые не имеют ни состояния, ни бизнес-логики, а только принимают на вход свойства и выдают на выходе DOM. Есть «контейнеры», которые сами никакого JSX не содержат, но умеют правильно внедрять глобальное состояние и действия в «компоненты».

          Соответственно, бизнес-логика распределяется между действиями и контейнерами. Последние, как правило, содержат логику на уровне «взять event.target.value и использовать его как аргумент name у действия changeName». Если бизнес-логика простая (отправить несложный HTTP-запрос к REST API), то можно оставить ее в действиях (точнее, action creators). Если логика сложная, то можно вынести ее в отдельный класс типа PostService, получив классическое Multitier-приложение (но ни Redux, ни React, тут уже ни при чем).


          1. TheShock
            31.05.2017 21:18

            В контексте связки React + Redux, есть «компоненты», которые не имеют ни состояния, ни бизнес-логики, а только принимают на вход свойства и выдают на выходе DOM. Есть «контейнеры», которые...

            На самом деле компонентами называют и те, и те, а они уже делятся на Presentational и Container. Хотя, де-факто, это просто хипстерские названия для обычных View и толстого Controller'а.

            Что я не понял — зачем вы в оригинальном комментарии упомянули о Редаксе, если такое разделение возможно и без него.


            1. kafeman
              31.05.2017 21:36

              Теоретически, конечно, возможно. Я, кстати, тоже исходил в своих объяснениях из упомянутой вами статьи. На практике, вы все-равно либо придете к Redux, либо напишете свой аналог.

              толстого Controller'а
              Где же он «толстый», если это просто инжектор зависимостей, занимающий, как правило, десяток строк? Реальной бизнес-логики в нем нет (а то, где она должна быть, вы решаете уже сами — я предлагаю классическое разделение на UI-, Service- и Domain-layer).


              1. TheShock
                31.05.2017 21:53
                -1

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

                На практике, вы все-равно либо придете к Redux, либо напишете свой аналог

                Либо использование MobX


                1. kafeman
                  31.05.2017 22:08

                  Я не считаю себя сторонником ФП, но когда есть возможность описать представление «чистыми» функциями, то я ей пользуюсь. В конце концов, можно посадить «хипстеров» писать UI-слой, а бизнес-логику отдать суровому enterprise'у, и транспилировать ее потом из какой-нибудь Java (привет, GWT!). Для извращенцев в Java 8 добавлен новый JavaScript-движок, так что можно повторить процесс и в обратном направлении.


                  1. TheShock
                    31.05.2017 22:15

                    ООП, кстати, не отрицает чистоту, а очень даже поощряет в необходимых мерах.


                    1. wheercool
                      31.05.2017 23:24
                      -1

                      Интересно каким образом?


                      1. TheShock
                        31.05.2017 23:34

                        А что мешает в ООП создавать чистые методы или даже классы, которые делают свою работу и умирают?


                        1. raveclassic
                          31.05.2017 23:35
                          -1

                          Но зачем тогда ООП? Класс со стейтом противоречит идее «чистоты». Чистый метод не привязан к инстансу, зачем его тогда держать в классе?


                          1. kafeman
                            31.05.2017 23:55

                            С натягом, можно притянуть сюда const-методы из C++:

                            class Hello {
                            public:
                                Hello() : value(6) {}
                            
                                int GetValue() const { return value; }
                            
                            private:
                                int value;
                            };
                            

                            Формально, это как-бы «чистая функция», ведь она возвращает одинаковый результат для побайтово равных объектов (в данном случае, для всех, потому что Hello::value не меняется).


                            1. raveclassic
                              01.06.2017 00:00

                              Я, к сожалению, не могу похвастаться экспертизой в плюсах и слабо понимаю, что тут вообще написано, полькольку большую часть времени пишу на js и компании, но если уж ничего нигде не меняется, то я напишу const HelloValue = 6;.


                              1. kafeman
                                01.06.2017 00:14

                                В C++ у каждого метода есть «скрытый» аргумент *this. Модификатор const после названия метода сообщает компилятору, что this ссылается на константу, т. е. создается контракт, согласно которому функция Hello::GetValue не изменяет внутреннего состояния объекта.

                                Это можно «притянуть» к тому, что для двух равных объектов метод всегда вернет одинаковый результат. Точно также, как какая-нибудь

                                int foo(int a) { return a + 1; }
                                
                                вернет одинаковое значение для одинаковых a.


                          1. TheShock
                            01.06.2017 00:20

                            Но зачем тогда ООП?

                            А затем, что он не заставляет использовать чистоту там, где она не нужна, а нужен изменяемый стейт.

                            Класс со стейтом противоречит идее «чистоты».

                            Частично, ведь сам по себе он не чистый, но вы иногда можете использовать его чисто.

                            Чистый метод не привязан к инстансу, зачем его тогда держать в классе?

                            Почему вы мыслите столь ограничено? Почему чистый метод не привязан к инстансу?


                            1. raveclassic
                              01.06.2017 00:31

                              Почему вы мыслите столь ограничено? Почему чистый метод не привязан к инстансу?
                              Ну почему ограниченно? Я просто стараюсь минимизировать количество сущностей и абстракций. JS предоставляет модули, в которых я могу складывать все, что ни попадя, в том числе и эти чистые функции и константы. Зачем наворачивать вокруг них лишние сущности в виде классов?
                              Вы, скорей всего, ошибочно приняли мою точку зрения, я не против ООП в языках, не поддерживающих module/package-level сущности, кроме классов (привет java). Я просто не вижу смысла наворачивать классы.

                              Поинт про комбинирование подходов полностью поддерживаю.


                              1. TheShock
                                01.06.2017 01:41
                                +1

                                То есть вы не пишите классы просто чтобы не писать классы? И в JS их не пишите только потому что можете не писать?

                                Да, я понимаю, что вместо класса можно сделать структуру, содержащую данные и функции, которые этот объект обрабатывают как чисто, так и грязно. Получится старый добрый процедурный стиль конца 60-х. А со строгой типизацией это будет даже работать так же как в ООП только будет другая форма записи.

                                Как на меня, механика ООП просто поприятнее:
                                — не нужно импортировать эти функции, когда ты получаешь структуру в процедуру или объект в метод
                                — ide автоматически подсветит тебе все возможные варианты, когда ты напишешь myItem.<alt-space>
                                — ты получаешь дополнительные удобные механизмы вроде полиформизма в ООП понимании этого слова
                                — Удобную функцию-конструктор
                                — Возможность использовать более короткие имена:

                                unit.move({ y: 1 })
                                
                                // значительно приятнее, чем
                                
                                moveUnit(unit, { y: 1 }})
                                
                                // я уж молчу об
                                
                                newWorld = { ...world, {
                                  units: { ...world.units, [
                                    [unitId]: {
                                      ...world.units[unitId],
                                      y: world.units[unitId].y + 1
                                    }
                                  ]}
                                }}
                                
                                // Ну или если завернуть это в чистую функцию:
                                
                                newWorld = { ...world, {
                                  units: { ...world.units, [
                                    [unitId]: moveUnit(world.units[unitId], { y: 1 })
                                  ]}
                                }}
                                
                                // Или покороче:
                                
                                newWorld = moveUnitInWorld(world, unit.id, { y: 1 })
                                


                                Вы ведь не будете называть свою процедуру «move», ведь двигать можно что угодно?

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

                                А если уж мы использует классы, т.к. на них удобнее писать изменяемое состояние (а есть очень мало программ, где изменяемое состояние не нужно, в подавляющем большинстве программ изменяемое состояние — это и есть основа всех бизнес-требований), то зачем плодить сущности и использовать процедуры?

                                Я, конечо понимаю, что если у вас не сложная логика, то можно отдать заботу о состоянии процедурной библиотеке и делать вид, что ты пользуешься ФП, но это подойдет только для чего-то уж очень простого, потому что сложность растет слишком быстро и вообще это противоестественно.

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


                                1. kafeman
                                  01.06.2017 02:26

                                  при этом не отказывают от чистого кода
                                  Мне кажется, у вас какой-то культ слова «чистый», потому что выражение «чистый код» — это даже близко не про «чистые функции» и «неизменяемые объекты».


                                  1. TheShock
                                    01.06.2017 02:30

                                    Что ж, дайте ваше определение термина «Чистота». Я говорю о детермированности и отсутствии побочных эффектов. А вы?

                                    И почему культ именно у меня, если я как раз говорю, что делать культ из чистоты — глупо и вредно. Какое-то странное у вас понимание термина «культ».


                                    1. kafeman
                                      01.06.2017 02:40

                                      Я говорю о детермированности и отсутствии побочных эффектов. А вы?
                                      «Чистый код» — это красиво спроектированный код, противопоставление т. н. «спагетти-коду». Он может как использовать функциональный подход, так и не использовать его (изменяемое состояние, побочные эффекты, недетерминированность и т. д.)

                                      И почему культ именно у меня, если я как раз говорю, что делать культ из чистоты — глупо и вредно.
                                      Ну вот, опять. Смиритесь с тем, что не все, что имеет в своем названии «чистый», связано с ФП :-) В данном случае было бы правильнее: «И почему культ именно у меня, если я как раз говорю, что делать культ из функциональщины — глупо и вредно».


                                      1. TheShock
                                        01.06.2017 03:01

                                        Ну хорошо, что мы выяснили, что я подразумеваю под термином.

                                        что имеет в своем названии «чистый», связано с ФП

                                        Конечно, чистота в смысле детермированности и отсутствии побочных эффектов (видите, как вы все усложнили, теперь приходится уточнять термин) — не обязательно показатель функциональности, но функциональность — обязательно чистота (все целые числа — числа, но не все числа — целые).

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

                                        делать культ из функциональщины — глупо и вредно

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

                                        И вот чистоту в смысле детермированности и отсутствии побочных эффектов, которая так популярна в ФП можно взять на вооружение и в ОО программировании для определенных задач. И этот код не обязательно будет функциональным.


                                        1. kafeman
                                          01.06.2017 03:13

                                          Мне кажется, вам пора спать :-) Перечитайте еще раз всю ветку отсюда. Все это время я вообще никак не комментирую ваше мнение, а просто пытаюсь предостеречь от использования неправильных терминов. То, что вы имели ввиду под словом «чистый» и так ясно из контекста, но это просто неправильное слово.


                                          1. TheShock
                                            01.06.2017 03:38
                                            +1

                                            Ну вот на википедии сказано очень коротко: Чистота? (в отношении языка программирования) — отсутствие побочных эффектов. Если перейти на английскую статью — там есть интересное уточнение:

                                            An expression is said to be referentially transparent if it can be replaced with its corresponding value without changing the program's behavior

                                            Но вот я не нашел ни одного упоминание, что термин «Чистота» можно применять исключительно к ФП. Аргументируйте или дайте пруфы, пожалуйста. Потому что пока это выглядит как «это ведь наше, ФПшное, не забирайте, пожалуйста»


                                            1. kafeman
                                              01.06.2017 03:44

                                              Вам точно стоит выспаться :-)

                                              Потому что пока это выглядит как «это ведь наше, ФПшное, не забирайте, пожалуйста»
                                              Напротив, я говорю, что «чистая функция» — это из ФП, все остальное, что имеет в своем названии прилагательное «чистый», к нему не относится. Потому что это вы упоминали выше «чистый метод», «чистый код» и т. д. в контексте ФП. Откройте поиск по странице и введите туда «чистый», сильно удивитесь.


                                              1. TheShock
                                                01.06.2017 04:01

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

                                                Вы все еще не привели аргументов, почему общий термин программирования «Чистота» можно применять только в контексте ФП. Я понимаю, что есть также общий и синонимичный термин, но раз вы меня поняли, то функция языка общения выполнена корректно и вы просто придираетесь.

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

                                                Еще раз вы почему-то отожествляете понятия ФП и остальные названные. Да, ФП — это неизменяемое состояние, отсутсвие побочных эффектов, детерминированность. Но

                                                Неизменяемое состояние само по себе — не ФП
                                                Отсутсвие побочных эффектов само по себе — не ФП
                                                Детерминированность сама по себе — не ФП

                                                Почему вы стараетесь меня убедить, что если какой-то код не изменяет состояние, не имеет побочных эффектов и детерминированный — обязательно ФП? Смотрите, такой логический вывод можно сделать из ваших слов:

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


                                                Я запишу ваши слова псевдокодом:

                                                if (ФП) {
                                                  ...
                                                } else {
                                                  изменяемое _состояние();
                                                  побочные_эффекты();
                                                  недетерминированность();
                                                }
                                                


                                                Простите, но я с этим совершенно не согласен.


                                                1. kafeman
                                                  01.06.2017 04:18

                                                  Еще раз, я не спорю с вами о том, что относить к ФП, а что нет. Более того, признаться, я читаю ваши аргументы по-диагонали, поэтому не могу ни согласиться, ни не согласиться с вами. Я просто уточняю термины:

                                                  А что мешает в ООП создавать чистые методы или даже классы, которые делают свою работу и умирают?
                                                  Этот ваш комментарий вызвал недоумение не только у меня, потому что нет такого термина «чистый метод» или «чистый класс». То, что вы хотели сказать, называется «неизменяемый объект».

                                                  Я просто пишу код используя ООП и, при этом не отказывают от чистого кода, когда это облегчит поддержку приложения.
                                                  Термин «чистый код» существует, но означает совершенно другое. То, что вы хотели сказать, называется «код, написанный в функциональном стиле». Аббревиатура ФП также была бы к месту.


                                                  1. TheShock
                                                    01.06.2017 04:26
                                                    -1

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

                                                    То, что вы хотели сказать, называется «неизменяемый объект».

                                                    Нет, я не хотел сказать «неизменяемый объект». Вот пример:

                                                    var nVector = new Vector3(5, 3, 1).normalized

                                                    Вектор сделал свою работу и умер, но он не неизменяемый, зачем вы заставляете меня применять неправильные термины?

                                                    «код, написанный в функциональном стиле».

                                                    Нет, это код написанный в объектно-ориентированном стиле, а не функциональном, но он не имеет побочных эффектов, он детерминированный и он не изменяет состояние. Он чистый.

                                                    И вы всё-еще не привели аргументов для своего субъективного мнения, что слово «чистый» в контексте отсутствия побочных эффектов и детерминированности можно применять только для ФП.


                                                    1. kafeman
                                                      01.06.2017 04:40
                                                      +1

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

                                                      var nVector = new Vector3(5, 3, 1).normalized
                                                      
                                                      Это «временный объект», в JS было бы логичнее привести более распространенный случай
                                                      var ts = +new Date();
                                                      

                                                      Нет, это код написанный в объектно-ориентированном стиле, а не функциональном, но он не имеет побочных эффектов, он детерминированный и он не изменяет состояние. Он чистый.
                                                      Согласен, такого термина еще не придумали, потому что «stateless-объект» — оксюморон. Еще раз, «чистый код» — это термин, который другие люди стали использовать раньше вас. Есть также одноименная книга довольно известного автора.


                                                      1. TheShock
                                                        01.06.2017 05:10

                                                        Роберт Мартин, лежит такая у меня дома. И что — никогда не слышали про синонимы? Тем более, это не более чем заслуга переводчика.

                                                        в JS было бы логичнее привести более распространенный случай

                                                        Какой вы молодец, что придумали пример. Вот только он обращается к внешнему состоянию (ОС), следовательно не Pure, следовательно не равносилен моему примеру.

                                                        Термины уточняю, потому что у других комментаторов возникли проблемы с пониманием.

                                                        Кто? Вы меня поняли, raveclassic — тоже, сомневаюсь, что wheercool написал бы следующее, если бы понял меня в вашей странной манере, особенно учитывая, что «Чистый код» как раз об ООП.
                                                        Интересно каким образом?

                                                        И, скорее всего, в следующем сообщении вы просто неправильно расставили акценты:
                                                        Вы заявили то, что ООП поощряет чистоту, а не то что позволяет писать методы, к-ые не используют состояние.

                                                        Так кто и где меня неправильно понял?

                                                        Это «временный объект»,

                                                        А еще впомните о переменных, операторах, буквах, пикселях. Ок, это временный объект. А вот тоже временный объект: getMoved(unit, { x: 1 }). И что это характеризует?

                                                        Я не понимаю, чего вы хотите добиться. Просто повторяя свое мнение, которое я считаю ошибочным — вы меня не переубедите.

                                                        Чистый («Pure», не «Clean») код — это код, который не имеет побочных эффектов и детерминированный. Чистой не обязана быть функция, им может быть и выражение:



                                                        Вы заставляете писать меня «Pure» вместо «Чистый»? Зачем? Чтобы меньше людей меня поняло? Зачем вы стараетесь нарушить работу коммуникативной функции языка?


                                                        1. kafeman
                                                          01.06.2017 05:28

                                                          только он обращается к внешнему состоянию
                                                          Согласен, не удачный пример в контексте ФП, пытался придумать пример временному объекту.

                                                          И, скорее всего, в следующем сообщении вы просто неправильно расставили акценты:
                                                          И, скорее всего, вы перепутали меня с другим комментатором.

                                                          А еще впомните о переменных, операторах, буквах, пикселях. Ок, это временный объект.
                                                          Поверьте, я не придумываю слова на ходу, а обращаюсь к устоявшемуся понятию Temporary Objects (по ссылке C++, но относится и к другим языкам). И, таки да, временные (безымянные) переменные бывают (что вы имеете ввиду под временными пикселями, уже не совсем понятно — возможно, пиксели, которые затираются в процессе подготовки кадра).

                                                          Я не понимаю, чего вы хотите добиться.
                                                          Теперь уже я сам не понимаю, тем более, что к CSS это не имеет никакого отношения. Но надеюсь, что другие читатели теперь смогут понять ваши комментарии благодаря моим усилиям.


                                                          1. TheShock
                                                            01.06.2017 05:33

                                                            Согласен, не удачный пример в контексте ФП, пытался придумать пример временному объекту.

                                                            В контексте чистоты. При чем тут ФП? Почему вы эти два куска кода называете ФП? Первый — классический ООП, пусть и чистый, второй — не имеет ни чистоты, ни декларативности.
                                                            var nVector = new Vector3(5, 3, 1).normalized
                                                            var ts = +new Date();
                                                            


                                                            И, скорее всего, вы перепутали меня с другим комментатором.

                                                            Нет. Я имел ввиду, что вы, своим внутренним голосом неправильно расставили акценты в комментарии другого автора.

                                                            вы имеете ввиду под временными пикселями

                                                            А где я говорил о временных пикселях? Я говорил о просто пикселях, о просто переменных, о просто операторах и о других просто терминах, которые можно применить к моему примеру, которые будут корректны и которые, при этом, так само будут не соответствовать тому, что я хотел донести.

                                                            Но надеюсь, что другие читатели теперь смогут понять ваши комментарии благодаря моим усилиям.

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


                                                          1. TheShock
                                                            01.06.2017 05:42

                                                            var nVector = new Vector3(5, 3, 1).normalized
                                                            

                                                            На всякий случай еще раз акцентирую, это чистое выражение (Pure Expression), согласно определению из книги. То, что вы путаете Pure и Clean, и уж тем более, почему-то, считаете понятия «Чистота» («Pure») и «Функциональное программирование» тождественными — это лично ваши ошибки, которые лично вам стоит исправить. Выражение может быть чистым, но при этом не быть функциональным, что я и показал на примере.


                                1. raveclassic
                                  01.06.2017 10:13

                                  Я не пишу классы там, где они мне не нужны, так как не решают задачу, которую призваны решать — инкапсуляция состояния. Там где нужно — использую. А по вашему примеру это обычный неймспейсинг. Не, это тоже удобно и иногда даже удобнее, чем import * as Vector from 'vector';.

                                  Вы явно путаете процедурное и функциональное программирование.

                                  Можно усовершенствовать ваш пример до const moveUnit => where =>
                                  unit; (moveUnit :: TPlace -> unit -> unit)
                                  , сдвигая последний оперируемый объект в конец, как это делается ну практически во всех функциональных языках для удобства композиции частично примененных функций. Бонус в «явности» передачи состояния (объекта) и передачи таких функций в фвп без необходимости ручного байндинга на инстанс и без класс пропертей). Но JS тут как всегда со своим синтаксисом.

                                  Вы ведь не будете называть свою процедуру «move», ведь двигать можно что угодно?
                                  Именно так бы я ее назвал в purescript, как раз таки потому, что двигать можно что угодно, так это более общий алгоритм. И накидал бы type-class'ов. Но JS тут как всегда.


                                  1. TheShock
                                    01.06.2017 17:55

                                    Вы явно путаете процедурное и функциональное программирование

                                    Не, я прекрасно понимаю разницу между ними. Я прекрасно понимаю разницу между С и Лисп. Или между тем, как писали на ЖС в 96-м и стараются писать в 2016-м.

                                    Признаюсь вам честно, слышал, что говорят, что Лисп — ненастоящее ФП, а настоящее — только Хаскель. Из-за математики. Но вот это я уже не понимаю. И вчера применял термин «процедурное» именно там, где считал нужным. Если мы говорим просто о том, чтобы пользоваться процедурами в неймспейсах, которые изменяют данные в отдельно лежащих структурах, то это процедурное программирование. Для функционального необходимо кое-что большее чем «не писать классы».

                                    Можно усовершенствовать ваш пример до const moveUnit => where => unit; (moveUnit :: TPlace -> unit -> unit)

                                    Напишите полностью, пожалуйста.

                                    что двигать можно что угодно, так это более общий алгоритм.

                                    Двигать можно что угодно, но не все одинаково.


                                    1. raveclassic
                                      01.06.2017 21:37

                                      Напишу попозже развёрнутый ответ, с телефона неудобно


                                      1. raveclassic
                                        02.06.2017 01:00
                                        +1

                                        Итак.

                                        По поводу 96 и 2017. Парадокс в том, что различие между процедурой и функцией минимальное — первая императивно указывает что сделать с передаваемой структурой данных (само собой мутабельно), вторая же показывает, что должно находиться в конце, после выполнения списка преобразований. Ну то есть вместо «подвинь туда, поверни сюда», у нас результат операции над объектом — это такой объект, который был подвинут туда и повернут сюда.
                                        Да что я вам тут рассказываю, вы и так это знаете.

                                        По поводу Лиспа и Хаскеля — оба выходят за рамки моей повседневной практики, так что чего-то дельного и подкрепленного серьезным опытом сказать по этому поводу не смогу (хотя мне тут все пытаются продать Кложу). Но я помню Алана Кея, помню эти истории про Лисп, Смолтолк и компанию. Единственное, что могу сказать, так это что Эрланг гораздо более ООП-шный (в оригинальном смысле), чем его везде форсят как ФП.

                                        Красота же Хаскеля и смежных языков в том, что там есть type-class'ы. Это очень похоже на привычные ООП-шные интерфейсы. Разница в том, что в ООП мне нужен явный объект (инстанс класса, имплементирующего интферфейс), чтобы на нем (явно) вызывать нужные методы. Type-class'ы же позволяют определить множество, определяемое типом данных (ну например, Moveable), над которым определена операция move. Причем «глобально». Ну то есть я не вызываю метод move на инстансе класса, имплементирующего интерфейс Moveable. Я просто пишу move some_instance, а возможность операции move и, главное, ее реализация описаны в расширении множества Moveable для конкретного типа данных, реализующего (через, например, instance в Хаскеле) нужный type-class (интерфейс).

                                        Да уж, действительно достаточно туманно и непонятно.

                                        Но, смотрите, это позволяет писать алгоритмы более высокого уровня абстракции. Если мне нужно что-то подвинуть, например в перекомпоновке сцены, мне не нужно знать что именно, и главное как, я двигаю. Алгоритм перекомпоновки знает только (ну грубо говоря) про свою координатную ось, у него (допустим) есть список неких объектов (думаю, вы уже догадались, что они должны имплементить интерфейс Moveable). И теперь ему достаточно на списке (допустим list) этих объектов выполнить операцию map move list. Кратко и красиво. Данные отдельно (объекты), операции отдельно (алгебра над ними).

                                        По поводу moveUnit. На TS это было бы что-то вот такое уродливое:

                                        type Point = {
                                          x: number,
                                          y: number
                                        };
                                        type Unit = Point;
                                        const move = (where: Point) => (target: Unit) => ({
                                          where //ну тут понятно, что операция move должны быть сложнее и над структурой Unit
                                        });
                                        

                                        В том же purescript это выглядело бы вот так:
                                        type Place = {
                                          x :: Number,
                                          y :: Number
                                        }
                                        
                                        type Unit = Place
                                        
                                        move :: Place -> Unit -> Unit
                                        move place unit = unit {
                                          x = unit.x,
                                          y = unit.y
                                        }
                                        

                                        Изюминка в том, что частично примененную функцию move (или rotate или что угодно) можно использовать в функциональной композиции, опустив реальный аргумент (последний unit), «склеив» тем самым все операции в одну. И если у вас над типом Unit определены все нужные операции (move, rotate etc, причем отдельно от самого алгоритма), вы можете просто запихнуть структуру типа Unit в результат композиции этих функций. Если же что-то не сходится, вам компилятор тут же выплюнет ошибку.

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

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

                                        PS. На JS писать красивое ФП нереально, но мечтать хочется.


                                        1. TheShock
                                          02.06.2017 01:24

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

                                          Ну, как на меня, на мелких примерах ФП краше почти всегда)

                                          Интересно вот еще что — покажи как на purescript как в итоге поменять Юнит. Ну то есть функция изменения — это понятно, просто создает юнит. А как она используется в реальном приложении, когда мне, сообственно, необходимо сдвинуть этот юнит и перерисовать потом всю сцену.


                                          1. raveclassic
                                            02.06.2017 10:18

                                            Тут какая история — для того, чтобы обновить список юнитов функцией, нужны обычные filter, map и fold.
                                            Проблема в том, что не всегда нужно такое топорное обновление и вообще это все в вакууме и нужно где-то держать состояние, ну той же сцены, допустим. И чтобы не писать сейчас заклинание из монад, чтобы аккуратно обработать все эффекты, я просто оставлю ссылку на классную статейку.


                        1. wheercool
                          31.05.2017 23:50

                          Вы заявили то, что ООП поощряет чистоту, а не то что позволяет писать методы, к-ые не используют состояние.

                          Не нужно ходить далеко, проведите эксперимент. Попросите ваших знакомых написать сложение двух чисел.
                          Могу поделиться своим результатом. В большинстве случаев решение будет выглядеть примерно так:

                          class Sum {
                             public int Value {get; private set;}
                             public Sum(int initial) {
                                 Value = initial;
                             }
                             public void Add(int value) {
                                Value += value; 
                             }
                          }
                          


                          1. TheShock
                            01.06.2017 00:17
                            -1

                            Я сказал «в необходимых мерах». У вас всё в крайности впадает, потому архитектура жс-приложений на ФП так сложно поддерживается. Ну вот, смотрите, Unity, Vector3 — у вас есть возможность вызвать грязный метод Normalize, или чистых normalized. Если вам необходимо изменить состояние — изменяете состояние. Если необходимо обработать данные — обрабатываете и получаете чистые данные. Ну вот допустим, я в игре хочу получить все строения, которые я могу построить — напишу что-то вроде:

                            return buildings.Where(b => b.isAvailable)


                            А если у меня есть 20 строений и одно из них построилось — я напишу что-то вроде:
                            b.isReady = true


                            Или вот у меня есть маркдаун парсер на JS. Что-то вроде такого:

                            class UniqueAppRenderer : DefaultMarkdownRenderer { 
                              public Image (token: ImageToken) {
                                return <SuperAppImage token={token} />
                              }
                            }
                            
                            var tree = new MarkdownTree(input);
                            return tree.render(new UniqueAppRenderer());
                            


                            Чем этот пример не чистый? Ну или из классики:

                            var square = new Rectangle(100, 50).getSquare();
                            


                            На ООП вы пишете чистый код тогда, когда вам необходим чистый код. Комбинируете оба метода. И вам не приходится пересоздавать весь мир, потому что где-то открылась дверь. Именно потому на ООП написано так много столь сложного софта и так мало его написано на ФП.


                            1. kafeman
                              01.06.2017 00:19

                              Ясно. То, что вы имеете ввиду, в ООП называется immutable objects, и это не совсем тоже самое, что pure functions.


                              1. TheShock
                                01.06.2017 00:21

                                А я не говорил про «чистые функции», если один раз услышали чистоту вместе с функциями, то чистыми могут быть только функции?

                                Объекты, что я привел не иммутабельны. У Rectangle может измениться любая сторона и getSquare будет выдавать иной результат, ровно так и с вектором.


                                1. raveclassic
                                  01.06.2017 00:32

                                  Согласен. Но вы же можете описать function getSquare(rectangle: Rectangle): number; и это будет то же самое.


                                  1. TheShock
                                    01.06.2017 01:08

                                    Ухты! А на ФП, оказывается тоже можно писать чисто?!


              1. vasIvas
                31.05.2017 21:55

                Это басни, могу сказать из личного опыта. Когда отключаешь разум и начинаешь слушать непонятно кого, то да, так и получается. Я сначала противился редаксу, потом понял что люди были правы и что если писать так, как говорят, то реально получается редакс и что уж лучше взять оригинал. Затем я понял что редакс, это очень сложно на сложных интерфейсах, что это медленно на мобильных и образовался временный вакуум. Но потом я перешел на angular2, а после, когда пришлось вернуться на реакт, я отказался от редакса в пользу mobx + старая добрая сервисная архитектура и не поверите, все что говорят о реакте я вообще забыл. Нет вообще проблем и самое главное компоненты стали очень переиспользуемыми.
                Поэтому заявления что все равно получится редак неверны.


      1. questpc
        06.06.2017 11:17

        Мне тоже очень не нравится JSX, хотя сама идея вложенных компонентов в react хорошая. Кто-нибудь пробовал вместо JSX использовать такие шаблоны?


        https://github.com/wix/react-templates


        Хотя все равно кросс-трансляция будет, которая тоже неприятна :( Сейчас у меня никакой кросс-трансляции, ES5 + Knockout.js.


  1. stardust_kid
    31.05.2017 17:25
    +14

    Ниженаписанное — лишь мое скромное мнение. СSS внутри javascript — глупейший тренд последнего времени. Случаи, когда это действительно нужно, можно пересчитать на пальцах одной руки. За собой этот паттерн тянет множество недостатков, например, необходимость эмулировать селекторы и псевдоклассы. Аргумент о "глобальной видимости" притянут за уши, для этого есть css-modules. Серьезно, не пойму, какую проблему это все решает?


    1. avdept
      31.05.2017 17:32
      -1

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


    1. kafeman
      31.05.2017 19:21

      Решает проблему переопределения стилей при «наследовании». Серьезно, "!important" — это просто ад какой-то, по сравнению с inline-стилями.


      1. raveclassic
        31.05.2017 23:18
        +1

        Грамотное построение графа зависимостей в слое компонентов прекрасно справляется с задачей переопределения и дополнения чего бы то ни было. И никаких импортантов.


        1. kafeman
          31.05.2017 23:35

          const Button = ({className, text}) => (
              <button className={`Button ${className}`}>
                  {text}
              </button>
          );
          
          const SuperButton = ({className, text}) => (
              <Button
                  className={`SuperButton ${className}`}
                  text={text}
              />
          );
          

          .SuperButton {
              color: red;
          }
          
          .Button {
              color: green;
          }
          


          1. raveclassic
            31.05.2017 23:41
            +1

            Ну так вам надо с css-модулями ознакомиться:

            //Button.css
            .Button {
              color: green;
            }
            
            //Button.jsx
            import css from './Button.css';
            export const Button = ({className}) => <button className={`${css.Button} ${className}`}></button>;
            
            //SuperButton.css
            .SuperButton {
              color: red;
            }
            
            //SuperButton.jsx
            import { Button } from './Button.jsx';
            import css from './SuperButton.css';
            export const SuperButton = ({className}) => <Button className={`${css.SuperButton} ${className}`}/>;
            


            А еще лучше с react-css-themr, который элегантно решает проблему мерджа класснеймов.


          1. stardust_kid
            01.06.2017 11:38

            Откройте для себя магию селекторов


            .Button.SuperButton{
              color: chucknorris;
            }


            1. raveclassic
              01.06.2017 12:47
              +2

              Ну так вы грубо нарушаете инкапсуляцию, когда внутри SupperButton знаете, какие классы висят на Button.


              1. stardust_kid
                01.06.2017 19:13

                Дельное замечание. Тогда враппер надо делать, или по-вашему.


      1. stardust_kid
        01.06.2017 11:33

        Так не переопределяйте, епт. Серьезно, это что такая проблема да? 10 лет css пишу !important использую считанные разы. Трудно селекторы научиться писать?!


    1. Aingis
      31.05.2017 19:32

      Особенно веселит, когда рассказывают: «смелое решение, но набирать, конечно, неудобно. Но можно использовать template literals и писать как обычный CSS!»


      Только почему-то на этом и останавливаются. А ведь можно выделить этот CSS в отдельный файл, и получится старый добрый обычный CSS. С полноценной поддержкой редактора/IDE и других инструментов вроде postCSS.


    1. serf
      31.05.2017 21:13
      -1

      СSS внутри javascript — глупейший тренд последнего времени.

      React фанбоям должно понравиться, JSX ведь им нравится.


      1. stardust_kid
        01.06.2017 11:34
        -1

        У вас Дэн Абрамов под кроватью.


  1. vasIvas
    31.05.2017 18:18
    -3

    Полный бред. Статью даже не читал, так как не хочется забивать мозг лозунгами недальновидных людей.
    Скоро появятся css переменные, которые позволят управлять стилями из js. И на этом точка.
    Ну да, можно конечно отказаться от быстрого sass написанного на си и начать делать его аналог на js,
    при условии что он все рано будет компилироваться в css, да пожалуйста, делайте, тратьте время, если получится на отлично, то им будут пользоваться. Но это будет заменой sass, а не css у которого будущие только в js.


    1. justboris
      31.05.2017 18:27
      +9

      Полный бред. Статью даже не читал

      Отличное начало здравого обсуждения этой темы.


    1. kafeman
      31.05.2017 19:24
      +1

      которые позволят управлять стилями из js
      Уже. См. document.styleSheets.


  1. x07
    31.05.2017 19:06
    +3

    Во времена jqery это все называлось спагетти-кодом, jsx сам по себе гадость, а с css так это вообще прямой наследник лапши.


    1. serf
      31.05.2017 21:15
      -2

      это все называлось спагетти-кодом
      jsx сам по себе гадость

      Вот вот, ноги то ведь из Facebook растут, корни у которого с PHP связаны ...


  1. justboris
    31.05.2017 20:45
    +11

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


    0. CSS-in-JS это не инлайн стили


    Определение стилей <div style="..."></div> это совсем не про CSS-in-JS, инлайн-стили здесь тоже не привествуются. В статье рассказывается совсем о другом. Мы просто помещаем определение стилей в тот же JS файл, что и остальной код компонента, а CSS оттуда вытаскивается на этапе сборки или server-side-рендеринга. Ваши пользователи получат такой же CSS как и всегда, все :hover, медиа-запросы будут работать как и раньше.


    Когда мы разобрались, что CSS-in-JS уж как минимум не хуже обычного CSS, давайте разберемся, что мы можем от этого выиграть.


    1. Переиспользование констант между JS и CSS


    Довольно часто в responsive-сайтах нужно не только менять стили, в зависимости от размера экрана, но еще и исполнять какой-то Javascript. Например, зачем подписываться на клик на кнопку, которая все равно в мобильной версии не видна? Переиспользовать SASS-константы в Javascript — не самая простая задача. С CSS-in-JS вы сможете использовать JS-константы и для стилей, и для кода.


    2. Упрощение кода


    Вы когда-нибудь пытались взять квадратный корень из переменной в SASS? В Javascript для уже есть Math.sqrt, а в SASS придется попотеть. Кроме квадратного корня вам может понадобиться еще какая-нибудь логика в стилях, и в SASS с ней однозначно хуже, потому что он не предназначен для написания сложных алгоритмов. Кроме того, JS-функции проще покрыть тестами, чем SASS-миксины.


    3. Оптимизация неиспользуемых стилей


    На страницу очень часто грузятся стили для динамических блоков, которые не видны на странице сразу после загрузки. Разделять CSS на критический и второстепенный не так просто, а в CSS-in-JS, если у вас есть серверный рендеринг, это проще простого. В упомянутой в статье библиотеке Aphrodite есть поддержка server-side. Когда вы отрендерите на сервере изначальное состояние html, библиотека вернет вам стили только тех компонентов, что были на самом деле использованы при рендеринге, а остальные стили доставятся на клиент уже потом, вместе с JS.


    4. Динамический CSS


    С помощью CSS-in-JS можно отказаться от инлайновых стилей вообще! Определение стиля в JS может быть не только статической строкой, но и функцией:


    const styles = {
        left: (props) => props.x + props.offset
    }

    В Aphrodite я этой фичи не нашел, но в JSS, которым я иногда пользуюсь, такая фича есть.


    При изменении свойств компонента это значение будет пересчитываться, и библиотека синхронизирует его в CSS через document.styleSheets, упомянутый выше в комментариях. В классическом подходе с CSS все равно иногда не получается избежать инлайновых стилей (сделать анимацию, задать координаты для попапа), а в CSS-in-JS вы сможете сгруппировать все стили в одном месте, как всегда этого и хотелось.


    Так что, в следующий раз, когда вы будете решать в своем проекте одну из проблем, описанных выше, на секунду задумайтесь, что при использовании CSS-in-JS эта проблема могла бы быть решена проще.


    1. vasIvas
      31.05.2017 21:06
      +3

      1, Грамотно написанный css за глаза можно сделать более чем динамическим изменяя лишь css-var из js.

      2. И что? Если одному человеку из миллиона, раз в жизни потребуется квадратный корень в CSS, то это оправдывает все остальное? Зачем такие вещи в css? То что делают с квадратными корнями нормальные делают на canvas или webgl. В крайнем случаи в js оставляете логику и результаты устанавливаете, кому? css-var!

      3. Вы беспокоитесь о том что мощный комп или телефон пользователя будет грузить стили на 0,005 секунды медленнее и поэтому увеличиваете нагрузку на сервера?

      4. Ну вот тебе радость! В оправдание привели безумную идею инлайнстилей. Покупайте у меня аэрозоль, после применения которого можно не совать голову под автомобиль, что спасет Вам жизнь…


      1. justboris
        31.05.2017 21:41

        1) Во-первых, css-переменные нативно еще не поддерживаются, поэтому это лишь мечты о будущем, а фичи надо делать уже сегодня. Во-вторых, а переменную вы чем выставлять будете? Можно написать руками, а можно взять уже готовое решение, например JSS (5кб весит библиотечка)


        2) Этот пункт больше про то, что миксины на SASS получаются очень уж громоздкие. Вот пример из Bootstrap. На JS то же самое смотрится читабельнее. Ну и как тестировать SASS-миксины по-прежнему непонятно.


        3) В комментарии я привел ссылку на рассказ про то, почему критический CSS это важно и это совсем не 5мс разницы. Я понимаю вашу фрустрацию, но лучше прочитать материалы, прежде чем высказывать мнение "это всего 5 милисекунд и нагрузка на сервера мне дороже".


        4) Возьмем для примера тултип. Его нужно спозиционировать с учетом того, что у него влево выступает стрелочка. Довольно типичная задача в верстке. В CSS-in-JS это будет в одном месте, очень наглядно


        const arrowSize = 10;
        const tooltipStyles = {
           '&:before': {
               borderWidth: arrowSize,
               // тут остальные стили для стрелочки
           },
           left: (props) => props.left + arrowSize,
           top: (props) => props.top
        };

        А если делать это на CSS, то часть стилей будет написана в js через $().css({top: xxx, left: xxx}), а часть про размеры стрелочки написана в другом месте в CSS? Мы снова возвращаемся к вопросу, как нам прокинуть значение размера стрелочки из CSS в JS.


        1. vasIvas
          31.05.2017 22:08
          +2

          1) Ну да, это завтра, но статья о том что будет ЗАВТРА.
          2) Зачем Вам тестировать миксины css? Вы явно пишите что-то не в том месте.
          3) Те места, где важна скорость настолько, что лишние ,5ms важны не используют пререндер на сервере, а используют сервера на java, c# или в крайнем случаи ruby или pyton.
          4) Зачем Вы тултипу в js ширину задаете? В будущем тултипы вообще не будут нуждаться в влезаньи в них, ведь появятся attr + *-data.

          Поэтому тема, как таковая, неверна, ведь она о будущем, а я сказал что у этой ерунды нет будущего, если они не сделают альтернативу sass, которая, как я сказал, если будет быстрее и если она будет так же компилироваться в css, то даже я ей буду пользоваться.

          и проблем с именами переменных я в sass не заметил и так же будет и с css. Поэтому аргументы такие — Вы тысячи людей не правы, ведь мне так не нравится.


          1. justboris
            31.05.2017 23:07

            То есть ваша позиция в том, что скоро в браузеры по полной поддержат CSS-переменные, Shadow DOM, веб-компоненты, и после этого все эти временные решения будут не нужны.


            Скорее всего так и будет, только Shadow DOM стандарт так до конца и не устаканился (недавно его переписали целиком заново и он называется Shadow DOM v1 в противовес прошлому v0).


            В общем, можно долго ждать и надеяться на разработчиков браузеров, а задачи как-то решать надо. Поэтому я и изучаю варианты использования CSS-in-JS, в ближайшие пару лет это точно пригодится


            1. vasIvas
              31.05.2017 23:14
              +1

              Нет. Статья гласит «CSS в JavaScript: будущее компонентных стилей», а мое мнение, что это бред.
              Обоснование этому все то что я уже сказал + то что многие делают ошибку в одном месте, а пытаются решить её в другом. И вот из-за этого появляются подобные идеи.


              1. justboris
                31.05.2017 23:18

                Будущее бывает разное.
                Есть совсем далекое будущее, с межзведными перелетами, есть поближе, где все бразуеры начнут поддерживать Shadow DOM.


                А есть вопрос, на чем начать пилить админку в следующем месяце.


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


        1. faiwer
          01.06.2017 07:25

          • 3. По пункту 3 не совсем ясно. Скажем сгенерировали мы CSS для некоторой страницы, без лишнего кода. Юзер кликает на другую страницу и получает новый набор CSS, большую часть из которого он уже встречал на прошлой странице. Но загружает повторно, всякий раз… Даже если юзер вернулся на ту же самую страницу. Тут уже какой-то прямо commonChunk как в webpack-е нужен? Или я сбился с толку? Как предполагается решать такие задачи?
          • 4. Я правильно понял этот пункт? Это автоматически генерируемые стили в runtime? Там же вроде речь шла о предварительной компиляции всего этого добра в отдельную CSS?
          • 5. Так и не понятно в итоге, что с каскадами? Просто похоронить как избыточную сложность? Возможно это решение, но выглядит радикально. В качестве альтернативы городить трудно-читаемых монстров через интерполяцию и зависимости (подключать наследуемые имена классов из соответствующих файлов и пробрасывать их внутрь зависимых, где подключать через ${}).

          Мне сама идея более тесной взаимосвязи CSS и JS кажется интересной, т.к., на мой взгляд, в больших проектах, есть почти нерешаемая проблема в виде кучи не используемого legacy кода. А тут вроде можно всё так к друг другу поджать, чтобы оно на уровне резолва зависимостей отрабатывало. Посему всё жду серебрянной пули, идеального решения. Но куда бы я пока не глянул ? выглядит вторично и "не вкусно". Особенно когда некогда простые стили превращаются в нагромождение интерполяций, методов и разных хаков.


          1. raveclassic
            01.06.2017 10:18
            +1

            А смотрели themr? мне даже как-то неудобно дважды в топике советовать


            1. faiwer
              01.06.2017 10:24

              Нет не смотрел, не попадался. Спасибо, присмотрюсь.


              1. raveclassic
                01.06.2017 11:18

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


          1. justboris
            01.06.2017 10:21
            +1

            3) Обычно в веб-приложениях переходы по ссылкам происходят без перезагрузки страницы. Повторно ни скрипты, ни стили не загружаются.


            4) Предварительно выносится критический CSS — тот что юзер увидит еще до подгрузки Javascript. Стили для попапов и других динамических штук остаются в JS, потому что все равно у пользователей с отключенными скриптами они не будут видны


            5) Каскады на месте. Из нашего JS кода сгенерируется совершенно обычный CSS, hover, каскады, все можно использовать как и раньше. Вот пример из JSS: http://cssinjs.org/jss-nested?v=v4.0.1


            По поводу того как это выглядит в больших проектах, можно посмотреть на документацию JSS: https://github.com/cssinjs/cssinjs Этот сайт использует их же технологию для стилей. По-моему, читаются стиили так же, как и если бы были в CSS. Пример одного файла.


      1. justboris
        31.05.2017 21:44

        Ну и кроме того CSS-переменные должны иметь глобальные имена, поэтому мы возвращаемся обратно в БЭМ, только теперь в именах переменных --my-button--background-color-default


        1. vasIvas
          31.05.2017 22:38
          -2

          Короче ясно одно, что сие чудо является будущим для тех, кто —
          1. собирает из css картины, по очень сложным алгоритмам, для которых нужно писать тесты в миксинах.
          2. для тех кто не хочет учить css и ему проще забабахать все из js.
          3. для тех кто не знает сегодня что будет в css завтра, им видимо лень читать спеки.
          4. тем кто делает супербыстрые сайты на медленном пререндере и ему приходится экономить на на размере css.
          5. Тем кто в подобный рассказ может поверить прочитав пару таких же рассказов приведенных в доказательство.


          1. raveclassic
            31.05.2017 23:23

            Ну погодите. Вам привели пример 100%-ой проблемы, с которой сталкиваются ну просто все, кто так или иначе использует css — global scope. Ну устроен css так. Переменные решают другую проблему и не предоставляют адекватного инструмента для их инкапсуляции.


    1. kafeman
      31.05.2017 22:02
      +2

      Мне кажется, весь спор из-за того, что одни пишут веб-страницы, а другие веб-приложения.

      Для классических страниц (например, таких, как эта статья), конечно, классический CSS идеален. Автор один раз верстает HTML-страницу, а вы потом накладываете на нее какие угодно стили.

      Для веб-приложений классический CSS — это скорее зло, потому что стили одного компонента начинают негативно влиять на стили другого, становится трудно их переопределить и т. д. Усложняется повторное использование кода. Только inline-стили выручают (аргумент с «весом» страницы не рассматриваем — веб-приложение и так может определить, как именно оно хочет себя закешировать, и какие данные загружать повторно не нужно).

      Описываемый вами подход с «динамическим» CSS, это скорее, компромисс между двумя крайностями.


      1. justboris
        31.05.2017 22:16

        Вы правы, все так и есть.


        Однако в комментариях выше моего, довольно категорично заявляется что CSS-in-JS отстой и не может быть полезен вообще никогда, что и побудило меня написать свое мнение.


      1. vasIvas
        31.05.2017 22:46
        +1

        Да, если я сегодня напишу .button {color: red;} а завтра пущу в свой код сбежавшего из лаборатории бурундука и он не зная о моем коде напишет .button {color: green;}, то я согласен, будет беда. Но есть конвенции для конкретного проекта, документация, в которой все подробно описано. Если Вас что-то не устраивает, то есть бэм, есть css модули.
        И Вы не представляете, я много раз писал очень сложные приложения с нуля на реакте и ангуляр и у меня ни разу не было того, о чем Вы говорите.

        И хватит уже ерунду нести с глобальным пространством. Вы вот когда вэбпаком все импорты в одном месте собираете для вывода наружу не испытываете сложности? Нет! А почему? Потому что все имена разные!
        Ну это если Вы понимаете о чем я, если Вы действительно не пишите только легкие приложения.


        1. kafeman
          31.05.2017 22:59

          Если вы сегодня пишите компонент <Button />, а завтра наследуете от него <SuperButton />, то у вас уже большие проблемы с CSS. От того, в каком порядке соберутся стили, будет зависеть внешний вид вашей кнопки. В случае с inline-стилями такой проблемы не будет.

          хватит уже ерунду нести с глобальным пространством
          Простите, я разве что-то где-то об этом говорил?


          1. vasIvas
            31.05.2017 23:05

            Скажите честно, Вы наследуете в react или angular SuperButton от Button?
            Или возможно Вы никогда не писали на bootstrap и считаете twetter маленьким или вымышленным?
            Все что здесь привели ЗА из разряда «а что будет, если годзила встретит кинконга».


            1. kafeman
              31.05.2017 23:29
              +1

              Если вы имеете ввиду наследование JS-классов, то, конечно, нет, а компоненты — да, постоянно.

              Twitter Bootstrap, как раз таки, от этого страдает. Всякие ".btn .btn-default" тому подтверждение (совершенно немасштабируемое решение, если вы решите «унаследовать» свою кнопку от стандартной).


              1. raveclassic
                31.05.2017 23:31
                +1

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


                1. vasIvas
                  01.06.2017 00:57
                  -1

                  Поражает на самом деле то, что Вы думаете о том, что кто-то говорит «брать бутстрап».
                  Я говорю о том, что можно написать нужные Вам классы самому с самого нуля, как это сделано в бутстрап и обойтись вообще без чего-либо. Но не в коем случаи не тащить бутстрап туда, где придется все переписывать и переопределять, ведь он не затачивался под конкретный случай.


                  1. raveclassic
                    01.06.2017 01:01
                    +2

                    что Вы думаете о том, что кто-то говорит «брать бутстрап».
                    Вы, видимо, не представляете, сколько таких заказчиков, которые хотят космолет, но слышали про бутстрап и думают, что будет быстро и дешево. Еще и упрутся рогами.


                    1. TheOleg
                      01.06.2017 04:33
                      -2

                      Ну всё таки в современной верстке bootstrap — это только grid(и возможно типография).


                      1. TheOleg
                        02.06.2017 17:31
                        -1

                        Те, кто минус поставили могут объяснить что не так?


                        1. x07
                          02.06.2017 19:12

                          минус не ставил, но от себя добавлю, в современной верстке бутстрепчик это как опухоль. Он вообще не нужен, ни его сетка ни типография.


                          1. TheOleg
                            07.06.2017 05:06

                            Для нормальных проектов брать нормальное, для всяких одностраничников и домашних проектов берут бутстрап. Как-то так же?


                            1. x07
                              07.06.2017 10:08

                              нет, не так. Зависит от уровня разработчика. Новичок берет бутстрап, те кто уже поняли что он нахер не нужен, берут и пишут свою сетку, свои компоненты и все что им нужно в проекте.


                              1. TheOleg
                                08.06.2017 10:55

                                Что в большинстве сайтов в интернете(лендинги, бложики и прочее) не нужна своя сетка, когда есть бутстрап? Про компоненты я и написал что их не берёт никто.

                                Как я понимаю, со мной не согласны фронтендеры, которые работают над большими проектами. Весь бутстрап — удобно для проектирования всяких админок и внутренностей. Сетка — для простых страниц, когда проще взять готовое и сделать за несколько часов, чем тратить время на собственную сетку.


          1. justboris
            31.05.2017 23:14

            Только не нужно называть CSS-in-JS inline-стилями.
            Инлайн стили это <div style="..."></div>, и никаких CSS-селекторов, медиа-запросов, ховеров.


            А CSS-in-JS — это про генерацию обычного CSS, как будто JS теперь стал препроцессором для CSS, таким же как SASS или LESS.


            Очень важно различать эти понятия, чтобы не было ложного впечатления "нафиг этот CSS-in-JS, там :hover" не работает". Работает, также как и везде.


            1. raveclassic
              31.05.2017 23:29

              Вы лукавите :) Если у вас в ховере динамическое значение из js — оно пропишется инлайном


              1. justboris
                31.05.2017 23:50

                Сейчас проверил. Есть стили:


                const styles = {
                  button: {
                    background: 'blue',
                    '&:hover': {
                      background: props => props.hoverColor
                    }
                  },
                }

                Когда меняется props.hoverColor, обновляется правило в CSS, никаких инлайнов, все честно.


                1. raveclassic
                  31.05.2017 23:54

                  Ух ты, видимо эту проблему решили…
                  А как, новый тэг style пишется или в styleSheet обновляется?


                  1. justboris
                    01.06.2017 00:03

                    Используется CSSOM.


                    В исходниках JSS это делается вот так: https://github.com/cssinjs/jss/blob/master/src/backends/DomRenderer.js#L30


                    1. raveclassic
                      01.06.2017 00:05

                      А, ну то есть styleSheet. Интересно, надо ознакомиться.


          1. raveclassic
            31.05.2017 23:26
            -1

            если вы следите за порядком импортов и сначала подключаете js, а потом css (это простейшая и вполне адекватная конвенция при расширении), то проблем у вас не будет с порядком, вебпак все сам решит


            1. kafeman
              31.05.2017 23:41

              Вы, зачем то, оставили 2 комментария с одинаковым смыслом в двух разных местах. Ответил выше.


            1. mayorovp
              01.06.2017 13:35

              Зато такие решения как requirejs порядок не гарантируют.


              1. raveclassic
                01.06.2017 13:47

                Ну так, наверное, это проблема requirejs?


                1. mayorovp
                  01.06.2017 14:03

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


                  Проявиться она может в любых системах динамической загрузки модулей — requirejs, systemjs, webpack с настроенными чанками.


                  1. raveclassic
                    01.06.2017 14:10

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


                    1. raveclassic
                      01.06.2017 14:14

                      С другой стороны, согласен про динамическую загрузку. Тут не очень понятно, как решать.


                      1. raveclassic
                        01.06.2017 14:20

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


                        1. mayorovp
                          01.06.2017 16:10

                          Вот только вебпак никому не должен "переразруливать зависимости", потому что все работает в полном соответствии со спецификацией этих самых зависимостей.


                          Решить эту проблему можно если отказаться от неявной установки стилей в момент их загрузки, заменив ее на явный вызов:


                          import { Button } from './Button.jsx';
                          import css from './SuperButton.css';
                          export const SuperButton = ({className}) => <Button className={`${css.SuperButton} ${className}`}/>;
                          
                          css.install();

                          Но, скорее всего, для большинства разработчиков такое решение будет неудобным. Да и костылем оно отдает, потому что явно разделяет модули на модули-компоненты и модули вспомогательные...


                          1. raveclassic
                            01.06.2017 16:35

                            Ну как это не должен, вы же намеренно указываете ему распилить собранный им же граф зависимостей на чанки. Раз уж он один раз его собрал, то и при дозагрузке чанков в рантайме должен пересобрать его заново. Другое дело, если у вас вообще независимые бандлы, собранные по отдельности, тогда да.


                            1. mayorovp
                              01.06.2017 16:41

                              Что вы понимаете под "пересборкой"?


                              1. raveclassic
                                01.06.2017 16:48

                                Я не про ту «пересборку», о которой вы подумали. А о перераспределении тегов style в соответствии с изначальным графом зависимостей, когда догружается чанк.


                                1. mayorovp
                                  01.06.2017 16:50

                                  Так ведь webpack не управляет тегами style, вот в чем проблема.


                                  1. raveclassic
                                    01.06.2017 16:54
                                    +1

                                    Поэтому я и пишу «должен». Хотелось бы, чтобы был механизм синхронизации ядра, грузящего чанки и лоадеров, в частности style-loader'а.

                                    Причем, есть предчувствие, что он даже есть, через какие-нибудь ивенты.


                                    1. mayorovp
                                      01.06.2017 21:43

                                      Нарисуйте диаграмму зависимостей модулей — и будет ясно, что ничего он в такой схеме не должен.


                                      Более точно — зависимости модулей неправильно отражают желания программиста.


  1. woomka
    31.05.2017 21:11
    -3

    ниченепонял


  1. YemSalat
    01.06.2017 05:27

    «Типографика может создать или разрушить дизайн: выбор типа» (Typography can make or break your design: a process for choosing type)

    «Type» тут — это «шрифт», а не «тип»


  1. i360u
    01.06.2017 06:22
    +3

    Забавно читать такое, когда буквально недавно закончил рефакторинг ряда компонентов своего проекта с переносом практически всей логики и вариативности отображения в CSS. Утверждаю, что CSS в JS — это проблема скорее React-разработчиков и их мета-платформы. У них таких проблем много, но погрязнув в React-е они начинают эти проблемы экстраполировать на всю веб-разработку. И говорить о "будущем" тут очень некорректно, будущее (а для кого-то уже давно настоящее) — за веб-компонентами и нативными CSS.


    1. justboris
      01.06.2017 10:31

      Посмотрите мой комментарий выше, пункт 0 именно о том, что вы говорите.


      Не надо заблуждаться что CSS-in-JS это про то, что стили теперь делаются на JS. Нет, анимации все так же делаются на CSS, каскад и :hover тоже доступны. Зато, от переноса исходного кода стилей (именно исходного, а не конечного результата) в JS можно получить некоторое удобство.


      Утверждаю, что CSS в JS — это проблема скорее React-разработчиков и их мета-платформы.

      CSS-in-JS не проблема, а решение. Как вы осуществляете ленивую подгрузку стилей на своей любимой платформе? Как вы изолируете стили, чтобы они применялись только к конкретному компоненту?


      CSS-in-JS предлагает по максимуму переиспользовать уже готовые инструменты для JS, вместо изобретения дополнительных инструментов для CSS.


      1. i360u
        01.06.2017 10:59

        Я видел ваш комментарий выше и даже плюсанул его. А в остальном — вы, прежде чем говорить о дополнительных инструментах, ленивой подгрузке, изоляции стилей и т.д, поинтересуйтесь хотя-бы что такое веб-компоненты, почитайте их актуальные спецификации, посмотрите доклады на эту тему с последнего Google I/O на ютубе… Это не очень объемные материалы, но обещаю — это очень полезно и позволяет во многом по новому взглянуть на современную веб-платформу и на ее ключевые отличия от популярных мета-платформ и прочих костылей.
        Лично я эти вещи использую уже довольно давно, успел наработать свой эффективный подход, и мне, порой, просто дико смотреть на все эти "решения" несуществующих проблем. Ну и PostCSS поизучайте вдогонку.


        1. justboris
          01.06.2017 11:23

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


          И Shadow DOM так просто не полифиллится, там такие костыли и проблемы с производительностью, что я уж лучше на JSS посижу, пока в браузерах нормальная поддержка не появится.


          1. raveclassic
            01.06.2017 11:24
            +1

            Ну и shadow dom все еще в working draft. А зная, как эти ребята любят все менять (вспомнить флексбоксы), я пока держусь от всего этого безобразия подальше.


          1. i360u
            01.06.2017 11:38

            У нас в команде Хром — референсный браузер, но мы также все детально тестируем в остальных (кроме экзотики конечно). Часто сталкиваюсь с заявлениями о том, что полифилы тормозят. Блин, я бы согласился с этим пару лет назад, но сейчас мне непонятно откуда вы это берете. А мы делаем довольно сложный фронт. Ну и нельзя сказать, что только в Хроме компоненты поддерживаются (а это, если что, самый популярный браузер и он сейчас задает темп для остальных), ибо не только. Есть конечно нюансы, технология проходит долгий и трудный путь, но, если сравнивать с остальным зоопарком — для меня выбор очевиден.


            1. justboris
              01.06.2017 11:55

              Подозреваю, что вы пользуетесь не чистыми веб-компонентами, а Polymer. Это важно, потому что последние версии Polymer достигли более-менее приличной производительности, только когда перешли с честных полифиллов, на упрощенные, которые по своей сути работают так же как с CSS-modules или CSS-in-JS. Вся разница только в том, как это называть, а суть одна и та же: мы генерируем СSS с динамическими именами классов.


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


              1. i360u
                01.06.2017 12:12

                У меня сейчас есть три проекта: на чистых компонентах, на Polymer 1.9.1 и на Polymer 2.0.1 ))) То, что вы говорите относится только к версиям 1.x


              1. SPAHI4
                01.06.2017 15:20
                +2

                В современных браузерах, которых большинство, как раз все отлично работает, без полифиллов и костылей с именами классов. На смартфонах Polymer — один из лучших вариантов для PWA в плане производительности.

                Пример приложения https://shop.polymer-project.org/


                1. justboris
                  01.06.2017 15:34

                  В Firefox, Internet Explorer не поддерживается. Так что все равно придется думать о фоллбеке. И в качестве фоллбека там работает такой же точно механизм, что и с CSS-in-JS с процессингом и динамической генерацией стилей в браузере пользователя.


                  Кроме того, синтаксис определения Polymer компонентов мне кажется неудобным, уж лучше CSS в JS писать. Но это уже тема другой дискуссии.


                  1. SPAHI4
                    01.06.2017 15:36

                    Не так уж и неудобно. А кому надо, так есть набор декораторов для TS.


                  1. mayorovp
                    01.06.2017 16:13

                    И в качестве фоллбека там работает такой же точно механизм, что и с CSS-in-JS с процессингом и динамической генерацией стилей в браузере пользователя.

                    И почему же это плохо?


                    1. justboris
                      01.06.2017 16:18

                      Я не говорю что это плохо.


                      Просто я не понимаю, почему на CSS-in-JS такая негативная реакция, в то время как Polymer делает то же, и всех это устраивает.


                      1. raveclassic
                        01.06.2017 16:26

                        Сейчас вам начнут петь о каких-то разделениях, которые необходимы, иначе все, капец. И JSX еще припомнят.


                        1. justboris
                          01.06.2017 17:47

                          У меня есть прекрасная иллюстрация на эту тему


                          Картинка

                          image


                          1. raveclassic
                            01.06.2017 21:36

                            Изумительно! Сохраню себе


                      1. mayorovp
                        01.06.2017 21:46

                        Потому что важен язык разметки, а не его реализация.


                        1. raveclassic
                          01.06.2017 22:39
                          -1

                          Ну вот почему никто не может принять, что jsx — это не разметка, не маркап, не html, да как угодно. Это инструмент компоновки самодостаточных блоков интерфейса. То, что он похож на html — это лишь деталь. Ну а если мы настолько от привычной разметки абстрагируемся, то какая разница, какой dsl использовать для описания внешнего вида этих блоков? Css, jss, да что угодно.


        1. justboris
          01.06.2017 11:27
          +1

          Лично я эти вещи использую уже довольно давно, успел наработать свой эффективный подход, и мне, порой, просто дико смотреть на все эти "решения" несуществующих проблем.

          Расскажите поподробнее, как вы делаете лениво загружаемые стили и рендерите критический CSS. Без троллинга, реально интересно, как это можно сделать, не перенося CSS в JS.


          1. i360u
            01.06.2017 11:41

            Я уже два раза начинал писать статью про все это, и оба раза откладывал, т.к. времени мало, а рассказать хочется очень многое. Обещаю в скором времени взять себя в руки и написать. Боюсь придется родить целый цикл, если никто не опередит =)


            1. Synoptic
              01.06.2017 12:55

              Не могу плюсануть, но тоже очень хотелось бы почитать и статью, и весь цикл.


  1. dreammaster19
    01.06.2017 10:38
    +1

    CSS уже доволно хорошо поддерживает множество фишек, типа переменных и т.д. А плохо структурированный код можно написать и в SASS с LESS, так же как и сделать хорошо оформленный CSS. Как мне кжется такие статьи просто для того, тобы сказать: «Ух, какая прикольная штука. Давайте пользоваться все».