Не е ли нашият код само * НАЙ-ДОБРОТО *

Изгледи от 6-те седмици в ада прекарах пренаписвайки брони в реакция.

Просто напълно пренаписах уеб приложението Bumpers, използвайки react. (Ако не знаете какво представлява бронята, това е супер прохладно приложение за записване / споделяне на аудио истории на вашия телефон. Изтеглете го, всъщност ще е целия ви живот. Това е най-великото приложение, правено някога. Реагира ли? Айт е.)

Във всеки случай това, което следва, са всички мои бележки, мисли и т.н. по този процес. (Неща, които ми се иска да бях прочел преди да започна). Надяваме се, че ще получите нещо от това.

предговор

БОГ. Мразя рамки. Много.

Също така мразя да нямам рамка и всеки, който „търкаля“ собствената си „рамка“. Аз също просто мразя кодирането, като цяло. И най-вече мразя да пиша за код.

Така че понасяйте с мен.

Напоследък стилът ми на кодиране беше някакво социопатично, трептене между пристъпи на осакатяващо съмнение в себе си и екстремен бог-подобен комплекс на канье - където аз или марширувам около моята умилителна цял ден, плача на глас, или призовавам майка ми да пусне знаят, че нейният 30-годишен син „е *** в играта (по добър начин)“. Така че естествено изглеждаше подходящ момент да си починете и да напиша за него.

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

Избор на Реакция

Малка история: „Уебсайтът на бронята“ беше толкова хубав. Имаше около 7 класове es6, нямаше външни зависимости и само около 759 реда код. Обща сума.

Разполаганията му бяха представени на сървъра от нашето Go приложение. Използвахме postcss. Имахме наистина проста директория с активи, в която поставихме всички наши SVG, и видео или две. Беше чудесно. Написахме някои javascript. Забравихме за това.

Беше чудесно. Написахме някои javascript. Забравихме за това.

Междувременно Николас Галахър беше част от екипа, който току-що завърши проект, пренаписал година, пренаписвайки мобилния уеб-продукт на Twitter в React.

Познавам се с Никола отдавна. И той лесно е един от по-обмислените хора, които познавам. И когато след това той ми каза, че React е решил по същество всички проблеми в пространството за развитие на Front End и че е продължил да се тревожи за други неща, аз му казах веднага.

За номинална стойност React вършеше тези неща:

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

Но в същото време в реакцията имаше няколко неща, от които не се вълнувах:

  • Моят пакет от 700 линии на JavaScript беше на път да стане ~ 1.5mb
  • рендерирането от страна на сървъра за производство изисква сървър на възел (и дори тогава решенията изглеждат наполовина изпечени)
  • стайлинговите практики са супер фрагментирани в общността (използвате ли афродита, css-модули, етикети за стил и т.н. - какво ще кажете за вашите зависимости?)
  • facebook nerds го написа
  • webpack → babel → jsx → горещо зареждане → източници на карти → хромирани инструменти като стека осакатяват моя беден малък macbook
  • Трябваше да гледам тези видеоклипове с „пайерхед“, за да науча намаление
  • инструментална екипировка изглеждаше разединена и отгоре ...

Въпреки всичко това решихме да продължим. (Основната надежда да реагираме по някакъв начин ще ни нагласи да изградим нещо, което се чувстваше по-„ап-у“).

Избор на "останалото"

Оказва се, че след като решите да реагирате (визуалната си либ), наистина оставате с шепа други решения: Как ще управлявате държавата? Как ще стилизирате компонентите си? Ще използвате ли es6? es7? es2015? jsx? Какво означават тези? Ще използвате уебпакет? или да сърфирате? Къде ще живее всичко? ...

Започнах, като обединих заедно репото на котел на TJ Holowaychuk (https://github.com/tj/frontend-boilerplate/tree/master/client) (което той признава, че по същество е остарял в readme) и този дълъг имейл, на който Николай беше писал мен за това къде е кацнал туитър (половината от които не разбрах по това време, но независимо, можете да прочетете имейла в неговата цялост тук: https://gist.github.com/fat/9ab5325ab39acfe242bc7849eb9512c4).

Също така разгледах няколко от многото репозитори на котлоните за универсално реагиране-редукс-глахбълбхалд на github, но те до голяма степен всички ми създаваха панически атаки.

Във всеки случай по някакъв начин успях да стигна до място, от което съм някак доволен, което изглежда така:

  • Babel (с „предварително зададени настройки“: [„es2015“, „stage-0“, „реагира“]) Така че мога да използвам всички луди нови лайна като оператори за разпространение, функции със стрелки и т.н.
  • Уебпакет с горещи товарачи, който (когато работеше) намерих по-удобен при актуализиране на стила в определени състояния на приложението. Но определено ми предизвика много тревожност. Честно казано, имам чувството, че никой не разбира истински как работи уебпакетът. А ние просто продължаваме да хвърляме произволни свойства и плъгини към него, молейки се, че всичко ще се окаже. AggressiveMergingPlugin? сигурен. OccurrenceOrderPlugin? Добре. DedupePlugin? глоба.
  • Redux, съчетан с normilzr и denormalizr, за да помогне при разрушаване и след това рехидратиращ api реакции.
  • Афродита / не-важни js стилове, не css, но без всички тези! Важни навсякъде.
  • Svg-reag-loader, който зарежда svg-и като реагиращи компоненти.
  • Шепа други, ако видите нещо друго в този списък на зависимостите, от който се интересувате, оставете бележка и аз ще ви обясня.

Структура на директория

ДОБРЕ. След като се спрях на 38 зависимости bumpers.fm, които уебсайтът не беше необходим, беше време да напиша действителен код.

Структурата на нашата директория е организирана около две входни точки:

  • index.js, който създава рутера и съхранява за основния ни пакет от приложения.
  • embed.js, който е отговорен за по-малкия ни пакет за вграждане (както се вижда в слайк, туитър, среда и т.н.).

Оттам извличаме нашите маршрути от подходящо наречената директория „route“, която в момента е просто един, единствен компонент за реагиране на рутер, който изглежда така:

Забележете, че тези маршрути сочат към това, което наричаме „екранни контейнери“.

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

  • компоненти, тук живеят нашите функционални потребителски компоненти
  • контейнерите тук живеят обработчиците на действия за нашите потребителски компоненти
  • технически екрани това са само контейнери, но обикновено правят повече извличане на страници от най-високо ниво и не се занимават с действия.
СТРАНИЧНА ЗАБЕЛЕЖКА Всъщност започнах само с директорията на контейнерите, без „екрани“ (което е доста често срещано от това, което видях в общността на реакциите) Отместих се от това по препоръка на Николай и понеже виждането на куп „екранни“ суфиксирани файлове, смесени с моите неекранни суфиксирани файлове, притесняваше адът от мен.

Последните две директории са директория „магазин“ и директория „константи“. „Магазинът“ съдържа цялата ни редукционна логика като действия, редуктори, селектори, крайни точки на api и т.н. (които ще вляза в по-голяма дълбочина по-долу), докато директорията „константи“ съдържа… добре… константи.

UI компоненти

Нашите потребителски интерфейс компоненти са доста стандартни, функционални, без гражданство, презентационни, реактивни компоненти. Ето един стандартен епизоден компонент (който се състои от много други по-малки, стандартни, функционални, без гражданство, презентационни, реактивни компоненти).

Както споменах по-горе, ние използваме Афродита на Кан Академия, за да генерираме css.

БЪРЗА ЗАБЕЛЕЖКА Първоначално написах приложението, използвайки пакета за стилово зареждане, но неспособността му да предостави убедителна стратегия за сървър (нещо, което в крайна сметка искам да проуча), беше достатъчно за мен да опитам нещо друго. (Също така рутинно смятах React-Native, за което Никола постоянно ще ми напомня, че е по-добре, отколкото всяко решение, до което бях стигнал независимо, защото той го беше написал).

Независимо от това, писането на стиловете ми в javascript дойде съвсем естествено и с помощта на нови функции на ES6 можеше да се направи доста елегантно.

Успях да постигна подобен стил на този, който направихме, когато работех в Medium, създавайки тип мащаби, цветни везни, zIndex мащаби и т.н. И дори успях да използвам функцията за изчислени имена на свойства на ES6, за да абстрахирам моите медийни заявки в променливи ,

Едно нещо, в което не бих могъл да вляза, беше да назовавам всичките си имена на класове общо, като „кутия“ или „контейнер“ или „главен“ или „корен“. Получавам целия локален css meme meme - но изглежда, че идва с цената на отстраняването. Вместо това кацнах на семантично именуване, недалеч от това, което беше очертано в SuitCSS, просто леко модифицирано за JavaScript (използвайки „_“ вместо „-“). На практика това изглеждаше така:

Едно последно нещо, което бързо ще спомена е, че всичките ни съответни файлове живеят в съответните им директории на компоненти.

Стиловете се поставят в отделен файл, наречен style.js, наред със съответните SVG активи, които се импортират директно с помощта на svg-react-loader. Това прави много лесно да изтривате компоненти / функции и да не се оставяте постоянно да се питате: изчакайте, все още имам ли нужда от този css? все още имам нужда от този svg?

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

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

Направих обаче друга снимка за теб (уау, точно там), защото се почувствах зле, защото нямах много да кажа за контейнерите . И аз мислех, че това е подходящ момент, за да си починете. Опъвам, разтягам?

Извинете.

магазин

~ ~ Добре. Тази секция в магазина лесно може да бъде нейният СОБСТВЕНО ЦЯЛО СТАТИЯ , но аз ще се опитам да го преправя за вас… така че понасяйте с мен. Също честно предупреждение - на път е да получите DENSE.

СТРАНИЦА ЗАБЕЛЕЖКА, което следва, вероятно ще има абсолютно нулев смисъл, освен ако не сте запознати с redux (http://redux.js.org/). Ако се интересувате да научите повече за Redux и да го използвате за управление на състоянието във вашите приложения за реакция - препоръчвам ви да проверите тези уроци за патладжани, те са безплатни и всички смятат за доста добри: https://egghead.io/courses/getting -started-с-Redux

Магазинът ни е съставен от 4 файла от най-високо ниво (навлизам в повече подробности за всеки по-долу, но просто бързо) ...

  • index.js - нашия магазин инициализатор
  • reducer.js - дърпа всички редуктори от различни обекти в един гигантски метод „комбинирани редуктори“
  • schema.js - всички наши модели normalizr
  • api.js - помощник на api за нашия магазин

Отвъд това, нашият магазин е структуриран около модели, с директории като потребители, подкани и т.н., а не от традиционната редукционна йерархия на функционалните директории на най-високо ниво на действия /, редуктори /, селектори /, bleh.

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

ОКАЙ, но защо Тхо? В изграждането на това приложение се озовах постоянно да казвам неща от рода на: "dang I rly искам да работя върху потребителски неща rn" и почти никога не казвам нещо от рода на: "dang, аз искам да сменя един куп редуктори наведнъж, сигурно се радвам всички те са в тази масивна директория за редуктори “.

СТРАНА ЗАБЕЛЕЖКА Не мога да си спомня къде всъщност видях тази стратегия ... но съм сигурен, че не съм я измислил. Ако познавате някой, който го е направил или който го обясни добре, оставете бележка и ще се радвам да насоча хората към това. Също така аз мисля ~ twitter прави нещо подобно. Но бих могъл да го измисля.

Nitty пестеливост на файловете на кореново ниво

Добре, така че магазинът index.js (накратко споменат по-горе) отговаря за 3 основни задачи:

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

На практика всичко това в крайна сметка изглеждаше като метод по-долу - но по някаква причина ми причини много мъка:

След това нека посетим накратко нашия файл Redurs.js, който по същество е само един метод на комбинация комбинирани, който дърпа редуктори от другите ни директории и ги излага като един гигантски водопад с редуктор. tbqh, този файл е доста скучен и вероятно бих могъл просто да го сложа в index.js . Опа.

Въпреки това! Едно нещо, което си струва да се обадим тук, е, че редукторът ни „субекти“ (видян по-горе) работи като кеша на нашия магазин.

За да оттеглим това, използвахме проект, наречен normalizr (https://github.com/paularmstrong/normalizr), за да принудим нашите дълбоко вложени JSON api отговори в по-управляеми / кешируеми ID-индексирани обекти. Което означава, че започваме с по-традиционен api отговор и след това го превръщаме в по-сочен, индексиран с идентификатор идентификатор на субект:

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

Нашата schema.js тогава е мястото, където ние определяме логиката за изтегляне на горните трансформации на субекта за нашия кеш (и за normalizr). Тези съпоставяния на връзки в крайна сметка са много прости за писане - но определено лесно се забравят. Ако ще отидете на маршрута за кеширане на редукциите, е редно да си ги представите.

СТРАНИЦА ЗАБЕЛЕЖКА Не е изобразено по-горе, Schema.js също съдържа персонализиран mergeStrategy, който написахме специално за бронята. По каквато и да е причина, по подразбиране mergeStrategy, предоставено от normalizr, се отключваше от само себе си, но няма да вляза в това тук, тъй като беше почти сигурна грешка на потребителя . (Това каза, ако имате подобни проблеми, оставете бележка и ще се радвам да споделя къде сме кацнали.)

Последният ни корен файл в директорията на магазина е api.js.

След много удряне в главата ми забелязах, че средният софтуерен пакет (на който разчитахме за асинхронни действия) ни позволява да предадем допълнителен аргумент на всички ваши редукционни действия (отгоре на изпращане и getState).

Запомнете това от store / 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 (за кеширане) и след това задайте активното състояние на потребителя.

Това е най-чистият начин за работа с асинхронните методи в редукс, който някога съм виждал (не ме харесвайте @).

Endpoints

Не съм виждал някой друг да прави тези файлове в крайните точки - но мисля, че това е наистина чист начин да поддържате съответните си api обаждания на всички живи на едно място (да не говорим, че тестовете за упоритост са много лесни). Обърнете внимание и на „изоморфния добив“ - кълна се някой ден, че ще пренесем тези неща на сървъра . Междувременно, готиното при използването на извличането е, че то връща обещание и прави доста чист api, когато се вмъкне в нашите асинхронни действия.

селектори

И накрая, нашият селектор файл използва библиотеката denormalizr (https://github.com/gpbl/denormalizr) (сестринският проект на normalizr), за да конструира по-работещи данни от нашия кеш. По принцип просто използва моделите с имена, за да реконструира голям вложен обект - нямате ~ да правите това, но намерих много по-приятно / предсказуемо да работите с данни по този начин.

Освен всичко това, нашите селекторни методи изглеждат почти както бихте очаквали:

заключение

ЕХА. Добре, че се чувствахте като сериозно пътуване. И тези неща в магазина вероятно бяха твърде скучни и се загубиха като 90% от читателите, така че съжалявам.

Благодаря много за четенето и съжалявам, ако този пост беше непоносим. Просто си обещах, че ще публикувам нещо подобно, защото открих, че научавам всички тези лайна да са толкова безумно разпръснати / твърди.

Ако имате въпроси относно нещо, оставете коментар или бележка и ще направя всичко възможно да отговоря.

❤ мазнини

НЯКОЙ Q / A

Да, определено съм щастлив! Ще се лъжа, ако не казах, че това е основна пита, но Bumpers е просто огромно приложение за аудио плейър - и управлението на състоянието в навигациите и в многото малки елементи за обратна връзка, които имаме навсякъде, би било безумно трудно в противен случай.

Мисля, че има какво да се каже и за използването на „познати“ инструменти, когато ги имате - и се надявам, ако някога успеем да наемем повече фронтенди в „Бъмпери“, че те ще могат да се гмуркат в сравнително лесно, без да се чувстват напълно претоварени (и като те трябва да научат всичко от нулата).

Да, доста. Направихме подобно нещо в Medium, докато аз също бях там. Трябва да бъдете внимателни как го правите, защото на хакерските инжекционни скриптове, но това е доста готин начин да подходите към нещо като усещането на „сървърното рендериране“, без да се налага да правите шаблони за реакция на сървъра.