
Введение
Существует множество подходов для того, чтобы стилизовать React-компоненты, и в этой статье будут рассмотрены некоторые из них. Но, для начала, определимся с ключевыми моментами, на которых основано повествование:
Использование React предполагает, что View нашего Web-приложения разбито на компоненты, и каждый из них, так или иначе, отвечает за собственное отображение и функциональность.
Подразумевается, что будут использованы современные инструменты разработки вроде babel, webpack, browserify, gulp, post- pre- css-процессоры.
Время подобных решений осталось в прошлом:
<!doctype html> <!--[if lt IE 7]> <html class="ie6 oldie"> <![endif]--> <!--[if IE 7]> <html class="ie7 oldie"> <![endif]--> <!--[if IE 8]> <html class="ie8 oldie"> <![endif]--> <!--[if gt IE 8]><!--> <html class=""> <!--<![endif]-->
- Существуют такие инструменты как autoprefixer, и больше не нужно писать подобный CSS руками:
background: #1e5799; /* Old browsers */ background: -moz-linear-gradient(top, #1e5799 0%, #7db9e8 100%); /* FF3.6-15 */ background: -webkit-linear-gradient(top, #1e5799 0%,#7db9e8 100%); /* Chrome10-25,Safari5.1-6 */ background: linear-gradient(to bottom, #1e5799 0%,#7db9e8 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#1e5799', endColorstr='#7db9e8',GradientType=0 ); /* IE6-9 */
- React можно рендерить как на клиенте, так и на сервере. Есть множество проектов, которые используют эту замечательную возможность. За этим будущее.
Про стилизацию DOMElement-ов
React-компоненты рендерятся в настоящий HTML, т.е. в DOMElement-ы. Существует два способа настроить отображение DOMElement-а:
- Внешние CSS стили (тег style в разметке страницы, либо подключение файла стилей через тег link).
- Внутренние (inline) стили (атрибут style).
Второй подход имеет множество недостатков:
- Невозможность использования media-queries, например, чтобы реагировать на изменение ширины экрана и отличать мобильное устройство от десктопа.
- Невозможность использования псевдоклассов и псевдоэлементов вроде :hover, :active, ::placeholder, :before, :after и прочих.
- Значительное увеличение размера разметки и снижение производительности . В случае серверного рендеринга увеличится еще и нагрузка на канал трафика, а также время загрузки страницы пользователем.
- Невозможность переопределять стили по более сложным селекторам. Отсюда невозможность привести отображение в разных браузерах к одному виду (reset.css, normalize.css и т.д.)
Некоторые из этих недостатков могут компенсироваться комбинированием 2-го подхода с 1-ым, но не все.
Про политику Facebook
Известно, что Facebook, как компания-разработчик React, несмотря на описанные выше недостатки всё равно продвигает использование Inline Styles. Их можно понять, они стремятся обеспечить единообразный подход с React Native, ибо там только Inline Styles и имеются. Но нам от этого не легче, ведь для Web сам Facebook в плане стилизации React-компонентов ничего толкового не предлагает. Зато нашлось множество энтузиастов, которые написали свои велосипеды. Далее мы рассмотрим самые популярные из них и разберемся с основными подходами стилизации React-компонентов.
Эволюция подходов для стилизации React-компонентов без Inline Styles
Пишем один внешний файл со стилями, подключаем его и используем в React-компонентах className со значениями имен классов из этого файла.
Всё точно так же как в предыдущем пункте, но разбиваем стили на множество файлов в соответствии с компонентами. Далее каким-нибудь инструментом собираем все файлы в один и подключаем его. Для избежания коллизий имен используем, например, БЭМ.
/* Button.js */ import React, { Component } from 'react'; export default class Button extends Component { render() { return ( <div className = {'button ' + (this.props.isRed ? 'button--red' : '')}> <div className = 'button__caption'> {this.props.caption} </div> </div> ); } }
/* Button.css */ .button { line-height: 32px; border: 1px solid #000; } .button__caption { text-decoration: underline; text-align: center; } .button--red { background-color: red; }
- Развивая идею модульности, можно перейти к использованию CSS-Modules.
/* ScopedSelectors.js */ import styles from './ScopedSelectors.css'; import React, { Component } from 'react'; export default class ScopedSelectors extends Component { render() { return ( <div className = {styles.root}> <p className = {styles.text}> Scoped Selectors </p> </div> ); } };
/* ScopedSelectors.css */ .root { border-width: 2px; border-style: solid; border-color: #777; padding: 0 20px; margin: 0 6px; max-width: 400px; } .text { color: #777; font-size: 24px; font-family: helvetica, arial, sans-serif; font-weight: 600; }
Использование CSS-Modules подразумевает, что имена стилей будут доступны только локально, т.е. каждый React-компонент может иметь свой стиль root, и никаких коллизий не будет — имена стилей трансформируются при сборке. Также существует возможность определить глобальные стили, которые будут доступны везде — их имена трансформироваться не будут. Для поддержки CSS-Modules со стороны Node.js существует проект css-modules-require-hook, либо можно использовать Webpack для компиляции серверного кода приложения (статья про компиляцию серверного кода в двух частях — 1, 2).
Про все эти подходы можно добавить, что к ним успешно и легко прикручиваются autoprefixer и всякие less, sass, postcss, stylus с константами, импортами, миксинами и прочими плюшками. Также нет никаких проблем с подсветкой синтаксиса и подсказками в IDE. В рамках этого списка будет легко переключиться с одного подхода на другой.
Стилизация React-компонентов на основе идей Inline Styles
Существует множество решений на этот счет, они очень разные и навязывают свой единственно возможный вариант их использования, со всеми вытекающими последствиями: сложность миграции на что-то другое, свой особый синтаксис, декораторы, волшебные классы, от которых нужно наследоваться — придется учить новый framework, чтобы этим пользоваться. Ряд решений не работает с серверным рендерингом или не поддерживает autoprefixer, который должен отрабатывать на этапе компиляции — не тащить же его в браузер.
Рассмотрим несколько самых известных представителей данного подхода:
Radium
Хороший проект, который поддерживает media queries и псевдоклассы, например, :hover. Он предлагает следующий синтаксис:
const styles = { button: { padding: '1em', ':hover': { border: '1px solid black' }, '@media (max-width: 200px)': { width: '100%', ':hover': { background: 'white', } } }, primary: { background: 'green' }, warning: { background: 'yellow' }, }; ... <button style={[styles.button, styles.primary]}> Confirm </button>
Чтобы это всё работало, при объявлении компонентов нужно использовать декоратор Radium.
React Style
Используется такой же синтаксис, как и в стандартных Inline Styles для React, но со слегка расширенными возможностями.
import StyleSheet from 'react-style'; const styles = StyleSheet.create({ primary: { background: 'green' }, warning: { background: 'yellow' }, button: { padding: '1em' }, // media queries '@media (max-width: 200px)': { button: { width: '100%' } } }); ... <button styles={[styles.button, styles.primary]}> Confirm </button>
Как и в Radium, работают media queries, но, в отличие от него, не поддерживаются псевдоклассы и анимация средствами CSS, есть loader для Webpack.
JSS
Поддерживает autoprefixer, псевдоклассы и media queries. Может использоваться с React через react-jss. Также существует экспериментальный jss-loader для Webpack. Пример использования:
import classNames from 'classnames'; import useSheet from 'react-jss'; const styles = { button: { padding: '1em' }, 'media (max-width: 200px)': { button: { width: '100%' } }, primary: { background: 'green' }, warning: { background: 'yellow' } }; @useSheet(styles) export default class ConfirmButton extends React.Component { render() { const {classes} = this.props.sheet; return <button className={classNames(classes.button, classes.primary)}> Confirm </button>; } }
jsxstyle
Проект вводит свой волшебный синтаксис, не поддерживает media queries, зато есть loader для Webpack.
// PrimaryButton component <button padding='1em' background='green' >Confirm</button>
Есть неплохой англоязычный обзор всех этих технологий.
Заключение
Нельзя однозначно сказать, какое из решений лучше остальных — всё зависит от конкретных задач и ограничений. Какому-то проекту больше подойдут CSS-Modules, другому хватит БЭМ-именований для классов, третьему — что-то основанное на идее Inline Styles.
Выбор подхода для стилизации React-компонентов — это серьезное архитектурное решение, которое оставит свой след в коде всего проекта, во многом определит инструменты его разработки и повлияет на множество других факторов, связанных с поддержкой кода и скоростью работы приложения в целом. Надеюсь, данный обзор будет полезен Web-разработчикам как краткая справочная информация о разных подходах к стилизации React-компонентов и поможет оценить риски при выборе своего собственного пути.
