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


«Зачем кому-то писать CSS в JS?
Конечно, это ужасная идея!
Им бы просто выучить CSS!»


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


image


Сообщества непонимания


React-сообщество часто не понимает CSS-сообщество, и наоборот. Меня эта ситуация интересует особенно, поскольку я оказался зажат меж двух миров. Я начал изучать HTML в конце 1990-х, и профессионально работал с CSS ещё с тёмных времён макетов на основе таблиц. Вдохновлённый CSS Zen Garden, я был в первых рядах миграции кодовых баз в сторону семантической разметки и каскадных таблиц стилей. Вскоре я стал одержим разделением работы, используя ненавязчивый JavaScript для оформления разметки, сделанной на сервере, действиями на клиенте. Вокруг этого подхода сложилось небольшое, но бурное сообщество, и мы стали первым поколением фронтенд-разработчиков, старавшихся отдать браузерной платформе заслуженное уважение.


Так что вы понимаете, что с таким опытом по разработке для веба я был рьяным критиком React-модели HTML-in-JS, которая, как казалось, шла наперекор оберегаемым нами принципам. Но фактически всё оказалось наоборот. По моему опыту, компонентная модель React в сочетании с его возможностью отрисовки на сервере наконец-то позволила нам массово создавать сложные одностраничные приложения, предоставляя пользователям быстрые, доступные и прогрессивные продукты.


Так что можете считать это знаком примирения одного сообщества с другим. Давайте вместе разберёмся, что означает тенденция объединения CSS с JS. Возможно, результат не совершенен, он может не подходить для использования в ваших продуктах. Но как минимум заслуживает вашего внимания.


Зачем нужен CSS-в-JS?


Если вы знакомы с моей недавней работой с React и CSS-модулями, то наверняка удивлены, что я защищаю CSS-в-JS.


image


В конце концов, CSS-модули обычно выбирают те разработчики, которые хотят получить стили в локальной области видимости (locally scoped styles) без того, чтобы поверить в CSS-в-JS. По сути, я даже не использую CSS-в-JS в своей работе. Несмотря на это, я питаю неподдельный интерес к CSS-в-JS-сообществу, внимательно слежу за нововведениями, которые они постоянно предлагают. Более того, я считаю, что это в интересах всего CSS-сообщества. Но почему? Чтобы вы поняли, почему люди предпочитают писать свои стили на JavaScript, давайте рассмотрим практические преимущества этого подхода.


Я разбил статью на пять основных частей:


  1. Стили в области видимости (Scoped styles)
  2. Критический CSS
  3. Более грамотные оптимизации
  4. Управление пакетами
  5. Внебраузерное применение стилей

1. Стили в области видимости (Scoped styles)


Не секрет, что чрезвычайно трудно эффективно строить большую архитектуру CSS. Когда разработчик присоединяется к уже давно существующему проекту, то для нередко труднейшей для него работой становится становится разбирательство с CSS. Чтобы снизить остроту проблемы, CSS-сообщество вложило много усилий в улучшение сопровождаемости стилей, разработав такие подход, как OOCSS и SMACSS. Но самым популярным решением сегодня является BEM, или Block Element Modifier.


По сути, BEM (применённый к чистому CSS) — всего лишь соглашение о наименованиях, ограничивающее стили классами, соблюдающими паттерн .Block__element--modifier. В любой кодовой базе, использующей BEM, разработчики всегда должны следовать правилам этого соглашения. При строгом соблюдении BEM работает хорошо. Но почему на усмотрение соглашения оставлена такая фундаментальная вещь, как определение области видимости (scoping)? Не важно, говорят они об этом явно или нет, но большинство CSS-в-JS-библиотек следуют образу мыслей BEM, стараясь применять стили к отдельным элементам интерфейса, но реализуют это совершенно разными способами. Как это выглядит на практике? Например, в случае с glamor:


import { css } from 'glamor'
const title = css({
  fontSize: '1.8em',
  fontFamily: 'Comic Sans MS',
  color: 'blue'
})
console.log(title)
// > 'css-1pyvz'

Как вы могли заметить, нигде в коде нет CSS-класса. Это больше не жёстко прописанная ссылка на класс, определённый где-то в системе. Теперь она автоматически генерируется для нас библиотекой. Больше не нужно беспокоиться о селекторах, конфликтующих в глобальной области видимости, а значит нам больше не нужно вручную ставить им префиксы.


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


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




Прежде чем продолжать дальше, нужно обговорить крайне важный момент. Это генерирование настоящего CSS, а не инлайновых стилей. Большинство из ранних CSS-в-JS-библиотек прикрепляли стили напрямую к каждому элементу. Но критическим недостатком этой модели является то, что атрибуты ‘style’ не могут делать всё то, что может CSS. Многие новые библиотеки теперь сосредоточены на динамических таблицах стилей, вставляя и удаляя в ходе исполнения правила из глобального набора стилей.


В качестве примера давайте рассмотрим JSS, одну из ранних CSS-в-JS-библиотек для генерирования настоящего CSS.


image


При использовании JSS вы можете воспользоваться стандартными CSS-возможностями, вроде hover-стилей и медиа-запросов, которые напрямую проецируются на эквивалентные CSS-правила.


const styles = {
  button: {
    padding: '10px',
    '&:hover': {
      background: 'blue'
    }
  },
  '@media (min-width: 1024px)': {
    button: {
      padding: '20px'
    }
  }
}

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


const { classes } = jss.createStyleSheet(styles).attach()

Эти сгенерированные классы можно использовать вместо жёстко прописанных строк классов при генерировании разметки на JavaScript. Этот паттерн работает вне зависимости от того, используете ли вы полноценный фреймворк или что-то простое, наподобие innerHTML.


document.body.innerHTML = `
  <h1 class="${classes.heading}">Hello World!</h1>
`

Такое управление стилями само по себе имеет мало значения — обычно оно используется в паре с какой-нибудь компонентной библиотекой. Поэтому в большинстве популярных библиотек встречаются биндинги. Например, JSS может с помощью react-jss легко биндиться на React-компоненты, внедряя маленькие наборы стилей в каждый компонент, тем самым управляя своим глобальным жизненным циклом.


import injectSheet from 'react-jss'
const Button = ({ classes, children }) => (
  <button className={classes.button}>
    <span className={classes.label}>
      {children}
    </span>
  </button>
)
export default injectSheet(styles)(Button)

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


Совершенно новый подход к этой проблеме был представлен в styled-components авторства Glen Maddern и Max Stoiber.


image


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


import styled from 'styled-components'
const Title = styled.h1`
  font-family: Comic Sans MS;
  color: blue;
`

Применяя такие стили, мы не прикрепляем класс к существующему элементу. Мы просто отрисовываем сгенерированный компонент.


<Title>Hello World!</Title>

В то время, как styled-components использует традиционный CSS-синтаксис посредством маркированных шаблонных литералов (tagged template literals), другие предпочитают работать со структурами данных. Интересной альтернативой является Glamorous.


image


Glamorous предлагает тот же component-first API, что и styled-components, но оперирует объектами, а не строками, благодаря чему можно не включать в библиотеку CSS-парсер — это уменьшает размер библиотеки и влияние на производительность.


import glamorous from 'glamorous'
const Title = glamorous.h1({
  fontFamily: 'Comic Sans MS',
  color: 'blue'
})

Какой бы синтаксис вы не выбрали для описания своих стилей, они больше не находятся в одной области видимости с компонентами — они от них неотделимы. При использовании библиотек наподобие React, компоненты являются базовыми строительными блоками, и теперь наши стили формируют основную часть архитектуры. Если мы описываем всё в нашем приложении как компоненты, то почему бы не описать так и стили?




Для закалённых ветеранов BEM всё это может выглядеть относительно мелким улучшением, учитывая важность изменения в системе. Фактически, CSS-модули позволяют добиться этого без потери комфорта от использования экосистемы CSS-инструментов. Так много проектов используют CSS-модули, поскольку те успешно решают большинство проблем с написанием больших объёмов CSS без необходимости жертвовать знакомым обычным CSS.


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


Глава 2. Критический CSS


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


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


Совершенно другое дело CSS-в-JS. При работе с приложением, которое отрисовывается на сервере, извлечение критического CSS не просто оптимизация — CSS-в-JS на сервере априори требует, чтобы критический CSS использовался в первую очередь. Например, при использовании Aphrodite система отслеживает, какие стили используются в рамках одиночного прохода отрисовки с применением своей функции css, которая вызывается инлайново, когда применяются классы к вашим элементам.


import { StyleSheet, css } from 'aphrodite'
const styles = StyleSheet.create({
  title: { ... }
})
const Heading = ({ children }) => (
  <h1 className={css(styles.heading)}>{ children }</h1>
)

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


import { StyleSheetServer } from 'aphrodite';
const { html, css } = StyleSheetServer.renderStatic(() => {
  return ReactDOMServer.renderToString(<App/>);
});
Теперь вы можете отрисовывать свои критические CSS-блоки:
const criticalCSS = `
  <style data-aphrodite>
    ${css.content}
  </style>
`;

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


Поскольку отрисовка вашего HTML и CSS на сервере выполняется одновременно, библиотеки вроде Aphrodite зачастую помогают нам организовывать генерирование критического CSS и отрисовку HTML на сервере в рамках одного вызова, как мы видели в предыдущем примере. Теперь мы можем аналогичным образом отрисовывать React-компоненты в статичном HTML.


const appHtml = `
  <div id="root">
    ${html}
  </div>
`

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


Глава 3. Более грамотные оптимизации


Недавно мы наблюдали возникновение новых способов структурирование CSS — например, Atomic CSS и Tachyons — которые сторонятся «семантических классов» в пользу маленьких, специализированных классов. Например, при использовании Atomic CSS вы применяете классы с синтаксисом наподобие функций, которые потому могут использоваться для генерирования соответствующей таблицы стилей.


<div class="Bgc(#0280ae.5) C(#fff) P(20px)">
  Atomic CSS
</div>

Цель — сохранить наш CSS-пакет как можно более компактным за счёт максимального повторного использования классов, обращаясь с классами как с инлайновыми стилями. Это идёт на пользу размеру файла, при этом влияние на кодовую базу и членов вашей команды совершенно незначительное. Сама натура этих оптимизаций подразумевает изменение одновременно CSS и разметки, что делает их более важным с точки зрения архитектуры. Как уже упоминалось, при использовании CSS-в-JS или CSS-модулей вам больше не нужно жёстко прописывать в разметке строки классов. Вместо этого используются динамические ссылки на JavaScript-значения, которые автоматически генерируются библиотекой или сборочным инструментом.


Вместо этого:


<aside className="sidebar" />

Мы пишем это:


<aside className={styles.sidebar} />

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


В предыдущем примере ‘styles.sidebar’ вычисляется в строку, но ничто не ограничивает его одним классом. Насколько известно, это может быть строка из более чем десятка классов.


<aside className={styles.sidebar} />
// Could easily resolve to this:
<aside className={'class1 class2 class3 class4'} />

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


Мой любимый пример — Styletron.


image


Так же, как CSS-в-JS и CSS-модули автоматизируют процесс добавления в классы префиксов а-ля BEM, так и Styletron делает то же самое для Atomic CSS. Основной API заточен под одну задачу: определение конкретных CSS-правил для каждой комбинации свойства, значения и медиа-запроса, который потом возвращает автоматически сгенерированный класс.


import styletron from 'styletron';
styletron.injectDeclaration({
  prop: 'color',
  val: 'red',
  media: '(min-width: 800px)'
});
// > 'a'

Конечно, Styletron предоставляет API более высокого уровня, такие как функция injectStyle, которая позволяет определять одновременно несколько правил.


import { injectStyle } from 'styletron-utils';
injectStyle(styletron, {
  color: 'red',
  display: 'inline-block'
});
// > 'a d'
injectStyle(styletron, {
  color: 'red',
  fontSize: '1.6em'
});
// > 'a e'

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


image


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


Глава 4. Управление пакетами


Сначала зададим себе обманчиво простой вопрос:


Как нам делиться друг с другом CSS?


Мы мигрировали от ручного скачивания CSS-файлов к специальным серверным менеджерам пакетов наподобие Bower, а затем через npm к инструментам вроде Browserify и webpack. Даже хотя некоторые из инструментов автоматизировали процесс включения CSS из внешних пакетов, фронтенд-сообщество по большей части придерживалось ручного включения CSS-зависимостей.


В то же время, есть одна вещь, для которой CSS-зависимости не слишком хороши: зависимость от других CSS-зависимостей. Как многие из вас помнят, аналогичный эффект с JavaScript-модулями мы наблюдали между Bower и npm. Bower не был связан с каким-то определённым форматом модулей, в то время как модули, публикуемые в npm, использовали формат CommonJS. Это оказало серьёзное влияние на многие пакеты, публикуемые на каждой платформе.


Комплексные деревья маленьких, вложенных (nested) зависимостей хорошо воспринимались в npm, в то время как Bower был склонен к большим, монолитным зависимостям, которых можно было иметь две или три — плюс несколько плагинов, конечно. Поскольку у зависимостей не было модульной системы, на которую можно было положиться, пакеты не могли просто использовать свои собственные зависимости, поэтому интегрировать приходилось вручную, причём самим потребителям.


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


image


К сожалению, для CSS-сообщества всё это выглядит очень знакомо. Мы тоже наблюдали относительно медленный рост монолитных пакетов по сравнению с JavaScript-пакетами на npm. А что если мы хотели соответствовать экспоненциальному росту npm? Что если мы хотели иметь возможность зависеть от сложных иерархий пакетов разного размера, не сосредотачиваясь на больших, всеохватывающих фреймворках? Для этого нам не только нужен был менеджер пакетов, который выполнит задачу, нам также нужен был соответствующий формат модулей.


Означает ли это, что нам нужен был менеджер пакетов, спроектированный специально для CSS? Для препроцессоров вроде Sass и Less? Действительно интересно то, что мы уже прошли через аналогичную реализацию в HTML. Если вы зададите тот же вопрос применительно к тому, как мы делимся друг с другом разметкой, то быстро заметите, что мы почти никогда не делимся напрямую чистым HTML — мы делимся HTML-в-JS. Делается это посредством jQuery-плагинов, Angular-директив и React-компонентов. Мы собираем большие компоненты из маленьких, каждый со своим собственным HTML, каждый независимо публикуется в npm. Как формат HTML для этого недостаточно силён, но встраивая HTML в полностью сформировавшийся язык программирования мы смогли легко обойти это ограничение.


А что если, как и в случае с HTML, мы бы делились CSS — и генерируемой им логикой — посредством JavaScript? Что если вместо миксинов мы применяли бы функции, возвращающие объекты и строки? Если вместо расширяющих классов мы просто объединяли объекты с Object.assign, или использовали новый оператор распределения объектов (object spread operator)?


const styles = {
  ...rules,
  ...moreRules,
  fontFamily: 'Comic Sans MS',
  color: 'blue'
}

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


image


Polished — это, по сути Lodash в мире CSS-в-JS. Она предоставляет сложный набор миксинов, цветовых функций, сокращений и так далее, делая процесс авторинга стилей в JavaScript гораздо более привычным для тех разработчиков, которые приходят из языков наподобие Sass. Ключевым отличием является то, что теперь этот код гораздо удобнее для комбинирования, тестирования и передачи другим; он способен использовать всю экосистему JavaScript-пакетов.


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


Глава 5. Внебраузерное применение стилей


Пока что все рассмотренные моменты — которые гораздо проще, когда пишешь CSS в JavaScript — это отнюдь не являются невозможными при использовании обычного CSS. Поэтому я оставил самое интересное напоследок. Что-то, не обязательно играющее огромную роль в текущем CSS-в-JS-сообществе, вполне возможно в будущем может стать фундаментом в дизайне. Что-то, влияющее не только на разработчиков, но и на дизайнеров, радикально меняющее взаимодействие этих двух дисциплин. Но сначала давайте проведём краткий экскурс в React.




Используемая в React модель завязана на компонентах, отрисовывающих немедленное представление финального результата. При работе в браузере мы вместо прямого манипулирования DOM-элементами строим сложные деревья виртуального DOM. Интересно то, что отрисовка в DOM не является частью основной React-библиотеки, она обеспечивается react-dom.


import { render } from 'react-dom'

Хотя React и была сначала создана для DOM, и до сих пор по большей части используется в этой среде, но всё же сама модель позволяет использовать React в самых разных средах просто за счёт введения новых рендереров.


JSX — это не просто виртуальный DOM, это виртуальное что угодно. Это то, что позволяет работать React Native, писать на JavaScript по-настоящему нативные приложения с помощью компонентов, которые отрисовывают виртуальные представления своих нативных двойников. Вместо div и span у нас теперь View и Text. С точки зрения CSS, самое интересное в React Native заключается в том, что он идёт с собственным StyleSheet API:


var styles = StyleSheet.create({
  container: {
    borderRadius: 4,
    borderWidth: 0.5,
    borderColor: '#d6d7da',
  },
  title: {
    fontSize: 19,
    fontWeight: 'bold',
  },
  activeTitle: {
    color: 'red',
  }
})

Здесь вы видите знакомы список стилей, в данном случае относящихся к цветам, шрифтам и границам. Эти правила довольно просты и легко применяются к большинству UI-окружений. Гораздо интереснее, когда речь заходит о нативных макетах.


var styles = StyleSheet.create({
  container: {
    display: 'flex'
  }
})

Несмотря на то, что она расположена вне браузерного окружения, React Native поставляется с собственной нативной реализацией flexbox. Изначально она была выпущена как JavaScript-пакет css-layout, реализующего flexbox полностью на JavaScript (при поддержке соответствующего комплексного набора тестов). Теперь она мигрировала на С ради лучшей портируемости. Учитывая масштабы и важность проекта, он получил более значительное название — Yoga.


image


Хотя Yoga предназначен для портирования CSS-концепций в небраузерные окружения, потенциально неограниченная область его применения была ограничена только CSS-фичами. «Yoga предназначен для создания выразительной библиотеки макета, а не для реализации всего CSS». Подобные компромиссы выглядят ограничениями, но если посмотреть на историю CSS-архитектуры, то станет понятно, что работа с большими объёмами CSS связана с выбором подходящего подмножества языка.


Авторы Yoga избежали каскада в пользу стилей в области видимости, и заточили весь движок макетов под flexbox. Это лишает нас многочисленной функциональности, но открывает невероятную возможность для использования кроссплатформенных компонентов со встроенными стилями, и мы уже видели несколько известных opensource-проектов, пытающихся извлечь выгоду из этого факта.


React Native for Web предназначен для замены библиотеки react-native. При использовании бандлера (bundler) вроде Webpack можно легко использовать привязку сторонних пакетов.


module: {
  alias: {
    'react-native': 'react-native-web'
  }
}

Использование React Native for Web позволяет компонентам React Native работать в браузерном окружении, включая браузерное портирование React Native StyleSheet API.


Аналогично, react-primitives обеспечивает кроссплатформенный набор примитивных компонентов, абстрагированных от особенностей целевой платформы, создающих рабочую основу для кроссплатформенных компонентов.


Даже Microsoft представила ReactXP, библиотеку, разработанную для упрощения передачи кода между веб- и нативными проектами, также включающую в себя их собственную платформонезависимую реализацию стилей.




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


image


Многие из нас тратят кучу времени, пытаясь стандартизировать язык дизайна, как можно больше ограничивая количество дублирования в системах. К сожалению, поскольку мы хотели бы иметь только один источник правды, казалось бы, лучшее, на что мы могли бы надеяться, это сократить количество до двух источников — руководство для разработчиков по «живым стилям» (living style) и руководство для дизайнеров по статичным стилям. Хотя это однозначно лучше того, к чему мы привыкли исторически, всё же это подразумевает ручную синхронизацию из дизайнерских инструментов — вроде Sketch — в код и обратно. И здесь поможет react-sketchapp.


Благодаря JavaScript API, используемому в Sketch, а также возможности React подключаться к разным отрисовщикам, react-sketchapp позволяет брать наши кроссплатформенные React-компоненты и отрисовывать их в Sketch-документах.


image


Очевидно, что это перетряхнёт взаимодействие между дизайнерами и разработчиками. Теперь, когда при итерировании наших дизайнов мы ссылаемся на одни и те же компоненты, мы можем ссылаться и на одни и те же реализации, независимо от того, с какими инструментами мы работаем — для дизайнеров или разработчиков. С символами в Sketch и компонентами в React мы начинаем сходиться в единой абстракции, что открывает возможность плотнее взаимодействовать в работе, используя общие инструменты. Не случайно, что так много из этих новых экспериментов берут своё начало в React-сообществе и смежных с ним.


В компонентной архитектуре становится высоко приоритетной задача совмещения как можно большего количества компонентов в одном месте. Конечно, сюда входят и стили в локальной области видимости, но благодаря библиотекам вроде Relay и Apollo охватываются и более сложные сферы, вроде извлечения данных. Это открывает перед нами необъятные возможности, и мы только копнули сверху. Это оказывает огромное влияние на применение стилей в наших приложениях, и столь же большое влияние — на всё остальное в нашей архитектуре. И не без оснований.


Унифицируя нашу модель с точки зрения компонентов, написанных на едином языке, мы получаем возможность лучше разделять наши усилия — не по технологиям, а по функциональности. Сосредоточивая всё вокруг модуля из компонентов, собирая из них большие, но удобные в сопровождении системы, которые оптимизированы так, как раньше мы не помышляли, просто и быстро делясь друг с другом своей работой и комбинируя большие приложения из маленьких строительных opensource-блоков. И что ещё важнее, всё это мы можем делать без того, чтобы ломать прогрессивное расширение, без отбрасывания принципов, которые многие считают неотъемлемой частью серьёзного подхода к веб-платформе.


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


К примеру, у себя в SEEK мы создаём своё руководство по «живым стилям» на основе этой компонентной модели, где семантика, взаимодействие и визуальное оформление объединены в рамках единой абстракции. В результате формируется язык дизайна, общий для разработчиков и дизайнеров. Создание страницы должно быть таким же простым, как комбинирование необходимых компонентов — это гарантирует, что наша работа соответствует бренду, но при этом мы можем обновлять наш язык дизайна гораздо позже отправки в production.


import {
  PageBlock,
  Card,
  Text
} from 'seek-style-guide/react'
const App = () => (
  <PageBlock>
    <Card>
      <Text heading>Hello World!</Text>
    </Card>
  </PageBlock>
)

Хотя наше руководство по стилям сейчас построено на React, Webpack и CSS-модулях, архитектура точно отражает всё, что вы нашли бы в любой системе, построенной с помощью CSS-в-JS. Технологии могут быть выбраны разные, но подход остаётся тем же. Однако в будущем нам скорее всего придётся как-то корректировать свой выбор, и поэтому наблюдение за тенденциями крайне важно для продолжающейся разработки нашей компонентной экосистемы. Вероятно, сегодня мы не будем использовать CSS-в-JS, но совсем не исключено, что в скором времени возникнет убедительная причина перейти на эту технологию.


CSS-в-JS эволюционировал удивительно сильно за короткое время, но важно отметить, в общей схеме эта технология находится лишь в начале своего пути. Ей ещё есть куда развиваться, постоянно появляются нововведения. По-прежнему выпускаются библиотеки для нерешённых пока задач и улучшения процесса разработки — увеличения производительности, извлечения статичного CSS при сборке, таргетирования CSS-переменных и снижения уровня входа для всех фронтенд-разработчиков. В этом направлении движется CSS-сообщество. Несмотря на существенные изменения в нашем рабочем процессе, ничто не отменяет того факта, что нам всё ещё необходимо знать CSS. Мы можем выражать это разным синтаксисом, мы можем по-разному строить архитектуру наших приложений, но фундаментальные строительные CSS-блоки никуда не делись. Наша индустрия неизбежно движется к компонентной архитектуре, и желание переосмыслить фронтенд через её призму лишь крепнет. Нам совершенно необходимо работать вместе, чтобы наши решения были широко применимы среди разработчиков с любым опытом — дизайнерским, инженерным или обоими.


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


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


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

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


  1. copal
    04.08.2017 13:21
    +5

    Из всех пяти перечисленных в начале плюсов, выделил для себя только один, последний, но после прочтения стало понятно, что мне, пишущему только под вэб, предлагают полюбить cssinjs потому, что кто-то, наверняка 0,,,00000%, пишет на react native. Получается что 0,001% разработчиков, которые решают каждый день проблемы, которые по другому решить невозможно, решили что теперь всем нужен их cssinjs. В большинстве случаев причины надуманы. Вместо css нужно загрузить столько же js + кавычки + сам cssinjs.


    1. maxminimus
      04.08.2017 13:38
      +2

      JS+CSS+HTML — это уже готовый прекрасный удобный фреймворк

      Логика+оформление+структура


    1. Zo0m3R
      04.08.2017 13:47
      +1

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


      1. copal
        04.08.2017 14:14
        +2

        Ну вот лично я никогда не сталкивался с проблемой перекрытия идентификаторов в css. А размер загружаемого css, при правильном подходе к сборке, будет, если не меньше, то точно не больше, чем c inline (который как раз увеличивает размер) или созданию из js. Понимаете, все хотят быть лучше и наверняка мечтают что в будущем, технологии смогут сделать нас совершеннее, как вид. Но если это спроецировать на конкретную проблему, то окажется, что вместо стильного, умного, многофункционального костюма, как у супергероев, с Вас сдерут кожу и кости до мяса и вкрутят дрелью стальные рельсы. Есть разница? Вот также и с css. решать проблему с помощью сборщиков, означает создать костюм супер героя, то есть не вмешиваться в процесс — внутренности, в которые предлагает лезть jsincss.


        1. raveclassic
          05.08.2017 03:18
          -2

          Ну вот смотрите, пример из жизни. Педалим мы значит некую отчетность на реакте. Все красиво, css-модули, никаких стилей в js. И тут приходит такой менеджер и говорит: "А мы можем наши красивые контролы/таблички/графики отрендерить в pdf?". И мы такие "Конечно мож… Упс". Нет не можем, ибо css. А могли бы, ибо есть рендереры для css-in-js.


          Edit: фантом не предлагайте, бьет рендеринг.


          1. Aingis
            05.08.2017 10:59

            Лол, что?! Буквально недавно делали стили для печати на обычном CSS с тем же Реактом.


            1. raveclassic
              05.08.2017 13:04

              Не путайте @media print с автоматической генерацией pdf на бэкенде.


              1. Aingis
                05.08.2017 13:11

                То есть какая-то поделка не умеет в CSS и виновата в этом технология? Охренеть!


                1. raveclassic
                  05.08.2017 13:20

                  Вы чего тут беситесь? Какая поделка? На ваш любимый css никто не наезжает.


          1. vintage
            05.08.2017 13:15

            А могли бы, ибо есть рендереры для css-in-js.

            Рендереры в pdf? И не бьют рендеринг?


            1. raveclassic
              05.08.2017 13:20
              -1

              Тоже бьют =(


              1. vintage
                05.08.2017 15:13
                +1

                А если не видно разницы — зачем платить больше? :-)


      1. RomanYakimchuk
        05.08.2017 06:47

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

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


        и загрузки-отрисовки

        Загрузка отрисовка в случае процессинга шаблонов в процессе сборки будет даже выше. Если вам нужна оптимизация, можно даже сделать инлайн-стиль (там где это возможно; так делают под низкопроизводительные платформы), и всё будет еще быстрее, потому что в таком случае ни JS, ни каскадности классов не будет, и на браузер будет абсолютный минимум нагрузки.


        Поэтому, судя по всему, такое решение (в статье) вызвано лишь стеком.


        Мои коллеги работали с БЭМ — имхо, это монстр, с которым можно либо работать на его условиях (БЭМ-стек), либо будешь собирать море граблей. В данном случае, возможно, это решение является одним из таких шагов, чтобы на БЭМ было удобно работать, но в обычных проектах CSS в JS не нужен, это плохая практика.


      1. Large
        06.08.2017 14:58
        -1

        Для модульности есть shadow dom, вообще использование веб компонентов решает большинство из озвученных выше проблем.


  1. pm_wanderer
    04.08.2017 13:49
    +3

    Я не сторонник CSS-in-JS. Сейчас попробую объяснить почему.
    Стили и html могут писать «низкооплачиваемые» участники процесса разработки, типа верстальщиков/дизайнеров.
    Отверстав по стайл гайду все возможные состояния компонентов, они могут передать свою работу далее программисту, который привяжет внешнее состояние сверстанных компонентов к модели приложения/сайта.

    Например:
    Допустим у некой кнопки есть два состояния (нажата — зеленый бэкграунд, отжата — красный). Дизайнер написал стили для каждого состояния и повесил их на разные селекторы. Программист привязывает эти состояния к событиям в системе и при возникновении условного события buttonOn вешает на компонент соответствующий ему класс.

    Как из этого всего можно сделать модули?
    Тут необходим подход по типу Vue:
    Помещаем в один файл с расширением .component разметку на html/jade, стили на css/scss, логику на js. Все это лежит тремя блоками в тегах div, style, script. Далее специальный сборщик все это обрабатывает и аккуратно раскладывает по полочкам: стили добавляет в css-файл, разметку подставляет в html и скрипты добавляет в js-файл.

    Все просто и логично: верстальщик создал свою часть компонента, а программист — свою. И никаких десятков библиотек и препроцессоров в зависимостях. А то у React-сообщества уж сильно тяга к овер-инжинирингу проявляется)

    Вот таким я вижу будущее компонентной разработки)


    1. igontarev
      04.08.2017 14:03
      +4

      будущее, где один компонент типа кнопки пишут несколько человек :)


      1. pm_wanderer
        04.08.2017 14:21
        +3

        Вполне неплохое будущее)
        Фронтендеры часто жалуются на обилие технологий, которые надо изучать. Мой подход позволяет им, при желании, отстраниться от нюансов CSS и HTML и позволить верстальщикам самим писать хаки для IE6 внутри стилей, а сеошникам писать в HTML свою микроразметку для поисковиков. Освободившееся время они могут потратить на тусовки и развлечения.

        В моем будущем программист — свободный человек, который не беспокоится о том, что вышел очередной фреймворк. В будущем от Facebook и Google ему уготовано лишь «eat, sleep, code, repeat»))


      1. oklas
        08.08.2017 11:56

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


  1. igontarev
    04.08.2017 15:21
    +1

    к чему столько сложностей, давайте просто будем писать стили как мы привыкли на css, less, sass и использовать css modules, каждый может работать в своем файле, и не важно будь это react или vue или еще что-то

    import theme from './theme.scss'
    
    function Button() {
        return <button className={theme.button} />
    }
    


  1. aliev
    04.08.2017 15:36
    +5

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


  1. dom1n1k
    04.08.2017 16:50
    +2

    Не очень убедительно.


  1. x07
    04.08.2017 19:19

    В ангуляре из коробки идёт изоляция стилей компонента. В двух разных компонентах на одной странице могут использоваться разные по стилям классы, но с одинаковым именем и при этом они друг на друга не будут влиять. И самое главное, все пишется в привычных файлах CSS,scss,stylus. Почему бы не применить этот подход, который отлично работает?


    1. vintage
      05.08.2017 13:20

      В двух разных компонентах на одной странице могут использоваться разные по стилям классы, но с одинаковым именем

      Что выносит мозг программисту при отладке.


      1. oklas
        08.08.2017 12:26

        Что-то невероятное вообще написано: "css классы с одинаковым именем и разными стилями"? Или имеется в виду класс из языка программирования то тогда наверно надо говорить об объекте, т.к. класс только характеризует каким может быть объект, но у объекта не может быть имени (если мы не дадим ему такого свойства).


        1. raveclassic
          08.08.2017 13:09

          Ангуляр составляет селектор из его имени и рандомного аттрибута типа .btn[asd234]. Так что может быть 2 компонента с селектором .btn, но стили будут разные из-за аттрибута.


          Это все невероятно доставляет, ибо хваленая инкапсуляция стилей (в отличие от css-модулей) работает только наполовину и любой такой класснейм в глобале протечет в несчастную кнопку. Думаю, что вы догадались по имени селектора кнопки, какой именно движок протекает?


      1. oklas
        08.08.2017 12:48

        Вообще такое неизбежно при разработке подсистем группа разработки может именовать внутренние классы как будет удобно. Для этого в c++ используется namespace а в js да разные классы могут быть объявлены в разных scope иногда могут совпадать имена, при этом сами они будут разными. Но они должны использоваться только внутри своих подсистем и не быть доступными снаружи для пользователя данной библиотеки или подсистемы. Если они должны быть видны снаружи они обычно должны иметь общий префикс в имени. При отладке обычно такие классы используются внутри своих подсистем и отлаживаются при отладке своих подсистем и покрываются тестами. Поэтому при отладке системы которая использует эти подсистемы на прямую работать с такими внутренними классами не должно быть необходимости.


        1. vintage
          08.08.2017 13:49

          Ну вот и представьте: у вас есть диалог, запиленный одной группой людей, в нём форма, запиленная второй группой людей, в нём календарик, запиленный третьей группой людей, вам нужно разобраться почему календарик распирает диалог, а каждая из этих 3 труп людей посчитала своим долгом дать своим блокам лаконичные имена вида "main", "box" и тому подобные. В итоге вас в стилях: .main[erucqi], .main[ifdofi] и .main[reoo]. Счастливой отладки.


          1. oklas
            08.08.2017 15:38

            Группы могли бы всё назвать по разному, для этого им потребуется общий реестр. Если бы был реестр то имена были бы не .main[erucqi], .main[ifdofi] и .main[reoo] а например .dialog_main, .form_main, .calendar_main. То есть при отладке разница будет лишь в именах.


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


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


            1. vintage
              08.08.2017 15:45

              Группы могли бы всё назвать по разному, для этого им потребуется общий реестр.

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


              Напрашивается возможность в отладчике задать именам классов определяемые пользователем псевдонимы (alias)

              Чтобы ещё больше всех запутать?


              1. oklas
                08.08.2017 16:05

                Кто-то приходит, кто-то проходит мимо, это дело вкуса.


                Механизм css так устроен что одно на другое очень сильно влияет, один только clearfix чего стоит. Одна из причин почему появился flex. Всё собрано в один глобальный css с глобальными именами. Это проблема вызвана самим css, в котором нет пространства имен. Здесь рассмотрены разные методы решения этих проблем. Их не навязывают, можете их не использовать это дело вкуса.


                А то что у кого то в отладчике прописано временно для отладки показывать .dialog_main вместо .main[erucqi] никого другого не запутает.


                1. vintage
                  08.08.2017 16:39

                  ММеханизм css так устроен что одно на другое очень сильно влияет, один только clearfix чего стоит.

                  Вы не смешивайте тёплое с мягким. При чём тут конфликты имён?


                  Это проблема вызвана самим css, в котором нет пространства имен.

                  Пространства имён есть везде, где есть имена. Ваши префиксы "dialog", "form" и "calendar" — пространства имён в чистом, не замутнённом синтаксическим сахаром, виде.


                  в отладчике прописано временно для отладки показывать .dialog_main вместо .main[erucqi] никого другого не запутает.

                  Запутает того, кто подойдёт помочь разобраться. Да и вас самих запутает тоже с ростом разношёрстных алиасов. Если вам не лень каждый класс каждого компонента именовать вручную в отладчике, то почему бы не делать это сразу в исходниках?


                  1. oklas
                    08.08.2017 17:01

                    Вы не смешивайте тёплое с мягким. При чём тут конфликты имён?

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


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


          1. Druu
            09.08.2017 09:27
            +1

            > вам нужно разобраться почему календарик распирает диалог, а каждая из этих 3 труп людей посчитала своим долгом дать своим блокам лаконичные имена вида «main», «box» и тому подобные. В итоге вас в стилях: .main[erucqi], .main[ifdofi] и .main[reoo].

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


            1. vintage
              09.08.2017 10:17

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


              1. raveclassic
                09.08.2017 11:44

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


              1. Druu
                09.08.2017 15:02

                > Чтобы понять кто что отрендерил и нужны глобально уникальные имена.

                Зачем? Если вы знаете что нода от компонента Х — то у нее стили именно от этого компонента.

                > Или вы предлагаете интерпретировать код у себя в голове, пытаясь понять какой из элементов к которому из нескольких одноимённых компонент относится?

                Не могу себе даже в теории представить ситуацию, когда непонятно к какому компоненту относится инспектящаяся нода. Без каких-либо интерпретаций в голове. Можете привести пример?


                1. vintage
                  09.08.2017 17:39

                  Зачем? Если вы знаете что нода от компонента Х — то у нее стили именно от этого компонента.

                  Как понять, что она от компонента X?


                  Не могу себе даже в теории представить ситуацию, когда непонятно к какому компоненту относится инспектящаяся нода.

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


                  1. Druu
                    10.08.2017 03:19

                    > Как понять, что она от компонента X?

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

                    > компонентами высшего порядка в реакте не пользовались?

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

                    > Трансклюдами в ангуляре

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

                    > А есть фреймворки и с более динамичной компоновкой…

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


                    1. raveclassic
                      10.08.2017 04:11

                      Это же известный антипаттерн.

                      Может мне хоть кто-нибудь это объяснить?


                      стили инлайнят

                      давно уже не инлайнят


                      компоненты сильно дробят

                      что плохого?


                      за неимением адекватных средств, действительно, приходится использовать HOC

                      Раскажите, пожалуйста, какие адекватные средства вы имеете в виду, и почему за неимением оных "приходится использовать" HOC. И почему, собственно, HOC плохи?


                      1. Druu
                        10.08.2017 05:03

                        > давно уже не инлайнят

                        И как тогда решается проблема с инкапсуляцией классов?

                        > что плохого?

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

                        > Раскажите, пожалуйста, какие адекватные средства вы имеете в виду

                        Трансклюд, директивы

                        > И почему, собственно, HOC плохи?

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


                        1. raveclassic
                          10.08.2017 13:23

                          И как тогда решается проблема с инкапсуляцией классов?

                          При чем тут инкапсуляция? Под "давно уже не инлайнят" я имел в виду CSSOM и экстракт в css-файлы. Или вы о чем?


                          Если дробления нет — для той же задачи достаточно одного мимолетного взгляда.

                          Ну я даже не знаю… Вы за трех-экранные полотна маркапа? Если нет, то где тогда грань? И как же декомпозиция, как же переиспользуемость?


                          Трансклюд, директивы

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


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

                          Не понимаю, о чем вы.


                          1. Druu
                            10.08.2017 15:27

                            > При чем тут инкапсуляция?

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

                            > Под «давно уже не инлайнят» я имел в виду CSSOM и экстракт в css-файлы. Или вы о чем?

                            При чем тут экстракт? Я говорил об использовании. Под «инлайнингом» подразумевался подход, эквивалентный styled components, когда вы тем или иным способом указываете стиль для конкретного дом-элемента, в противовес классическому css подходу с указанием стиля для селектора, то, что сейчас подразумевается под css-in-js. Если вы под css-in-js подразумеваете просто генерацию стилей на ходу — то так оно и в ангуляре css-in-js тогда, там после компиляции никаких .css не остается.

                            > Ну я даже не знаю… Вы за трех-экранные полотна маркапа? Если нет, то где тогда грань? И как же декомпозиция, как же переиспользуемость?

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

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

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


                            1. raveclassic
                              10.08.2017 15:37

                              Вот я повесил на дом-ноду какого-то компонента класс, описал для него стиль, как обеспечивается изоляция этого стиля?

                              Эээ, а откуда вы это класс взяли? Из глобала? Ну так какая тогда инкапсуляция? Если из css-модуля — эти стили заинкапсулированы в компоненте, который импортирует этот css-модуль. Пусть даже и в глобале все так же, но с уникальным селектором.


                              Грань определяется золотой серединой

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


                              в ангуляре есть и HOC

                              Что, простите?


                              Все используют директивы

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


                              Послушайте, у меня нет никакого желания опять поднимать эту тему и устраивать тут недельную разборку. Я вас спрашивал, чем плохи HOC, не надо опять соскакивать в обсуждение angular vs react.


                              1. Druu
                                10.08.2017 15:54

                                > Пусть даже и в глобале все так же, но с уникальным селектором.

                                С каким селектором?

                                > А где эта середина?

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

                                > Что, простите?

                                То, простите. HOC — неотъемлемая часть ангуляра, например, на каждый класс компонента применяется HOC «Component».

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

                                HOC тоже не генерит оберток (откуда?). Но используют директивы.

                                > Я вас спрашивал, чем плохи HOC, не надо опять соскакивать в обсуждение angular vs react.

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


                                1. raveclassic
                                  10.08.2017 16:09

                                  С каким селектором?

                                  Что-нибудь вида .Button__element_34732, как настроите.


                                  Я же описал.

                                  Это-то и понятно. Если перечитаете мой предыдущий комментарий, поймете, к чему такие вопросы.


                                  на каждый класс компонента применяется HOC «Component».

                                  Ух ты чушь какая. У меня складывается ощущение, что вы не понимаете, что из себя представляет higher order component. HOC — это компонент, принимающий в качестве "аргумента" другой/другие компоненты и инстанцирующий/рендерящий их, а не просто декоратор.


                                  Вот это HOC.


                                  const List = ({Item}) => <ul><Item>item</Item><ul>;

                                  Вот это HOC.


                                  const HOC = () => (Target) => {
                                    return class Wrapped extends Component {
                                      static displayName = `Wrapped(${Target.name})`;
                                      render() {
                                        return <Target {...this.props} addSomeProp={true}/>;
                                      }
                                    }
                                  };

                                  Да, они разные, и, если честно, первый вариант мне больше кажется HOC, нежели второй.


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


                                  @Component({
                                    selector: 'foo-foo',
                                    template: 'i\'m a foo'
                                  })
                                  class Foo {
                                  }

                                  HOC тоже не генерит оберток (откуда?). Но используют директивы.

                                  См. предыдущий пункт


                                  1. Druu
                                    10.08.2017 16:33

                                    > Это-то и понятно. Если перечитаете мой предыдущий комментарий, поймете, к чему такие вопросы.

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

                                    > HOC — это компонент, принимающий в качестве «аргумента» другой/другие компоненты и инстанцирующий/рендерящий их, а не просто декоратор.

                                    Во-первых, HOC — не компонент. Это функция. Функция, которая принимает компонент (функцию или класс) и возвращает компонент (функцию или класс). В каком месте это не «просто декоратор»?

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

                                    Это не аннотация, это функция, которая принимает класс и возвращает класс. Как-нибудь так:

                                    const Component = (annotation) => (target) => {
                                        return class Wrapped extends target {
                                            ...
                                        }
                                    }
                                    


                                    > См. предыдущий пункт

                                    Практически любую директиву можно переписать в HOC наподобие вашего второго примера. Но так никто не делает. Почему?


                                    1. raveclassic
                                      10.08.2017 17:16

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

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


                                      Во-первых, HOC — не компонент. Это функция. Функция, которая принимает компонент (функцию или класс) и возвращает компонент (функцию или класс). В каком месте это не «просто декоратор»?

                                      HOC — higher order Component. И именно это показывает List. А дальше я специально уточнил, что я не совсем согласен с зародившейся в экосистеме реакта называть фабрики компонентов компонентами высшего порядка.


                                      Это не аннотация, это функция, которая принимает класс и возвращает класс.

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


                                      Практически любую директиву можно переписать в HOC наподобие вашего второго примера. Но так никто не делает. Почему?

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


                                      1. Druu
                                        11.08.2017 03:58

                                        > Чего? Полотна разметки и там и там одинаковые

                                        Нет, не одинаковые. В реакте у вас map'ы и ?:, которые делают код совершенно нечитабельным. В итоге приходится выделять содержащий подобные вещи код в отдельные функции. Которые иначе были бы не нужны.

                                        > и степень вырвиглазности в отвратного DSL ангуляра повыше будет

                                        Даже плохой DSL почти всегда лучше универсального языка. Именно по-этому DSL создаются (ведь это требует дополнительной работы), именно по-этому от jsx-подходов на бекенде отказались давным-давно.

                                        > HOC — higher order Component.

                                        HOC — не компонент. Как бы он ни назывался. Он не обладает свойствами компонента, его нельзя использовать как компонент. Обратите внимание на аналог — HOF. HOF — по факту _является_ ф-ей, обладает св-вами функции, может использоваться как ф-я. Видите разницу? В любом случае, давайте не будем заниматься спором о терминологии, мы говорим о конкретной сущности, а как ее называть — дело десятое.

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

                                        Нет, это не так, конечно. Любой класс является классом компонента сходу, а Component — лишь связывает этот класс с его видом, добавляя во внешнюю конфигурацию соответствующую информацию. Сообщает движку, что «если ты увидишь этот селектор то надо создать данный вид и привязать к указанному компоненту». У одного компонента может быть много видов, к слову. Вы же сами в курсе, что аннотации из Component не наследуются — потому что они не являются св-вами полученного класса, и вообще к этому классу отношения не имеют. Иными словами, класс компонента в ангуляре не содержит ф-и «render». Эта ф-я живет отдельно от компонента и к нему не привязана. С точки зрения ментальной модели реакта это трудно понять, т.к. в реакте как раз ф-я «render» сама по себе уже является компонентом (вид = модель). Но фреймворки разные, ничего удивительного. Надо эту разницу в рассуждениях учитывать.

                                        > Нет нельзя, так как у вас появится dom-обертка.

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

                                        > Поведение модели должно определяться и расширяться моделью, а не нагромождением заклинаний в маркапе.

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


                                        1. raveclassic
                                          11.08.2017 10:35

                                          В реакте у вас map'ы и ?:, которые делают код совершенно нечитабельным.

                                          Все, прекратите, каша из звездочек, скобочек и "коробочек" уж никак не лучше обычных языковых конструкций, субъективщина чистой воды.


                                          Он не обладает свойствами компонента, его нельзя использовать как компонент.

                                          Вот что вы мне тут рассказываете


                                          const List = ({Item, items}) => (
                                            <ul>
                                              {items.map((item, i) => (
                                                <Item key={i}>{item}</Item>
                                              ))}
                                            </ul>
                                          );
                                          
                                          const Item = ({children}) => <li>{children}</li>;
                                          
                                          const result = <List Item={Item} items={[1,2,3]}/>;

                                          Компонент? Компонент. Высшего порядка? Высшего.


                                          Модель — класс компонента.
                                          Связывание маркапов с моделями производится на внешенм этапе конфигурировани

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




                                          И вы так и не объяснили, чем плохи HOC.


                                          1. Druu
                                            11.08.2017 18:53

                                            > Все, прекратите, каша из звездочек, скобочек и «коробочек» уж никак не лучше обычных языковых конструкций, субъективщина чистой воды.

                                            Это объективный факт. Сообщество много лет назад давным-давно признало, что генерить портянки руками — ад и погибель, и все перешли на шаблонизаторы в том или ином виде. То, что на волне хайпа фейсбуку удалось продать наивным, не нюхавшим пороху пионерам от фронтенда фреймворк, предназначенный для портирования протухшего легаси-кода с php на js — просто исторический казус.

                                            Тот факт, что jsx нечитаем — подтверждают сами люди, которые пишут на jsx, когда выделяют ветки тех же ?: в отдельные ф-и.

                                            > Высшего порядка? Высшего.

                                            Нет, конечно. Это обычный компонент, который возвращает обычный кусок ДОМ. HOC же возвращает компонент, а не кусок ДОМ.

                                            > Только меня смущает не это, а то, что добавление поведения к модели (классу компонента) происходит из маркапа другого компонента.

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

                                            И, в итоге, так и не получается ответить на вопрос: если в ангуляре есть и директивы, и HOC, которые могут выполнять одну и ту же задачу, и HOC решают эту задачу лучше, то почему все используют директивы?


                                            1. raveclassic
                                              11.08.2017 21:00

                                              что генерить портянки руками

                                              Ну так не генерите портянки. Декомпозиция, все дела, я уже устал это повторять.


                                              То, что на волне хайпа фейсбуку удалось продать наивным, не нюхавшим пороху пионерам от фронтенда фреймворк, предназначенный для портирования протухшего легаси-кода с php на js

                                              О, ну да, ну да


                                              Тот факт, что jsx нечитаем — подтверждают сами люди, которые пишут на jsx, когда выделяют ветки тех же ?: в отдельные ф-и.

                                              Такой же факт подтверждают те, кто пишет вот такой бред:


                                              <div *ngIf=”condition; else elseBlock”>Truthy condition</div>
                                              <ng-template #elseBlock>Falsy condition</ng-template>

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


                                              Нет, конечно. Это обычный компонент, который возвращает обычный кусок ДОМ. HOC же возвращает компонент, а не кусок ДОМ.

                                              Давайте вспомним функции высшего порядка, ведь именно по этой аналогии появились компоненты высшего порядка. Так вот, фвп — функция либо принимающая другую функцию в виде аргумента, либо возвращающая другую функцию, либо и то, и то.
                                              Так же и с компонентами, либо принимаем в аргументы, либо возвращаем новый, либо все сразу. Так вот при первом упоминании я описал вам оба варианта: List принимает функцию, Wrapper возвращает.
                                              А dom тут вообще не при чем, это результат выполнения функции (компонента), принимающей другую (другой компонент), то есть первый случай.


                                              Надеюсь, мы это утрясли. Если нет — определение HOF все проясняет.


                                              Никто никакого поведения к модели не добавляет.

                                              А как же @Host в связке с @HostBinding и @HostListener? Как-то все сразу вперемешку становится. Сами же мне говорили использовать директивы для связки тупых компонентов со стором ngrx. И дело тут не в реакте или в "неправильной ментальной модели" а в том, что вместо явного создания нового компонента через HOC с подмешанным поведением, приходится прямо из маркапа обвешивать несчастного дополнительными конструкциями, что-то в него пихающими и слушающими его события.


                                              И, в итоге, так и не получается ответить на вопрос: если в ангуляре есть и директивы, и HOC, которые могут выполнять одну и ту же задачу, и HOC решают эту задачу лучше, то почему все используют директивы?

                                              Еще раз, в ангуляре нет HOC. @Component — это не компонент. Компонент — это результат его выполнения. Так вот результирующий компонент уже никак (почти) не может быть компонентом высшего порядка. Почти — потому-что все же есть механизм динамического инстанцирования, и можно передать класс другого компонента через @Input, но это жутко неудобно и ограниченно. А вот вернуть другой компонент при "вызове" этот уже никак не может.


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


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


                                              1. Druu
                                                12.08.2017 06:48

                                                > Ну так не генерите портянки.

                                                А как в реакте не генерить портянки, если другого способа не предоставлено?

                                                > Декомпозиция, все дела, я уже устал это повторять.

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

                                                > Такой же факт подтверждают те, кто пишет вот такой бред:

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

                                                > и мы уже наконец закроем тему синтаксиса.

                                                Я не затрагивал тему синтаксиса.

                                                > Давайте вспомним

                                                Ок. Утверждение «HOC является компонентом» — ложно, так как существуют HOC, которые компонентами не являются. В точности так же, как является ложным утверждение вида «Человек — это негр». На этом спор по части терминологии предлагаю закрыть.

                                                > А как же Host в связке с @HostBinding и @HostListener?

                                                Это реализация директивы, а не ее использование. Вы когда директиву используете, то вообще не в курсе, какие там @HostListener внутри. У вас задача — сделать, чтобы компонент мигал. Это свойство вида, которое никак не затрагивает логику работы самого компонента. По-этому информация о том, что компонент — мигучий, расположена в разметке, это вид.

                                                > а в том, что вместо явного создания нового компонента через HOC с подмешанным поведением, приходится прямо из маркапа обвешивать несчастного дополнительными конструкциями

                                                Если бы в jsx можно было использовать компоненты как <Hoc(Component) .../>, не объявляя промежуточных, то большинство бы так и делало. И было бы это таким же обвешиванием из маркапа. И от того, что вы промежуточную переменную создаете — в сущности ничего не меняется.

                                                > Еще раз, в ангуляре нет HOC. Component — это не компонент. Компонент — это результат его выполнения.

                                                Конечно.

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

                                                Он и не должен. HOC — это Component, а не результат работы Component.

                                                > А вот вернуть другой компонент при «вызове» этот уже никак не может.

                                                Но Component возвращает компонент при вызове. Любой декоратор это делает.

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

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

                                                > Так что вопроса как такового нет. Все используют директивы, потому-что по-другому никак.

                                                Очень даже «как». Практически все, что может директива, можно сделать при помощи НОС. Но НОС не используют.

                                                Еще раз — НОС не используют не для «переопределения рендер функции» (это и директивой нельзя), а для тех вещей, который можно сделать директивой. И их же (в основном) можно сделать в НОС.


                                                1. raveclassic
                                                  12.08.2017 12:31

                                                  А как в реакте не генерить портянки, если другого способа не предоставлено?

                                                  Молча, как везде. Бьете на компоненты и собираете в композицию.


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

                                                  Ну это только вам там кажется. Вы же не пишете (надеюсь) функции/методы по 500 строк?


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

                                                  Аааааа, вы прикалываетесь? Это же корявое месиво из невнятных конструкций, сплошной синтаксический шум.


                                                  Я не затрагивал тему синтаксиса.

                                                  Что же это, если не синтаксис разметки?


                                                  У вас задача — сделать, чтобы компонент мигал

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


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

                                                  Так, надоело, давайте пример HOC WithBlink, из обычного компонента делающего мигающий. Учтите, что базовый компонент уже аннотирован @Component, а WithBlink оборачивать нельзя, он же "принимает компонент — возвращает компонент"


                                                  потому что в директиве вы не можете переопределить разметку,

                                                  Забавно, но в реакте я могу это сделать. Внутри HOC я могу пробежаться по результату выполнения оборачиваемого компонента и делать вообще что душе угодно. Догадываюсь, что вы скажете, что это "неправильная ментальная парадигма" и так делать нельзя, фу фу фу.


                                                  Еще раз — НОС не используют не для «переопределения рендер функции» (это и директивой нельзя), а для тех вещей, который можно сделать директивой. И их же (в основном) можно сделать в НОС.

                                                  Мне кажется, вы лихо сели в оборону и съехали на "HOC" в ангуляре, тогда как


                                                  реакт, там все, действительно, совсем плохо — стили инлайнят (привет технологиям десятилетней давности), компоненты сильно дробят, за неимением адекватных средств, действительно, приходится использовать HOC с другими подобными костылями и т.д…

                                                  ну и самое важное


                                                  Это же известный антипаттерн.

                                                  жду обоснований, пока только вода


                                                  1. Druu
                                                    12.08.2017 13:54

                                                    > Бьете на компоненты и собираете в композицию.

                                                    Ну так мы возвращаемся к началу — в случае с шаблонами не бью, а в случае с jsx — вынужден бить, когда длоя этого по факту нету никаких причин кроме «иначе код вырвиглазен». И это чрезмерное разделение приводит к негативному результату, значительно повышая сложность приложения.

                                                    > Аааааа, вы прикалываетесь? Это же корявое месиво из невнятных конструкций, сплошной синтаксический шум.

                                                    Синтаксис не существенен, это вопрос привычки. В пример можно привести известную картинку о лиспе и скобках. А вот структура кода — важна весьма.
                                                    И, насчет синтаксиса — будем честны, в JSX месива никак не меньше.

                                                    > Мне не интересно это мигание.

                                                    Но именно эту задачу мы и рассматриваем. Еще раз, по порядку. Есть задача: сделать мигающий компонент (и другие задачи подобного класса). В данном случае мы работаем с видом и это свойство вида, а не модели. Решить задачу можно через НОС, можно через директиву. На практике — решают ее через директиву. Почему?

                                                    > Учтите, что базовый компонент уже аннотирован Component, а WithBlink оборачивать нельзя, он же «принимает компонент — возвращает компонент»

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

                                                    > Мне кажется, вы лихо сели в оборону и съехали на «HOC» в ангуляре, тогда как

                                                    Я вам констатирую один простой наблюдаемый факт: есть класс задач, которые в ангуляре решаются как при помощи HOC, так и при помощи директив. При этом люди выбирают директивы. На основании этого я делаю гипотезу: директивы являются значительно более хорошим решением. Альтернативная гипотеза — существую какие-то иные причины. ОЧевидна, одна из этих причин верна. Я этих иных причин не вижу, вы их привести не можете. Отсюда я делаю вывод, что таких причин нет и верна первая гипотеза.

                                                    > жду обоснований, пока только вода

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

                                                    Здесь единственное, что следует уточнить — возможно, НОС не являются антипаттерном именно _в контексте реакта_. То есть, да, ясно, что решение плохое — но точно так же ясно, что плохое решение лучше, чем никакого, а других вариантов в реакте нету. На безрыбье — и рак рыба, что называется.

                                                    Но мы же рассуждаем о концепции в целом?


                                                    1. raveclassic
                                                      12.08.2017 14:42

                                                      в случае с шаблонами не бью

                                                      Вы предпочитаете полотна? Ну фиговый из вас разработчик тогда. И про методы на 500 строк промолчали.


                                                      Но именно эту задачу мы и рассматриваем.

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


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

                                                      Ну так не надо говорить тогда, что это HOC. У вас банально функция будет принимать класс и возвращать класс. Поверх которого потом еще и @Component жахнуть надо. И от обертки вы не избавились. И пример не привели. Не можете — не надо спорить.


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

                                                      лол какой копипасте? у вас какое-то ну совсем странное мышление. повышение порядка — это повышение уровня абстракции и гибкости и как следствие снижение копипасты.


                                                      Оно нарушает большую часть принципов SOLID

                                                      В каком месте HOC нарушает хоть что-то?


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

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


                                                      Но мы же рассуждаем о концепции в целом?

                                                      Нет. Мы рассуждаем о HOC в контексте реакта, так как в ангуляре их нет. Есть функции, которые принимаю класс и возвращают класс. Хотите называйте их фвп, как хотите. Но они ни являются компонентами, ни возвращают компоненты. Ни одно из условий для HOC не выполняется.


                                                      В общем понятно, какое-то размусоливание, а по делу ничего. Давайте закончим.


                                                      1. Druu
                                                        13.08.2017 10:43

                                                        > Вы предпочитаете полотна?

                                                        При чем тут полотна? Естественно, код разделяется на компоненты. Но семантически, когда это нужно. А когда это не нужно (как в случае искусственного выделения условных веток) — не разделяется.

                                                        > Я ее не рассматриваю

                                                        То есть вы просто игнорируете неудобные для своей гипотезы факты?

                                                        > Ну так не надо говорить тогда, что это HOC. У вас банально функция будет принимать класс и возвращать класс.

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

                                                        > И от обертки вы не избавились.

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

                                                        > И пример не привели.

                                                        Я пример не привел потому, что вы изначально потребовали надуманные невыполнимые условия. Давайте я потребую привести от вас в реакте аналогичный HOC, который можно будет применять, не объявляя отдельную переменную для результирующего класса. Вы пример не приведете, потому что его написать невозможно, и я объявлю, что НОС в реакте не существует. Глупость же.

                                                        > повышение порядка — это повышение уровня абстракции и гибкости и как следствие снижение копипасты.

                                                        Это, конечно, далеко не всегда так.

                                                        > В каком месте HOC нарушает хоть что-то?

                                                        S, O, D — нарушается откровенно.

                                                        > Ну окей, если вам нравится такая модель общения — в реакте нет директив, потому-что они не нужны, так как есть HOC, выразительные, гибкие, понижающие связность кода, упрощающие его, убирающие копипасту и повышающие качество кода в целом. Как вам?

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

                                                        > Нет. Мы рассуждаем о HOC в контексте реакта, так как в ангуляре их нет.

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

                                                        > Но они ни являются компонентами, ни возвращают компоненты.

                                                        Они не являются компонентами в реакте, но являются компонентами в ангуляре. Понятие компонента в ангуляре и реакте — разное. В реакте не существует компонентов в понимании ангуляра (т.к. класс без логики рендера не является компонентом), а в ангуляре не существует компонентов в понимании реакта (т.к. компонент не может содержать логику рендера).


            1. raveclassic
              09.08.2017 11:41

              Ну я же чуть выше написал.
              Вот у вас в компоненте есть элемент .container, стили к нему, соответственно, будут в селекторе .container[hsagfkj]. Но вот незадача, заказчик захотел свой кастомный бутстрап в виде css. И, вот же незадача, у вашего элемента вдруг от куда не возьмись появились паддинги по бокам...


              Держать это все в голове, думая над именами селекторов? Ну так это уже не инкапсуляция, а пародия.


              1. Druu
                09.08.2017 15:06

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

                Это проблема реализации (shadow dom эмулирутеся), а не подхода. Перейдут все на нативный shadow dom — проблема сама собой исчезнет.

                > Держать это все в голове, думая над именами селекторов?

                Зачем? Можно решить эту проблему так, как вы решали бы ее с css-in-js — вместо того, чтобы импортировать свой бутстрап глобально, импортируйте его в каждую компоненту, локально. Все точно так же и с точно тем же результатом.


  1. Shifty_Fox
    05.08.2017 12:01

    Ну, первая (вторая) змея адаптивная, а вторая (третья) резиновая


  1. toy-php
    05.08.2017 18:54

    О, боги, ubiquitous JavaScript!!!