Русский
Русский
English
Статистика
Реклама

Frontend-разработка

Микросервисы во фронтенде

11.09.2020 10:13:26 | Автор: admin

image


Привет, Хабр!
Микросервисы уже давно стали классикой в бэкенд архитектуре, но в отличие от бэкенда, во фронтенде одновременно можно использовать только один фреймворк. Это порождает неудобства при масштабировании и переиспользовании модулей. Микросервисный подход избавляет от необходимости придерживаться конкретного фреймворка и дает возможность программисту сосредоточиться на конкретном модуле с использованием наиболее удобных для него инструментов. Мы не пропагандируем расширение зоопарка технологий и использование каждого фреймворка необходимо тщательно обдумать, прежде чем внедрять. Подробнее в статье от разработчика команды BSL gyk007


Сразу спойлер уже готов мини фреймворк, который легко позволит вам это сделать.


Начну с того, зачем это нужно


Еще совсем недавно web был простым. Для успешной работы необходимо было знать html, css и популярную на тот момент, библиотеку jquery. На этом знания фронтенд-программиста заканчивались, да и за программистов их особо не считали, так, верстальщики или веб-мастера. Но веб стремительно развивается, интерфейсы становятся все сложнее и сейчас для многих проектов, в которых нет HighLoad и микросервисов (а они в большинстве случаев и не нужны), клиентская часть становится сложнее, чем серверная, и время жизни кода на клиенте обычно меньше, чем на сервере.
Также появляется целая армия фреймворков, которые по сути делают одно и то же, но разными способами. И программисты выбирают свой стек: кто-то React, кто-то Vue и так далее.
Но наше веб-приложение живет долго, а фреймворки и UI-библиотеки меняются быстро. В итоге через пару лет мы получаем легаси, который хочется переписать, но это долго, а бизнес не готов за это платить приложение же работает.
Я думаю, многие программисты слышали про закон Иглсона: Ваш код, который вы не просматривали 6 или более месяцев, выглядит так, будто его написал кто-то другой.


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


Учитывая все вышесказанное, хотелось бы иметь не один большой монолит, который хочется переписать, а набор микросервисов (назовем их модули, каждый из которых максимально изолирован, чтобы при необходимости мы могли быстро его заменить или написать новый на другом стеке (если, конечно, это нужно). И тестировать только те модули, код которых менялся.
Чтобы решить все проблемы, я написал фреймворк, который легко позволяет делить приложение на модули, причем каждый модуль при необходимости может иметь свой стек (React, Vue, Angular, Webix . )


Что мы получили


Модули (микросервисы)


Любое веб-приложение можно разбить на модули (хедер, футер, окно с чатом, боковое меню и т.д..


Вот основные их типы:


  • Root-модуль главный модуль, один на все приложение. Отвечает за взаимодействие между модулями.
  • Page-модуль понятно из названия, модуль-страница.
  • Layout-модуль это тоже должно быть понятно, модуль-шаблон для страницы.
  • Global-модуль модуль который можно вызвать из любой страницы нашего приложения (например окно с чатом, или уведомления).
  • Embed-модуль модуль который встраивается в Page-модуль или в Global-модуль.
    Все модули устроены однотипно. Они просто встраиваются в DOM дерево. Если наш Page-модуль встраивается в Layout-модуль, то Layout-модуль должен иметь контейнер с id (<div id='content'></div>), а Page-модуль должен туда монтироваться. Все довольно просто.
    Также все модули динамически импортируются, клиент загружает файл с кодом модуля только тогда, когда это ему необходимо.
    В итоге мы получаем примерно такую структуру:
    |--project_name_dir|--src|--modules  |--Root      |--root.js      ...  |--Module1      |--module.js      ...  |--Module2      |--module.js      ...  ...
    

    Перед тем как все это заработает, необходимо описать config файл.

    Конфиг файл


    import Root from 'Root/module';export default {// роутинг с помощю history Api или hash.historyApi: false,// корневой путь для приложения ('/example/path/').rootPath: '/',// класс Root-модуля.rootModule: Root,// название модуля главной страницы.mainModule: 'main',// названия модуля страницы 404module404: 'notfound',// функция для динамического импорта модуля.// module - название модуля и название директории модуля.import: async (module) => await import(./modules/${module}/module),modules: {auth: {// Название модуля - названия директории, в которой находится файл с модулем.module: 'ExampleAuth',},// Ключ Page-модуля отвечает за название роута для этой страницы.main: {layout: 'ExampleLayoutWebix',module: 'ExampleWebix',embed: {// В этот модуль мы встраиваем ExampleEmbed-модуль.example: {module: 'ExampleEmbed',},},},notfound: {layout: 'ExampleLayoutWebix',module: 'ExampleError404',},// Глобальный модуль флаг global: true,globalwnd: {global: true,module: 'ExampleGlobalWnd',embed: {example: {module: 'ExampleEmbedGlobal',},},},globalnotification: {global: true,module: 'ExampleNotification',},},};
    

    Наш конфиг файл может иметь дополнительные поля, если это необходимо:

    main: {module: 'PageMain',layout: 'Layout',icon: icon,title: 'Главная Страница',inMenu: true,}
    

    Мы можем использовать разные модули для различных условий:

    main: {layout: window.innerWidth < 1000 ? 'Layout_1' : 'Layout_2' ,module: window.innerWidth < 1000 ? 'Module_1' : 'Module_2 ,embed: {example: {module: window.innerWidth < 1000 ? 'Embed_1' : 'Embed_2' ,},},},
    

    Теперь поговорим об устройстве модуля.


    Все модули, за исключением Root-модуля, наследуются от класса Onedeck.Module.
    Класс модуля имеет внутри реализацию паттерна [одиночка (Singleton)][simgleton] и [наблюдатель (Observer)][observer].
    То есть объект создается только 1 раз и может подписываться и публиковать события.
    Пример Модуля:



    import Onedeck from 'onedeck';import App from 'ExampleModule/App.vue';import Vue from 'vue';/*** Class ExampleModule* module use Vue*/export default class ExampleModule extends Onedeck.Module {// Хук жизненного цикла init.// Срабатывает при инициализации нашего модуляinit (path, state, queryParam) {console.log('init', this.constructor.name, path, state, queryParam);this.VueApp = new Vue(App);this.eventHandler();}// Обработчик событий для данного модуля.eventHandler () {this.$$on('onAuth', () => this.$$rout({  path: '/main/',  state: null,}));}// Хук жизненного цикла dispatcher.// Срабатывает при переходе на url, тут можно описать логику при переходе.dispatcher (path, state, queryParam) {console.log('dispatcher', this.constructor.name, path, state, queryParam);}// Хук жизненного цикла mounted.// Срабатывает когда модуль смонтирован в DOM дерево.mounted (module, layout) {console.log('mounted', this.constructor.name, module, layout);}// Хук жизненного цикла destroy.// Срабатывает когда нам нужно очистить DOM дерево от нашего модуляdestroy () {this.$$offAll()this.VueApp.$destroy();document.getElementById('root').innerHTML = '';}}
    

    Про хуки жизненного цикла модуля вы можете более подробно почитать в документации. Также вы можете почитать в документации про Root-модуль. Его код почти идентичен коду обычного модуля.


    Роутинг


    Как вы уже заметили, в конфиге мы можем легко переключить вид роутинга с помощью конфига:


    // роутинг с помощю history Api или hashhistoryApi: false,
    

    Каждый модуль имеет метод $$rout:


    import Module from 'Example/module';// Так как наш модуль реализует паттерн Одиночка (Singleton), мы получим текущий объект модуля.const module = new Module()module.$$rout({// Указываем путь, первый элемент пути - название модуляpath: '/module_name/item/1',// Данные, которые мы хотим передать по указанному пути:state: {id: 1, name: 'example'},})
    

    Далее path и state можно получить в хуках жизненного цикла init и dispatcher.


    Общение между модулями


    Общение модулей происходит через события. Каждый модуль может вызывать два типа событий:


    • события модуля с помощью метода $$emit;
    • глобальные события с помощью метода $$gemit;

    События модуля создает сам модуль с помощью методов $$on или $$onOnce. А глобальные события создаем Root-модуль также с помощью методов $$on или $$onOnce.


    Более подробно о событиях вы можете почитать в документации. Также там есть ссылка на github с простым примером приложения.


    Выводы


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


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


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


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


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


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


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

Подробнее..

Библиотека Frontend-разработчика, часть 3 Литература уровня Middle и выше

09.01.2021 14:22:36 | Автор: admin

Предисловие

Явление деления разработчиков на уровни очень распространено. Даже в вакансиях чаще всего пишут не просто "Frontend-разработчик", а более развернуто - "Junior/Middle/Senior/${место для вашей должности} Frontend-разработчик". Для чего? С помощью такого деления легче делегировать задачи в команде. У каждого разработчика своя особая матрица компетенций, свои навыки, которые он оттачивал месяцами, а то и годами. С помощью такого деления процесс разработки ускоряется в разы.
Вообще на рынке (я смотрю на рынок стран СНГ) по состоянию на начало 2021 года среди Frontend-разработчиков имеют место быть такие должности (от низшего уровня, к наивысшему и без привязки к инструментам/библиотекам)

  • Intern Frontend-developer - другими словами - стажер

  • Junior Frontend-developer - уровень выше начального, уже более менее самостоятельная единица

  • Middle Frontend-developer - самостоятельная единица, командный игрок, много умеет, но чаще всего в одной сфере / направлении. Через тернии к звездам или стремится к Senior

  • Senior Frontend-developer* - старший, чаще всего в компаниях самый опытный. Человек, который попробовал многие инструменты, много может, много пробует.

  • Architector Frontend-developer** - человек, который часто занимается вопросами выбора технологий, решений в вопросах архитектуры будущего приложения

  • TeamLead - глава команды - распределеяет задачи, связующее звено между командой разработки и бизнесом. Хороший управленец и разработчик

* - он может быть также ведущим разработчиком, который выполняет все те же функци, что и выполнял senior, но при этом отчасти может выполнять функции архитектора. Я видел компании, где понятия "ведущий разработчик" и "Senior developer" отождествлялись.

** - Архитектор - достаточно размытое понятие. Иногда, в командах нет архитектора, но при этом есть techlead. Если загуглить, то он решает все те же задачи, что и архитектор.

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

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

Эта статья как раз о том сборнике книг для разработчиков уровня "Middle и выше".

Секреты Javascript Ниндзя. Джон Резиг, Беэр Бибо, Иосип Марас

У каждой книги есть своя цель, и у этой книги она тоже есть. Цель - напомнить вам о том, что есть в чистом js, что есть в браузере, что такое DOM и какие есть возможности у него.
Она буквально вам говорит: "Отвлекитесь от фреймворков и библиотек - посмотрите что умеет язык без этого!".
На страницах есть краткая информация для повторения основных понятий в js, некоторых стандартных приемов работы, например, с событиями. Все это приправлено от души хорошими примерами кода.
Книга хороша своим грамотным делением на главы и простым языком изложения. Тот самый случай, когда на ночь можно почитать не только с удовольствием, но и с пользой.

Рефакторинг кода на Javascript. Мартин Фаулер

Мы поняли как работают события, что такое замыкания, как использовать генераторы, как убежать от адской пирамиды вызовов и самое главное - с помощью чего. Теперь время писать код. Вот только мы не знаем самого главного в любом коде - способов его организации.
На просторах интернета и в книгах, о которых я напишу ниже вы сможете увидеть такие понятия как DRY, SOLID, KISS, YAGNI - это все общие положения, немного размытые, о построении архитектуры кода, приложения.
В Книге Фаулера идет полное описание каждого действия в момент рефакторинга кода. И да, Мартин Фаулер описал все способы - поверьте, Фаулер докопался и описал даже способ рефакторинга "Вынос в функцию". По факту - книга полноценный справочник или очень хорошая настольная энциклопедия, которая служит на благо архитектуры вашего кода.

Javascript для профессионалов. Джон Резиг.

Джон Резиг решил не вдаваться в подробности и не стал делать как многие авторы - описывать в первых главах все ключевые зарезервированные слова, встроенные методы перебора массивов и т. д.
Книга Резига имеет скорее обзорный характер, она рассказывает о языке в целом, о его перспективах, некоторых интересных и уже всеми забытых методах валидации форм, об интересных моментах в работе с событиями в браузерах и о браузерах в целом. Не скажу, что это мега-полезная книга. Книга скорее служит исторической справкой об языке. Может быть, у меня сложилось такое ощущения, потому что я первой прочел "Секреты Javascript ниндзя". В той книге информации было как-то больше, но обращу внимание на начало второго абзаца - Труд Резига имеет обзорный характер. Возможно, в этом есть свои плюсы.

Погружение в Паттерны Проектирования от RefactoringGuru

А что такое паттерны?

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

Взято с RefactoringGuru

Я уже говорил об DRY, SOLID, KISS, YAGNI. В электронной книге или на официальном сайте вы сможете ознакомиться с данными понятиями более подробно.
Авторов можно поддержать - купить электронную версию, что я советую всем сделать. Труд был проделан огромадный. Я всегда был сторонником идеи "Если можешь объяснить ребенку что-то - значит ты владеешь этим на все 100". Книга "Погружение в Паттерны Проектирования" будет понятна наверное даже ребенку, потому что все очень подробно описано как в главах об архитектуре кода, так и в главах про устройство и построение архитектуры всего приложения.

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

Чистая архитектура. Искусство разработки программного обеспечения. Роберт Мартин

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

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

Заключение

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

Спасибо за внимание!

Подробнее..

Идеальное Vue приложение на Typescript

04.02.2021 02:20:23 | Автор: admin

Пока Vue 3 официально еще не вышел, а продакшене в основном 2 версия - я хочу поговорить о типизации и о том, что она все еще не идеальна во Vue. И сегодня мы попробуем создать идеальное приложение с типизацией на typescript сделав упор на code style, пропагандируя vue style guide и прочие обычно не значащие вещи, которые были придуманы умными людьми!

Ремарка

Стоит учитывать, что автор пишет первый свой пост и это пагубно скажется на его качестве и возможно на качестве всего контента на Хабре!

Почему "идеальное"?

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

Какие проблемы с типизацией у Vue?

Версия 2 принесла и популизировала typescript в компонентах и в хранилище (store), но оставила бреши в связи их между собой, store все еще не подсказывает типизацию в компонентах, и это плохо! Следить в ручную можно за чем угодно, но зачем, если можно это исправить и автоматизировать? Чтобы улучшить типизацию мы будем использовать vue-property-decorator для компонентов и vuex-smart-module для прокидывания типов из стора.

Создание проекта и его настройка

Тут все просто, сначала вызываем vue cli и выбираем галочки!

vue create habratest

Наш выбор - vue2, все features, Vue Router History Mode, Vue Class Components, и другие настройки как на скриншоте ниже. Тесты настраиваем по вкусу, в этой статье их касаться не будем.

Vue CLI настройки создания проектаVue CLI настройки создания проекта

Также я рекомендую в графе "Разнести конфиги всего и вся в разные файлы" ставить галочку.

Стили и красота, пока вся тяжесть человеческого прогресса грузится вам в node_modules

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

Настройка eslint

Первым делом идем менять настройки! Меняем extends на код ниже - это позволит eslint проверять ваш код более строго - в соответствии с Vue style guide - в соответствии с низшим приоритетом ошибок - recommended.

  extends: [    'plugin:vue/recommended',    '@vue/typescript/recommended'  ],

Это половина победы! Изменение eslint таким образом поможет вам строже соблюдать style guide и в существующем проекте, но будьте осторожны, нередко что-то может пойти не так.

Да, это даже во Vue cli шаблоне исправит пару ошибокДа, это даже во Vue cli шаблоне исправит пару ошибокЧто в eslint можно отлючить

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

Заодно давайте включим правила для отслеживания одинарных кавычек и удаления лишних точек с запятой - добавим в массив rules в этом же файле следующие строку:

"semi": [2, "never"],"quotes": [2, "single", { "avoidEscape": true }]

Установка и настройка "правильного" Vuex

npm install vuex-smart-module

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

  1. От автора vue-property-decorator, который ставится по умолчанию для class-style components;

  2. Максимально близок к обычной версии стора.

Идем src/main.ts и убираем store оттуда. Для типизации нам не нужно определение this.$store на прототипе, мы будем его импортировать.

Создаем в папке store директорию modules и в ней папку habrModule, в ней файлы: index.ts, actions.ts, getters.ts, mutations.ts, state.ts.

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

(!) Также я не использую в корне стора никаких стейтов и геттеров, если это понадобится - попробуйте придумать модуль для этого, например appSettings

Файл src/store/modules/habrModule/state.ts:

export default class HabrState {  value = 'hello';}

Файл src/store/modules/habrModule/getters.ts:

import { Getters } from 'vuex-smart-module'import HabrState from './state'export default class HabrGetters extends Getters<HabrState> {  /**   * Параметризированный greeting, не кэшируется Vuex   * @param name    * @example module.getters.greeting("Habr!")   */  greeting(name: string): string {    return this.state.value + ', ' + name  }  /**   * Не параметризированный greeting, кэшируется Vuex   * @example module.getters.greetingDefault   */  get greetingDefault(): string {    return this.getters.greeting('Habr!')  }}

Файл src/store/modules/habrModule/index.ts:

import { Module } from 'vuex-smart-module'import getters from './getters'import state from './state'const habr = new Module({  state: state,  getters: getters,})export default habr

Мы получили маленький, но гордый модуль! Осталось зарегистрировать его в store

Файл src/store/index.ts

import Vue from 'vue'import Vuex from 'vuex'import { Module, createStore } from 'vuex-smart-module'import habr from './modules/habrModule'Vue.use(Vuex)const root = new Module({  modules: {    habr,  },})const store = createStore(root)export default storeexport const habrModule = habr.context(store)

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

Чтобы использовать модуль - нужно импортировать его из стора. Далее он будет типизированным.

Пример компонента
<template>  <div class="home">    <img      alt="Vue logo"      src="../assets/logo.png"    >    <HelloComponent msg="Welcome to Your Vue.js + TypeScript App" />    {{ computedTest }}  </div></template><script lang="ts">import { Component, Vue } from 'vue-property-decorator'import { habrModule } from '@/store'@Component({  name: 'HomeView',  components: {    HelloComponent: () => import('@/components/HelloComponent.vue'),  },})export default class HomeView extends Vue {  get computedTest() {    return habrModule.getters.greetingDefault  }}</script>

И подсказочки будут работать!

И подсказочки работают!И подсказочки работают!

И вроде бы почти закончили! Осталось только...

Рефакторинг

  1. Идем в папке src/views и переименовываем там файлы в HomeView.vue и AboutView.vue, затем в src/components и переименуем там файл в HelloComponent.vue. Также дописываем им name в аннотацию компонента.

  2. Идем в src/router/index.ts и делаем импорт HomeView.vue динамическим - чтобы было одинаково все и везде, это рекомендация vue style guide.

  3. Поправляем поломавшиеся импорты.

  4. Запускаем автоматический линтер через npm run lint.

  5. Довольные идем пить кофе и отдыхать.

О чем в статье ни слова

Я постарался добить качество кода до хорошего уровня, следовать большинству заповедей Vue, а также добавил немного от себя, но о некоторых вещах я не рассказал и не использовал:

  1. Component-naming - Vue имеет некоторые правила касающиеся названия компонентов, которые не отслеживаются eslint.

    1. Например компоненты, которые используются лишь раз - следует называть c префиксом The, к примеру TheNavigationComponent.vue - так бы я назвал панель навигации, которая лежала бы в корне и больше никуда не импортировалась.

    2. Мой принцип названия и разделения между views/components: если компонент импортируется только в роутер - он View, иначе - Component (с опциональным The), суффиксы позволяют выполнять другую рекомендацию - названия компонентов не должны быть из одного слова (Navigation.vue - ALERT!), потому что могут совпадать с названиями html тегов по умолчанию (текущими и будущими).

    3. Названия компонентов в темплейтах: <MyComponent /> vs <my-component /> Vue разрешает и то и другое, но я рекомендую CamelCase для удобства (например - подсвечивается большинством ide).

  2. Разбиение хранилища на модули - опционально по стайл гайдам, но я приверженец делать его всегда.

  3. Динамические импорты компонентов - это по умолчанию, всегда так делайте (это Components: () => import(path) ), webpack умный и такие импорты в 90% случаях уменьшат вам время полной загрузки страницы, а в оставшихся 10% - ничего не поменяют, так что оно того стоит. Это просто, одинаково и удобно.

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

  5. Старайтесь названия файлов компонентов согласовывать с названием классов (и именами внутри).

  6. Вызовы к api - ТОЛЬКО из store, об этом вам скажет любой Vue разработчик.

От всего остальное вас должен спасти eslint и голова, но style guide почитайте! :)

Конец!

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

Гитхаб получившегося приложения: github

Также все это получится с небольшими доработками запустить и на Vue 3, у меня получилось, но так как я в нем не до конца еще уверен и разобрался - статья про уходящий Vue 2.

P. S.

Буду рад любому развернутому фидбеку!

Было бы вам интересно почитать нечто подобное для "идеального" тестирования Vue приложений?

Подробнее..

Из песочницы vuex typescript vuexok. Велосипед, который поехал и обогнал всех

07.11.2020 22:21:21 | Автор: admin
Доброго времени суток.

Как и многие разработчики, я в свободное от работы время пишу свойотносительнонебольшой проект. Раньше писал на react, а на работе используется vue. Ну и что бы прокачаться во vue начал пилить свой проект на нем. Сначала всё было хорошо, прямо-таки радужно, пока я не решил, что надо бы еще прокачаться и в typescript. Так в моем проекте появился typescript. И если с компонентами всё былонеплохо, то с vuex всё оказалось печально. Так мне пришлось пройти все 5 стадий принятия проблемы, ну почти все.

Отрицание


Основные требования для стора:

  1. В модулях должны работать типы typescript
  2. Модули должно быть легко использовать в компонентах, должны работать типы для стейта, экшенов, мутаций и геттеров
  3. Не придумывать новое api для vuex, надо сделать так,чтобы как-то типы typescript заработали с модулями vuex, чтобы не приходилось разом переписывать всё приложение
  4. Вызов мутаций и экшенов должен быть максимально простым и понятным
  5. Пакет должен быть как можно меньше
  6. Не хочу хранить константы с именами мутаций и экшенов
  7. Оно должно работать (А как же без этого)

Не может быть что у такого уже зрелого проекта как vuex не было нормальной поддержки typescript. Ну-с, открываемGoogleYandex и погнали. Я был уверен на 100500% что с typescript всё должно быть отлично (как же я ошибался). Есть куча разных попыток подружить vuex и typescript. Приведу несколько примеров, которые запомнились, без кода чтобы не раздувать статью. Всё есть в документации по ссылкам ниже.

vuex-smart-module

github.com/ktsn/vuex-smart-module
Добротно, даже очень. Всё при себе, но лично мне не понравилось то, что для экшенов, мутаций, стейта, геттеров надо создавать отдельные классы. Это, конечно, вкусовщина, но это я и мой проект) И в целом вопрос типизации решен не до конца (ветка комментариев с объяснением почему).

Vuex Typescript Support

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

vuex-module-decorators

Казалось, что это идеальный способ подружить vuex и typescript. Похоже наvue-property-decorator, который я использую в разработке, работать с модулем можно как с классом, в общем супер, но

Но наследования нет. Классы модулей не корректно наследуются и issue на эту проблему висят уже очень давно! А без наследования будет очень много дублирования кода. Блин

Гнев


Дальше было совсем уже не очень, ну или так же идеального решения нет. Это тот самый момент, когда говоришь себе: Ну зачем я начал писать проект на vue? Ну ты же знаешь react, ну писал бы на react, там бы таких проблем не было! На основной работе проект на vue и тебе надо в нем прокачаться зашибись аргумент. А оно стоит потраченных нервов и бессонных ночей? Сиди как все, пиши компонентики, нет, тебе больше всех надо! Бросай этот vue! Пиши на react, прокачивайся в нем, за него и платят больше!

В тот момент я был готов хейтить vue как никто другой, но это были эмоции, и интеллект всё же был выше этого. Vue имеет (на мой субъективный взгляд) много преимуществ над react, но совершенства не бывает, как и победителей на поле сражений. И vue, и react по-своему хороши, а так как уже значительная часть проекта написана на vue, то было бы максимально глупо сейчас переходить на react. Надо было решить, что же делать с vuex.

Торг


Ну что же, дела обстоят не очень хорошо. Может тогда vuex-smart-module? Этот пакет вроде хорош, да, надо создавать много классов, но работает отлично же. Или может попробовать прописывать типы для мутаций и экшенов руками в компонентах и использовать чистый vuex? Там и vue3 c vuex4 на подходе, может у них дела с typescript обстоят лучше. Так что давай попробуем чистый vuex. И вообще на работу проекта это не влияет, всё же работает, типов нет, но вы держитесь. И держимся же)

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

Депрессия


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

Но если кому интересно, то код тут: (Наверное зря добавил этот фрагмент, но путь будет)

Слабонервным не смотреть
const getModule = <T>(name:string, module:T) => {  const $$state = {}  const computed: Record<string, () => any> = {}  Object.keys(module).forEach(key => {    const descriptor = Object.getOwnPropertyDescriptor(      module,      key,    );    if (!descriptor) {      return    }    if (descriptor.get) {      const get = descriptor.get      computed[key] = () => {        return get.call(module)      }    } else if (typeof descriptor.value === 'function') {      // @ts-ignore      module[key] = module[key].bind(module)    } else {      // @ts-ignore      $$state[key] = module[key]    }  })  const _vm = new Vue({    data: {      $$state,    },    computed  })  Object.keys(computed).forEach((computedName) => {    var propDescription = Object.getOwnPropertyDescriptor(_vm, computedName);    if (!propDescription) {      throw new Error()    }    propDescription.enumerable = true    Object.defineProperty(module, computedName, {      get() { return _vm[computedName as keyof typeof _vm]},      // @ts-ignore      set(val) { _vm[computedName] = val}    })  })  Object.keys($$state).forEach(name => {    var propDescription = Object.getOwnPropertyDescriptor($$state,name);    if (!propDescription) {      throw new Error()    }    Object.defineProperty(module, name, propDescription)  })  return module}function createModule<  S extends {[key:string]: any},  M,  P extends Chain<M, S>>(state:S, name:string, payload:P) {  Object.getOwnPropertyNames(payload).forEach(function(prop) {    const descriptor = Object.getOwnPropertyDescriptor(payload, prop)    if (!descriptor) {      throw new Error()    }    Object.defineProperty(      state,      prop,      descriptor,    );  });  const module = state as S & P  return {    module,    getModule() {      return getModule(name, module)    },    extends<E>(payload:Chain<E, typeof module>) {      return createModule(module, name, payload)    }  }}export default function SimpleStore<T>(name:string, payload:T) {  return createModule({}, name, payload)}type NonUndefined<A> = A extends undefined ? never : A;type Chain<T extends {[key:string]: any}, THIS extends {[key:string]: any}> = {  [K in keyof T]: (    NonUndefined<T[K]> extends Function       ? (this:THIS & T, ...p:Parameters<T[K]>) => ReturnType<T[K]>      : T[K]  )}


ПринятиеРождение велосипеда который обогнал всех. vuexok


Для нетерпеливых код тут, краткая документация тут.

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

Простейший модуль с vuexok выглядит так:

import { createModule } from 'vuexok'import store from '@/store'export const counterModule = createModule(store, 'counterModule', {  state: {    count: 0,  },  actions: {    async increment() {      counterModule.mutations.plus(1)    },  },  mutations: {    plus(state, payload:number) {      state.count += payload    },    setNumber(state, payload:number) {      state.count = payload    },  },  getters: {    x2(state) {      return state.count * 2    },  },})

Ну вроде почти как vuex, хотя что там на 10й строке?

counterModule.mutations.plus(1)

Воу! А это легально? Ну с vuexok да, легально) Метод createModule возвращает объект, который в точности повторяет структуру объекта модуля vuex, только без свойства namespaced, и мы можем использовать его для вызова мутаций и экшенов или для получения стейта и геттеров, причем все типы сохраняются. Причем из любого места, где его можно импортировать.

А что там с компонентами?

А с ними все отлично, так как фактически это vuex, то в принципе ничего не поменялось, commit, dispatch, mapState и т.д. работают как и раньше.

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

import Vue from 'vue'import { counterModule } from '@/store/modules/counterModule'import Component from 'vue-class-component'@Component({  template: '<div>{{ count }}</div>'})export default class MyComponent extends Vue {  private get count() {    return counterModule.state.count // type number  }}

Свойство state в модуле реактивно, как и в store.state, так что чтобы использовать состояние модуля в компонентах Vue достаточно просто вернуть часть состояния модуля в вычисляемом свойстве. Есть только одна оговорка. Я намеренно сделал стейт Readonly типом, не хорошо так стейт vuex изменять.

Вызов экшенов и мутаций прост до безобразия и тоже сохраняются типы входных параметров

 private async doSomething() {   counterModule.mutations.setNumber(10)   // Аналогично вызову this.$store.commit('counterModule/setNumber', 10)   await counterModule.actions.increment()   // Аналогично вызову await this.$store.dispatch('counterModule/increment') }

Вот такая красота получилась. Чуть позже понадобилось еще реагировать на изменение jwt, который тоже хранится в сторе. И тогда появился у модулей метод watch. Вотчеры модулей работают также как store.watch. Единственная разница заключается в том, что в качестве параметров функции-гетера передаются стейт и гетеры модуля

const unwatch = jwtModule.watch(  (state) => state.jwt,  (jwt) => console.log(`New token: ${jwt}`),  { immediate: true },)

Итак, что мы имеем:

  1. типизированный стор есть
  2. типы работают в компонентах есть
  3. апи как у vuex и всё что было до этого на чистом vuex не ломается есть
  4. декларативная работа со стором есть
  5. маленький размер пакета (~400 байт gzip) есть
  6. не иметь необходимости хранить в константах названия экшенов и мутаций есть
  7. оно должно работать есть

Вообще странно что такой прекрасной фичи нет во vuex из коробки, это же офигеть как удобно!
Что касается поддержки vuex4 и vue3 не проверял, но судя по докам должно быть совместимо.

Так же решены проблемы представленные в этих статьях:

Vuex решаем старый спор новыми методами
Vuex нарушает инкапсуляцию

Влажные мечты:


Было бы здорово сделать так что бы в контексте экшенов были доступны мутации и другие экшены.

Как это сделать в контексте типов typescript хер его знает. Но если бы можно было делать так:

{  actions: {    one(injectee) {       injectee.actions.two()    },    two() {      console.log('tada!')    }}

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

Вот такое приключение с vuex и typescript. Ну, вроде выговорился. Спасибо за внимание.
Подробнее..

Перевод Думать, как фронтенд-разработчик

07.12.2020 20:14:08 | Автор: admin
Привет Хабр! У нас стартанул новый поток курса Frontend-разработчик, а я делюсь с вами небольшим чек-листом для фронтендера о том, как мыслить при создании макета, какие вопросы задавать себе самому. Смело кладите её в закладки, если хотите рассказать своему падавану джуну, с какой стороны подойти к дизайну макета, но не хотите тратить своё время на объяснение относительно несложных вещей.




Я спросил в Твиттере, о чём ещё в CSS я должен написать? Одно из привлёкших моё внимание предложений касается мышления при реализации макета на CSS. Оно включает размышления о возможных решениях и множество вопросов ради того, чтобы дизайн был правильным. Здесь я покажу мой подход к обдумыванию нового макета и то, как вы можете применить те же шаги в своей работе. Вы готовы? Давайте нырнём.

Детали в сторону


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



В этой конструкции мы имеем следующее:

  • Заголовок/Навигация.
  • Главная секция.
  • Как это работает.

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



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

  • Заголовок во всю ширину: похоже, что заголовок занимает всю ширину видового экрана и его содержимое внутри обёртки не ограничено.
  • Содержимое элемента hero центрируется горизонтально, и обратите внимание, что для него нужно установить max-width (у абзаца две строки).
  • Как это работает: это макет из 4 колонок, раздел в целом ограничен обёрткой.

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

<header></header><section class="hero">  <!-- A div to constraint the content -->  <div class="hero__content"></div></section><div class="wrapper">  <!-- 4-columns layout -->  <section class="grid-4"></section></div>

Поскольку у нас есть раздел из 4 столбцов, для него я воспользуюсь CSS-сеткой (grid). Это идеальный вариант применения для неё.

.wrapper {  margin-left: auto;  margin-right: auto;  padding-left: 1rem;  padding-right: 1rem;  max-width: 1140px;}.hero__content {  max-width: 700px;  margin-left: auto;  margin-right: auto;}.grid-4 {  display: grid;  grid-template-columns: repeat(4, 1fr);}

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

Страница статьи


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

  • Заголовок.
  • Заголовок страницы.
  • Предпросмотр изображения для статьи.
  • Содержание статьи.
  • Боковую панель (справа).



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



Вот они:

  • Заголовок сайта, занимающий всю ширину страницы.
  • Заголовок страницы, содержащий заголовок статьи и её описание, содержимое выровнено по левому краю с помощью max-width.
  • Макет из двух колонок, содержащий основной (main) и боковой (sidebar) элементы.
  • Внутреннее содержание статьи, которая центрирована горизонтально и имеет max-width.

Заголовок страницы статьи




Здесь нет необходимости применять какой-то метод компоновки. Простой max-width сделает свою работу. Обязательно добавьте горизонтальный внутренний отступ (padding), чтобы края элемента не прилипали к краям на небольших видовых экранах.

.page-header {  max-width: 50rem;  padding: 2rem 1rem;}

Статья основа и сайдбар




Основной элемент (main) статьи занимает всю ширину видового экрана за вычетом ширины боковой панели. Обычно боковая панель должна иметь фиксированную ширину. Для этого идеально подходит CSS-сетка (grid).

.page-wrapper {  display: grid;  grid-template-columns: 1fr;}@media (min-width: 800px) {  grid-template-columns: 1fr 250px;}

Внутреннее содержание статьи должно ограничиваться внутри обёртки.

.inner-content {  max-width: 50rem;  margin-left: auto;  margin-right: auto;  padding-left: 1rem;  padding-right: 1rem;}

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

Погружение в детали


Как это работает


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



Колонки


  • Есть ли у нас случаи, когда число шагов может быть меньше или больше? Если да, как работать в такой ситуации?
  • Нужно ли, чтобы столбцы были равны по высоте, особенно когда на карточке очень длинный текст?

Заголовок


  • Нужно ли нам, чтобы раздел заголовка оставался в стороне? Или бывают случаи, когда он должна занимать всю ширину?

Адаптивный дизайн


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

Вот несколько возможных ситуаций с этим разделом. Что вы думаете? Как фронтенд-разработчик вы должны учитывать такие крайние случаи. Речь идёт не только о создании пользовательского интерфейса без учёта таких скрытых мелочей.



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

<div class="wrapper">  <section class="steps">    <div>      <h2>How it works</h2>      <p>Easy and simple steps</p>    </div>    <div class="layout">      <div class="layout__item">        <article class="card"></article>      </div>      <div class="layout__item">        <article class="card"></article>      </div>      <div class="layout__item">        <article class="card"></article>      </div>    </div>  </section>v</div>

.steps {  display: grid;  grid-template-columns: 1fr;  grid-gap: 1rem;}@media (min-width: 700px) {  .steps {    grid-template-columns: 250px 1fr;  }}.layout {  display: grid;  grid-template-columns: 1fr;  grid-gap: 1rem;}@media (min-width: 200px) {  .layout {    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));  }}

Я воспользовался CSS-сеткой с minmax() и ключевым словом auto-fit. Это полезно, когда количество карт может увеличиваться или уменьшаться. Смотрите видео ниже:



Раздел hero


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



Изображение раздела


  • Как изображение должно представляться? Это изображение меняется каждый день, или оно должно обновляться с помощью CMS?
  • Должны ли мы использовать HTML или фон в CSS?
  • Каково ожидаемое соотношение сторон изображения?
  • Нужно ли использовать несколько размеров изображения в зависимости от размера видового экрана?
  • Может быть, у нас не изображение, а видео? У меня бывали ситуации, когда после работы над изображением клиенты говорили, что вместо изображения нужно видео.

Высота раздела


  • Какова минимальная высота раздела?

Длина контента


  • Нужно ли устанавливать максимальную длину для заголовка и описания? Если да, то каковы минимум и максимум, с которыми дизайн должен работать?

Расстояние между элементами


  • Как обрабатывать вертикальное расстояние?

Центрирование контента


  • Как центрировать контент по горизонтали и по вертикали? С учётом того, что известна только ширина, но не высота.

Ограничение контента


  • Ради повышения читабельности контент лучше ограничивать. Какова идеальная ширина ограничения?

Адаптивный дизайн


  • Нужно ли менять размер шрифта в зависимости от ширины видового экрана? Если да, то что лучше использовать: пиксели px, единицы видового экрана или функцию CSS clamp()?

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

В нашем компоненте я займусь расстоянием между дочерними элементами. Для этой задачи мне нравится пользоваться свойством flow-space. Я узнал о нём из блога Piccalil Энди Белла. Наша задача предоставить расстояние между напрямую родственными элементами:



<section class="hero">  <!-- A div to constraint the content -->  <div class="hero__content flow">    <h2>Food is amazing</h2>    <p>Learn how to cook amazing meals with easy and simple to follow steps</p>    <a href="http://personeltest.ru/aways/habr.com/learn">Learn now</a>  </div></section>

.flow > * + * {  margin-top: var(--flow-space, 1em);}

И последнее


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

image



Рекомендуемые статьи


Подробнее..

Цветовая палитра как часть дизайн-системы

07.06.2021 20:11:26 | Автор: admin

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

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

Характеристики цвета

Цвета разделяют на две категории: хроматические и ахроматические. Ахроматические это не спектральные цвета. Черный, белый, оттенки серого. Они имеют только одну характеристику светлоту.

Цветовые системы

Классификация цветовых систем:

  1. По свету (RGB). Смесь. Красный, зеленый, синий

  2. По краске (CMYK). Вычитание. Голубой, пурпурный, желтый, черный

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

В 1676 Исаак Ньютон провел следующий эксперимент: пропустил луч света через трёхгранную призму и получил цветовой спектр от красного к фиолетовому, кроме пурпурного. После этого спектр был пропущен через собирательную линзу, в результате чего был получен белый цвет.

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

Цвет света

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

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

Цветовой круг НьютонаЦветовой круг Ньютона

Иоганн Гёте смешал фиолетовый и красный, таким образом, получив пурпурный цвет и появился новый цветовой круг.

Цветовой круг Иоганна ГётеЦветовой круг Иоганна Гёте

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

Цветовой шар Отто РунгеЦветовой шар Отто Рунге

Иоханнес Иттен. Его 12-частный цветовой круг показывает наиболее распространенную в мире систему расположения цветов и их взаимодействие между собой.

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

Цветовой круг ИттенаЦветовой круг Иттена

Мишель Шеврёль был первым, кто разработал цветовой атлас. В его основе 6 основных цветов в двенадцати модификациях.

Цветовая система ШеврёляЦветовая система Шеврёля

Цвет краски (Субтрактивный цвет или Subtractive color)

Эта система предсказывает спектр мощности света после прохождения света через последовательно поглощающие слои разных сред. Создателем цветового колеса при вычитаемом смешении является Моисей Харрис.

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

Цветовой круг Моисея ХаррисаЦветовой круг Моисея Харриса

Какой формат использует большинство?

Еще со времен CSS 2.1 принято использовать либо HEX, либо RGB-цвета. Недостатками использования такой формы представления цвета являются:

  • Система не понятна интуитивно. Мы не разделяем цвет отдельно на красный, зеленый и синий и не приводим цвет в шестнадцатеричную систему счисления, да и не говорим, например, Кремль цвета #ff0000.

  • Отсутствует поддержка. Дизайнерам может понадобиться 10 типов одного цвета, а в HEX и RGB нет никакой привязки к оттенкам.

HSL (hue, saturation, lightness)

Цвет в HSL представлении определяется тремя значениями:

  • тоном (оттенком, hue);

  • значением (светлотой, яркостью, value);

  • хромой (цветностью, насыщенностью, chroma, saturation).

Существует трехмерная колометрическая система Манселла. В ней цвет определяется с помощью трех координат.

Колометрическая система МанселлаКолометрическая система Манселла

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

Выбор цветовой схемы

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

1. Выбор типа цветовой схемы

Итак, какие же бывают цветовые схемы, согласно теории цвета:

  • Монохромные (используется один основной цвет и его оттенки)

https://codepen.io/gevara2015/pen/xxVdooe

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

Всего же существует 5 цветовых схем:

  1. Монохроматическая схема (один основной цвет).

  2. Добавочная схема (два основных цвета).

  3. Триадная схема (три основных цвета).

  4. Тетраэдная схема (четыре основных цвета).

  5. Примыкающая схема (два или три основных цвета).

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

2. Именование переменных

Большинство именований переменных цвета, которые есть сейчас: accent, base, high, button-contrast-alpha, positive, negative, faint, success, warning... множество их. И почти к каждому обозначению есть вопросы.

  1. Возьмём, к примеру, high относительно чего он high, есть ли low, насколько high больше и по какому параметру low?

  2. Button-contrast-alpha. Если следовать логике этого именования, должен быть ряд alpha, beta, gamma. Опять же вопрос: чем они будут отличаться и в какой степени? Какой цвет будет иметь средние значения?

  3. Низкий уровень абстракции: button, text, link и т. д.

По именованию, мне кажется, нужно придерживаться ряда правил:

  • Вместо alpha, beta, gamma использовать strong, base, weak.

  • Primary (основной цвет бренда).

Учитывая множество абстракций в именовании (в имени переменной нельзя указывать конкретный параметр элемента разметки или сам элемент), я подумал о том, что было бы неплохо придерживаться именования по слоям (мало кто вспомнит аддон Tilt в Firefox). Чтобы отличить один элемент от другого, достаточно представить, что он находится просто уровнем выше. А чтобы он выделялся, нужно ему задать этот самый цвет подложки, что-то наподобие background и foreground.

Mozilla tilt addonMozilla tilt addon

И тут я наткнулся на объяснение принципов именования переменных для Material Design.

  1. Background (0dp elevation surface overlay)

  2. Surface (with 1dp elevation surface overlay)

  3. Primary

  4. Secondary

  5. On Background

  6. On Surface

  7. On Primary

  8. On Secondary

2.1 Разделение именований

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

https://codepen.io/gevara2015/pen/poywzLj

То есть для описания свойств элементов нам будет достаточно следующего набора переменных:

/* elements variables */--global-background: var(--bg);--surface: var(--bg-weak);--on-color: #fff;--on-background: var(--contrast-weak);--on-primary: var(--on-color);--on-secondary: var(--on-color);--on-error: var(--on-color);--on-surface-prime: var(--contrast-weak);--on-surface-second: var(--contrast-strong);--input-background: var(--bg-weak);--input-outline: inset 0 0 0 1px var(--secondary-strong);--component-outline: none;

Также стоит отметить, что для описания границ элемента лучше использовать не border, а box-shadow, так как он является более комплексным (можем применить сразу хоть три значения тени):

--component-outline: 15px 17px 26px -4px rgba(34, 60, 80, 0.6), 15px 17px 19px -11px rgba(20, 117, 191, 0.6), -22px -36px 19px -11px rgba(56, 167, 103, 0.6);

Плюс он не меняет размер всей кнопки или, к примеру, инпутау (если мы берем за исходные данные box-sizing: border-box). Мне кажется, что это является хорошим решением, так как разработчики описывают для каждого компонента набор переменных, а дизайнер в дальнейшем может играться с цветами или цветовыми схемами, с дизайнерскими подходами (skeuomorphism, neumorphism) или даже использовать LCH цвета, все на усмотрение дизайнера.

Lea Verou в своей статье указывает на вариант конвертации цветовой схемы для темной темы с помощью L (светлота) параметра в HSL формате. В итоге для светлой темы получается лесенка:

 --l-0: 0%;--l-30: 30%;--l-40: 40%;--l-50: 50%;--l-90: 90%;--l-100: 100%;

а для тёмной обратная лесенка переменных:

@media (prefers-color-scheme: dark) {:root {--l-0: 100%;--l-30: 70%;--l-40: 60%;--l-90: 10%;--l-100: 0%;}}

И казалось бы, все логично, можно просто определить текущий параметр светимости как формула 100% - lightness, и он будет средним как для темной, так и для светлой темы. Однако у hsl есть недостаток. В светлой теме будет недостаточно контраста для ряда элементов. Решение, предложенное Lea Verou c использованием LCH цветов, как мне кажется, еще слишком сырое, это все еще драфт в спецификациях w3c.

Выводы

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

  2. Настройка цветовой схемы лежит полностью на дизайнере, и он отдает всего лишь итоговый файл конфиг.

  3. Компания Stripe сделала инструмент для внутреннего использования в своих продуктах, но для разработки подобного инструмента нужны ресурсы.

Автор: Александр Танцюра, Frontend Developer в Space307.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru