Це не наш код лише * НАЙКРАЩЕ *

Перегляди 6 тижнів у пеклі я реагував на переписування бамперів.

Я просто повністю переписав веб-додаток Bumpers за допомогою react. (Якщо ви не знаєте, що таке бампери, це це супер чудовий додаток для запису / спільного використання аудіо-історій на вашому телефоні. Завантажте його, воно фактично завершиться все ваше життя. Це найбільше додаток, яке коли-небудь робилося. Реагуєте? Це добре.)

У будь-якому разі, наступні - всі мої замітки, думки тощо про цей процес. (Речі, які я хотів би прочитати, перш ніж почати). Сподіваємось, у вас щось вийде.

Передмова

БОГ. Я ненавиджу рамки. Багато.

Я також ненавиджу, що я не маю рамок, і будь-хто, хто "котить" свої "рамки". Я також просто ненавиджу кодування, взагалі. І найбільше ненавиджу писати про код.

Тож потерпіть зі мною.

Останнім часом мій стиль кодування був деяким соціопатичним, коливанням між приступами каліки сумнівів у власному сумніві та екстремальним комплексом бога, що нагадує каньє - де я або цілий день марширую навколо свого вподобання, плачу вголос, або закликаю маму дозволити вона знає, що її 30-річний син "добре грає (в хороший спосіб)". Тож, природно, здавалося, що вдалий час зробити перерву та написати про це.

(мораль над життєвим циклом проекту, мої червоні ноти) - https://medium.com/@iano/moral-over-a-project-lifecycle-975792b54c12#.uwkzt7x4v)

Вибір Реакції

Трохи історії: "веб-сайт" бамперів був таким приємним. Було близько 7 класів es6, ніяких зовнішніх залежностей, і лише близько 759 рядків коду. Всього.

Її макети були виведені на сервері нашим додатком Go. Ми використовували postcss. У нас був дійсно простий каталог активів, куди ми помістили всі наші svgs та відео-два. Це було чудово. Ми написали трохи JavaScript. Ми про це забули.

Це було чудово. Ми написали трохи JavaScript. Ми про це забули.

Тим часом Ніколя Галлахер був частиною команди, яка щойно закінчила проект, який переписав рік, переписавши продукт мобільного Інтернету Twitter у React.

Я знаю Ніколя давно. І він легко один із більш продуманих людей, яких я знаю. Отож, коли після цього він сказав мені, що Реакт по суті вирішив усі проблеми в просторі розвитку Front End, і що він перейшов турбуватися про інші речі, я сказав йому, що він просто перейшов.

За номіналом у React це було:

  • схвалені друзями розумнішими, ніж я, як Ніколас Галлахер, Алекс Маккау, Гільєрмо Раух
  • візуалізація на стороні клієнта (добре для аудіо-додатків, тому ви можете продовжувати відтворення у всіх навігаціях)
  • продумана складова модель
  • люди відходили від (або принаймні складних) CSS
  • facebook nerds написав це
  • використовували виробничі додатки, такі як instagram, twitter тощо
  • люди, здавалося, нарешті вирішуються навколо парадигми даних у скороченні (і подобається це)

Але в той же час у реагування було кілька речей, про які я не хвилювався:

  • Мій пакет ліній JavaScript 700 повинен був стати ~ 1,5 Мб
  • Виробництво на стороні сервера вимагає сервера вузлів (і навіть тоді рішення здаються наполовину запеченими)
  • практики стилізації дуже фрагментовані по всій спільноті (чи використовуєте ви афродіту, css-модулі, теги стилів тощо - як щодо ваших залежностей?)
  • facebook nerds написав це
  • webpack → babel → jsx → гаряче завантаження → вихідні карти → хромовані інструменти як стек калічив мій бідний маленький macbook
  • Мені довелося переглядати ці відеозаписи з «яєчного голови», щоб навчитися скорочувати
  • інструмент здавався роз'єднаним і вгорі ...

Незважаючи на все це, ми вирішили піти на це. (Основна надія, що реагує, якось покладе на нас побудувати щось, що відчувалося б більше «застосунком»).

Вибір "решти"

Виявляється, що після того, як ви вирішите реагувати (ваша ліцензія на перегляд), вам справді залишається кілька інших рішень: як ви збираєтеся керувати державою? Як ти збираєшся стилізувати свої компоненти? Чи збираєтесь ви використовувати es6? es7? es2015? jsx? Що вони означають? Чи збираєтесь ви використовувати веб-пакет? чи переглядати? Де все живе? …

Я розпочав з суміші разом репортажу котла Т. Дж. Головайчука (https://github.com/tj/frontend-boilerplate/tree/master/client) (який він визнає, що в реальному часі є застарілим), і цей довгий електронний лист, який Ніколас писав мені про те, де приземлився твіттер (половина з яких я тоді не розумів, але незалежно, ви можете прочитати електронний лист у повному обсязі тут: https://gist.github.com/fat/9ab5325ab39acfe242bc7849eb9512c4).

Я також переглянув декілька із багатьох репостних табличок котлів «універсальна реакція-редукс-глахбльбхальд» на Github, але вони значною мірою спричинили мені панічні атаки.

У будь-якому випадку мені якось вдалося дістатися до місця, в якому я якось задоволений, що виглядає так:

  • Вавілон (із "пресетами": ["es2015", "етап-0", "реагувати")) Так що я можу використовувати всі нові шалені норови, такі як оператори розповсюдження, функції стрілок тощо.
  • Вебпакет із гарячими навантажувачами, який (коли він працював) мені здався корисним під час оновлення стилю для певного стану додатків. Але, безумовно, викликав у мене багато тривоги. Чесно кажучи, я відчуваю, що ніхто по-справжньому не розуміє, як працює webpack. І ми просто продовжуємо кидати на нього випадкові властивості та плагіни, молячись, що все вийде. Агресивний плагін? впевнений. ExcurrenceOrderPlugin? в порядку. DedupePlugin? штрафу.
  • Redux поєднується з normilzr і denormalizr, щоб допомогти знищити, а потім регідратацію реакцій api.
  • Афродита / неважливі стилі js, не css, але без усіх цих! Важливих скрізь.
  • Завантажувач Svg-react, який завантажує svg в якості компонентів, що реагують.
  • Жменька інших, якщо ви бачите щось інше у списку залежностей, який вам цікаво, залиште записку, і я поясню це.

Структура каталогів

ДОБРЕ. Як тільки я зупинився на 38 залежності bumpers.fm, яку веб-сайт не потребував, настав час написати якийсь фактичний код.

Наша структура каталогів організована навколо двох вхідних точок:

  • index.js, який інстанціює маршрутизатор і зберігає наш основний пакет програм.
  • embed.js, який відповідає за наш менший пакет вбудовування (як це видно у слабкому, твіттерному, середньому тощо).

Звідти ми витягуємо наші маршрути з доречно названого каталогу «route», який наразі є просто простим, єдиним компонентом маршрутизатора, що виглядає так:

Зауважте, ці маршрути вказують на те, що ми називаємо "екранні контейнери".

У Bumpers наші реагуючі компоненти фактично розділені на 3 різні каталоги, залежно від їх функції (4, якщо ви включите каталог маршрутів). Такий спосіб організації компонентів в основному був просто вкрадений безпосередньо з Twitter, який, в свою чергу, думаю, запозичив його у Facebook та багатьох інших проектах. Це виглядає як:

  • Компоненти саме тут живуть наші функціональні компоненти інтерфейсу
  • У цьому контейнері живуть обробники дій для наших компонентів інтерфейсу
  • Екрани технічно - це лише контейнери, але, як правило, роблять більше завантаження сторінок верхнього рівня і менше стосуються обробки дій.
СТОРІННЕ ПРИМІТКА. Насправді я почав лише каталог контейнерів, без "екранів" (що досить часто зустрічається з того, що я бачив у спільноті реагуючих). Я відмовився від цього за рекомендацією Ніколя, і тому, що бачення купи «екранних» суфіксальних файлів, змішаних із моїми неекранними суфіксальними файлами, переймало мене, до пекла.

Останні два каталоги - це каталог магазину та каталог констант. "Магазин" містить усю нашу логіку скорочення, як дії, редуктори, селектори, кінцеві точки api тощо (про які я детальніше розповім нижче), тоді як каталог "константи" містить ... ну ... константи.

Компоненти інтерфейсу

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

Як я вже згадував вище, ми використовуємо Афродіту Ханської академії для створення нашого css.

ШВИДКОВА ПРИМІТКА Спочатку я писав додаток, використовуючи пакунок стилів-завантажувачів, але його нездатність надати переконливу стратегію сервера (те, що я врешті-решт хочу вивчити), було мені достатньо, щоб спробувати щось інше. (Я також звичайно вважав React-Native, про що Ніколя постійно нагадував мені, краще, ніж будь-яке рішення, до якого я самостійно дійшов, тому що він його написав).

Тим не менш, писати мої стилі в JavaScript вийшло досить природно, і за допомогою нових функцій ES6 це можна зробити досить елегантним.

Мені вдалося досягти подібного стилю до того, що ми робили тоді, коли працював у Medium, створюючи шкала типу, кольорові шкали, масштаби zIndex тощо. І навіть змогла використовувати функцію обчислених імен властивостей ES6, щоб абстрагувати мої медіа-запити змінними .

Одне, до чого я не міг увійти, - це називати всі мої імена класів загалом, як-от "ящик" або "контейнер", "головний" або "корінь". Я отримую цілий локальний мем-файл css - але, здається, це виходить ціною налагодження. Натомість я фактично приземлився на іменування семантики недалеко від того, що було викладено в SuitCSS, лише трохи зміненого для javascript (використовуючи «_» замість «-»). На практиці це виглядало приблизно так:

Останнє, що я швидко згадаю, - це всі наші відповідні файли, що живуть у відповідних каталогах компонентів.

Стилі розміщуються в окремому файлі з назвою style.js поряд із відповідними SVG-активами, які імпортуються безпосередньо за допомогою завантажувача svg-react. Це дозволяє дуже легко видаляти компоненти / функції, а не постійно запитувати себе: зачекайте, мені ще потрібен цей css? мені ще потрібен цей svg?

Переписка контейнерів

Чесно кажучи, я не збираюся багато нічого говорити про контейнери ™. Ми нічого не робимо тут, крім розділення каталогів екрана / контейнера (про які я вже розповідав вище).

Однак я намалював ще одну картину для вас (ух, прямо там), тому що мені стало погано за те, що я не мав багато чого сказати про контейнери . І я думав, що це вдалий час для вас, щоб перепочити. Розтягування?

Вибачте.

Магазин

~ ВЖЕ ~. Цей розділ магазину легко може бути його ВЛАСНИЙ ЦІЛЬКИЙ СТАТТІЯ , але я спробую все-таки розглянути його… так що будьте зі мною. Також справедливе попередження - ось-ось отримати ДЕНЗУ.

SIDE ПРИМІТКА, що далі, ймовірно, матиме абсолютно нульовий сенс взагалі, якщо ви не знайомі зі скороченням (http://redux.js.org/). Якщо вам цікаво дізнатися більше про Redux та використовувати його для керування станом у своїх реагуючих додатках - рекомендую ознайомитись із цими навчальними посібниками про яєчні голови, вони безкоштовні, і всі вони вважають досить непоганими: https://egghead.io/courses/getting -початок-зі-редукцією

Наш магазин складається з 4-х файлів верхнього рівня (я детальніше розглядаю кожен з них нижче, але просто швидко)…

  • index.js - наш ініціалізатор магазину
  • reducer.js - перетягує всі редуктори з різних об’єктів в один гігантський метод "комбінезонних редукторів"
  • schema.js - всі наші моделі нормалізатора
  • api.js - помічник api для нашого магазину

Крім цього, наш магазин структурований навколо моделей, з такими каталогами, як користувачі, підказки тощо, - а не з традиційною ієрархією функціональних каталогів верхнього рівня дій /, редукторів /, селекторів /, bleh.

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

ОКЕЙ, але чому тхо? Створюючи цей додаток, я постійно говорив такі речі, як: "я хочу, щоб я працював над користувачем", і майже ніколи не говорив щось на кшталт: "Данг, я хочу змінити купу редукторів" всі вони знаходяться в цьому масовому каталозі редукторів ”.

СТОРІННЯ ПРИМІТКА Я не можу згадати, де я насправді вперше побачив цю стратегію ... але я впевнений, що не вигадав. Якщо ви знаєте когось, хто це зробив, або хто це добре пояснює, залиште записку, і я би радий вказати на це людей. Крім того, я ~ думаю ~ щебетати робить щось подібне. Але я міг би це зробити.

Ніткова зернистість файлів кореневого рівня

Гаразд, тому магазин index.js (коротко згаданий вище) відповідає за три основні завдання:

  1. Імпорт попередньо завантажених, вбудованих даних у наш файл скорочення та встановлення початкового стану магазину (Наш бекенд попередньо вибирає дані, коли користувач отримує доступ до чогось на зразок bumpers.fm/fat, щоб, коли реагує додаток завантажується, не потрібно негайно робити xhr запит на дані користувачів, а натомість він може просто швидко заповнити сторінку).
  2. ініціалізувати наш магазин редукцій з нашими кореневими редукторами.
  3. застосовуючи посередницьке програмне забезпечення, наприклад, thunk, реагуйте на історію браузера, розробники та інші ...

На практиці все це виглядало приблизно так, як описано нижче, але з будь-якої причини мені стало багато горя:

Далі коротко відвідаємо наш файл Redurs.js, який по суті є лише єдиним методом комбінованих рудників, який залучає редуктори з інших наших каталогів і виставляє їх як єдиний гігантський водоспадний редуктор. tbqh, цей файл досить нудний, і я, певно, міг би просто помістити його в index.js . оні.

Однак! Тут варто звернути увагу на те, що редуктор "сутностей" (див. Вище) працює як кеш нашого магазину.

Щоб вирішити це, ми використовували проект під назвою normalizr (https://github.com/paularmstrong/normalizr), щоб примусити наші глибоко вкладені відповіді api JSON у більш керовані / кешовані об'єкти, індексовані ID. Що буде сказати, ми починаємо з більш традиційної відповіді api, а потім перетворюємо її в більш соке, індексований ідентифікатором хеш:

Як ви можете собі уявити, ця техніка кешування ~ надзвичайно корисна ~, коли ви починаєте навігацію по реагуючому додатку - так, як ніби ви отримуєте епізод, який ви, ймовірно, вже знайшли користувача (як автора), який ви зараз можете шукати за допомогою ідентифікатора, використовуючи один із методів вибору, не потрапляючи на ваш бекенд (читайте: майже миттєві навігації. Нічого собі).

Тоді наша schema.js вказує логіку витягування перелічених перетворень сутності для нашого кешу (і для normalizr). Ці відображення відносин у кінцевому підсумку є досить простими для написання, але, безумовно, легко забути. Якщо ви збираєтеся пройти кеш-пам'ять резервного файлу, то варто визначити це.

БІЛЬНА ПРИМІТКА Немає на зображенні вище, Schema.js також містить власну mergeStrategy, яку ми написали спеціально для бамперів. З будь-якої причини, стандартний mergeStrategy, наданий normalizr, спрацьовував у всьому, але я не збираюся тут вникати, оскільки це майже напевно помилка користувача . (Однак, якщо у вас виникають подібні проблеми, залиште записку, і я з радістю поділюся, де ми приземлилися.)

Останній наш кореневий файл у каталозі магазину - api.js.

Після сильного удару головою я помітив, що програмне забезпечення громовідводу (на яке ми покладалися для асинхронних дій) дозволяє нам передавати додатковий аргумент до всіх ваших операцій редукції (поверх відправки та getState).

Пам'ятайте про це з магазину / index.js

Це надзвичайно потужно, і я в кінцевому підсумку використовував його для передачі глобального помічника api у всі наші дії. Цей помічник api (визначений у api.js) забезпечує швидкий доступ до всіх наших кінцевих точок api, за допомогою додаткових помічників для розбору JSON, перевірки помилок тощо. Ви побачите це в дії нижче ... коли ми потрапимо у ... файли дії ... файли ...

Редуктори

Наші редукційні редуктори перетворилися на 3 основні функції.

  1. Визначте початковий стан
  2. Визначте обробник preloadData (для наших вбудованих даних)
  3. Викрийте редуктори оброблювачів дій

Наш початковий стан часто виглядає приблизно так, з константами стану для запиту і активними ідентифікаторами:

Наші обробники попереднього завантаження беруть наші об'єкти необроблених даних та розпаковують об'єкти даних, у цьому випадку встановлюючи активного користувача за замовчуванням:

І типовий редуктор виглядає приблизно так (зверніть увагу на використання обчислених імен властивостей (Es2015). Ми витягуємо їх безпосередньо з визначень дій, описаних нижче).

Дії

У наших файлах дій відбуваються якісь магічні речі. Спочатку ми використовуємо метод createActions "redux-Actions" для визначення назв наших дій:

Ми робимо це для того, щоб у нашому файлі редукторів ми могли використовувати обчислені імена властивостей (згадані раніше), щоб лише в одному місці були визначені наші імена дій. Також погляньте, як ми називаємо наші дії: метод + об'єкт + властивість. Це ~ надзвичайно важливо для збереження читабельності та унікальності всіх ваших ключів редуктора. Я бачив безліч прикладів в Інтернеті людей, які використовують ледачі, загальні імена, такі як "ім'я користувача" або "setUsername" для ключів ... довіряйте, якщо ви це зробите справді поганий час (пам'ятайте, ключі є глобальні та помилки, спричинені конфліктами імен, є головною проблемою для пошуку.

Для асинхронних дій ми використовуємо функцію redux thunk та помічник api, про який ми згадували вище. Це допомагає тримати наші асинхронні методи надто чіткими та зосередженими.

У наведеному вище прикладі ми встановлюємо isFetching на користувальницький об’єкт, запускаємо запит на наш api, перевіряємо відповідь на код статусу помилки, встановлюємо наш jwt маркер, перетворюємо відповідь у json, нормалізуємо відповідь, використовуючи normalizr (для кешування) , а потім встановіть активний стан користувача.

Це найчистіший спосіб поводження з методами асинхронізації у надлишку, який я коли-небудь бачив (не знаю @ мене).

Кінцеві точки

Я ще не бачив, щоб хтось робив ці файли кінцевих точок, але я вважаю, що це дійсно чистий спосіб зберегти ваші відповідні дзвінки api, що живуть в одному місці (не кажучи вже про те, що заглублюючі тести дуже просто). Також зверніть увагу на "izomorphic-fetch" - я клянусь, що колись ми передамо цей матеріал на сервер . Тим часом, приємна річ у використанні fetch - це повернення обіцянки та створення досить чистої api, коли втягується в наші дії з асинхронізацією.

Селектори

Нарешті, наш файл селекторів використовує бібліотеку denormalizr (https://github.com/gpbl/denormalizr) (сестринський проект normalizr) для реконструкції більш корисних даних із нашого кешу. В основному він просто використовує моделі імен для реконструкції великого вкладеного об'єкта - вам цього не потрібно ~ робити, але мені було набагато приємніше / передбачуваніше працювати з даними таким чином.

Крім того, наші селекторні методи виглядають приблизно так, як ви очікували:

Висновок

ОГО. Добре, що вам здалося, що це подорож серйозна. І цей магазин був, мабуть, занадто нудним і втратив, як 90% читачів, тож мені шкода.

Велике спасибі за прочитане, і вибачте, якщо ця публікація була нестерпною. Я просто пообіцяв собі, що опублікую щось подібне, тому що я виявив, що все це лайно було таким шалено розсіяним / важким.

Якщо у вас є якісь запитання щодо будь-чого, залиште коментар або записку, і я зроблю все можливе, щоб відповісти.

❤ жир

ДЕЯКІ Q / A

Так, я безумовно щасливий! Я б брехав, якби я не сказав, що це головна піта, але Бамперс - це в основному величезне додаток для аудіоплеєра - а керування станом у навігаціях та в багатьох безлічі елементів зворотного зв'язку у нас було б шалено важким інакше.

Я думаю, що також можна щось сказати про використання "знайомих" інструментів, коли у вас їх є - і я сподіваюся, що коли-небудь нам вдасться найняти більше фронтендів на "Бамперсах", що вони зможуть зануритися в досить легко, не відчуваючи себе повністю переповнені (і як їм потрібно навчитися все з нуля).

Так, досить багато. Ми зробили подібну річ у Середній, поки я теж був там. Ви повинні бути обережними з тим, як це зробити, через хакерські вставки, але це досить прикольний спосіб підійти до чогось на зразок "рендерингу сторони сервера", без необхідності надавати шаблони реагування на сервері.