Bizim kodumuz sadece * İYİ * değil mi?

Cehennemdeki 6 haftadaki manzaralar, tamponları yeniden yazmak için harcadım.

Ben sadece Bumpers web uygulamasını tepki kullanarak tamamen yeniden yazdım. (Tamponların ne olduğunu bilmiyorsanız, telefonunuzdaki sesli hikayeleri kaydetmek / paylaşmak için kullanılan bu harika chill uygulaması. İndirin, aslında tüm hayatınızı tamamlar. Şimdiye kadar yapılmış en iyi uygulama. Tepki? Aight.)

Her halükarda, buradaki tüm notlarım, düşüncelerim vb. (Başlamadan önce okuduğumu diliyorum. Umarım ondan bir şey çıkarırsın.

önsöz

TANRI. Çerçevelerden nefret ediyorum. Çok.

Ayrıca bir çerçeveye sahip olmamasından ve kendi “çerçevelerini” “yuvarlayan” herhangi birinden de nefret ediyorum. Ayrıca genel olarak kodlamadan da nefret ediyorum. Ve hepsinden önemlisi kod hakkında yazmaktan nefret ediyorum.

Benimle Dayan.

Son zamanlarda kodlama tarzım, bir tür sosyopatik, salgılanan kendinden şüphe uyandırmak ile aşırı kanye benzeri bir tanrı kompleksi arasındaki salınımdı - ya da bütün gün tek başıma dolaşıp yüksek sesle ağlamak veya annemi serbest bırakmak için çağırıyordum. 30 yaşındaki oğlunun “oyunu hızlandırmakta (iyi bir şekilde)” olduğunu biliyor. Doğal olarak bir mola vermek ve yazmak için iyi bir zaman gibi görünüyordu.

(proje yaşam döngüsü üzerine moral, kırmızı notlar benim) - https://medium.com/@iano/moral-over-a-project-lifecycle-975792b54c12#.uwkzt7x4v)

Tepki Seçimi

Küçük bir tarih: Tamponların “web sitesi” çok güzeldi. Yaklaşık 7 es6 sınıfı vardı, dış bağımlılık yoktu ve sadece yaklaşık 759 kod satırı vardı. Toplam.

Düzenleri sunucuda Go uygulamamız tarafından oluşturuldu. Postceler kullandık. Tüm svg'lerimizi koyduğumuz gerçekten basit bir varlık dizinimiz ve bir ya da iki videomuz vardı. Harikaydı. Javascript yazdık. Bunu unuttuk.

Harikaydı. Javascript yazdık. Bunu unuttuk.

Bu arada, Nicolas Gallagher, Twitter’ın React’te mobil web ürününü yeniden yazan bir yıl süren bir projeyi henüz tamamlamış olan ekibin bir parçasıydı.

Nicolas'ı uzun zamandır tanıyorum. Ve o kolayca tanıdığım en düşünceli insanlardan biri. Bundan sonra, React’in Front End Development alanındaki tüm sorunları esasen çözdüğünü ve başka şeyler hakkında endişelenmek için harekete geçtiğini söyledi.

Gerçeğe göre, React bunun için çalışıyordu:

  • arkadaşlarım tarafından daha akıllıca onaylanan sonra benim, Nicolas Gallagher, Alex MacCaw, Guillermo Rauch gibi
  • istemci tarafı oluşturma (ses uygulamaları için iyidir, bu sayede navigasyonlarda oynatmaya devam edebilirsiniz)
  • düşünceli bileşen modeli
  • insanlar CSS'den uzaklaşıyor (ya da en azından zorlayıcı)
  • facebook inekleri yazdı
  • üretim uygulamaları ~ instagram, twitter vb. gibi ~ kullanıyordu
  • insanlar nihayet redux'da bir veri paradigması etrafında yerleşmiş gibi görünüyordu (ve hoşuna gitti)

Ancak aynı zamanda, tepkiler hakkında heyecanlanmadığım bazı şeyler vardı:

  • 700 satırlık javascript paketim ~ 1.5mb olacaktı
  • üretim sunucusu tarafı oluşturma, bir düğüm sunucusu gerektirir (ve o zaman bile çözümler yarı pişmiş gibiydi)
  • stil uygulamaları topluluk genelinde süper parçalara ayrılmıştır (afrodit, css modülleri, stil etiketleri vb. kullanıyor musunuz - bağımlılıklarınız ne durumda?)
  • facebook inekleri yazdı
  • webpack → babel → jsx → sıcak yükleme → kaynak haritaları → yığın olarak krom araçları zavallı küçük macbook'umun sakat kalmasına neden oldu
  • Redux'u öğrenmek için bu videoları “egghead” videoları izlemem gerekti
  • Takım, parçalanmış ve üst kısımdan görünüyordu…

Bütün bunlara rağmen, bunun için gitmeye karar verdik. (Tepki vermenin asıl umudu bir şekilde bizi daha fazla "app-y" hissettiren bir şey inşa etmeye hazırlardı).

“Gerisini” seçme

Tepki üzerine karar verdikten sonra (görünüm lib'iniz), bir avuç başka kararla gerçekten geride kaldığınızı ortaya koyuyor: Devleti nasıl yöneteceksiniz? Bileşenlerinizi nasıl tasarlayacaksınız? Es6 kullanacak mısın? ES7? es2015? JSX? Bunlar ne anlama geliyor? Web paketini kullanacak mısın? veya browserify? Her şey nerede yaşayacak? ...

TJ Holowaychuk'ın kazan plakası deposunu (https://github.com/tj/frontend-boilerplate/tree/master/client) (temelde eski modası geçmiş olduğunu itiraf ettiği) birleştirerek başladım. twitter'ın nereye indiği hakkında (yarısı anlayamadığım kadarıyla, ama ne olursa olsun, epostayı buradan okuyabilirsiniz: https://gist.github.com/fat/9ab5325ab39acfe242bc7849eb9512c4).

Ayrıca, github'daki birçok “evrensel tepki - redux-glahblbhalkd” kazan plakası deposundan birkaçına baktım, ancak hepsi bana büyük oranda panik atak verdi.

Her neyse, her nasılsa, biraz mutlu olduğum bir yere gitmeyi başardım, ki şöyle görünüyor:

  • Babil (“ön ayar” ile: [“es2015”, “sahne-0”, “tepki”]) Böylece yayılma operatörleri, ok işlevleri vb. Gibi tüm yeni çılgın şeyleri kullanabilirim.
  • Sıcak yükleyicili Webpack (çalıştığı sırada) belirli uygulama durumlarında stil güncellerken kullanışlı buldum. Ama kesinlikle bana çok fazla endişe yarattı. Açıkçası, kimsenin web paketinin nasıl çalıştığını gerçekten anlamadığını hissediyorum. Ve hepimiz sadece tesadüfî özellikler ve eklentiler atmaya devam edersek, hepsinin ortaya çıkması için dua ediyoruz. AggressiveMergingPlugin? emin. OccurrenceOrderPlugin? tamam. DedupePlugin? ince.
  • Redux, normilzr ve denormalizr ile birleşti ve api yanıtlarının yok edilmesine ve rehidre edilmesine yardımcı oldu.
  • Afrodit / önemsiz js stilleri, css değil, her şey olmadan!
  • Svg’leri satır içi reaksiyon bileşenleri olarak yükleyen Svg-reaksiyon yükleyicisi.
  • Bu bağımlılık listesinde merak ettiğiniz bir başka şey görürseniz bir avuç dolusu not bırakın ve açıklayacağım.

Dizin yapısı

TAMAM. Bumpers.fm 38 bağımlılığına karar verdikten sonra, web sitesinin gerekmediğini, bazı gerçek kodları yazma zamanı gelmişti.

Rehber yapımız iki giriş noktası etrafında düzenlenmiştir:

  • Temel uygulama paketimiz için yönlendirici ve depolamayı başlatan index.js.
  • Daha küçük embed paketimizden sorumlu olan embed.js (zayıf, twitter, orta, vb. görüldüğü gibi).

Oradan, rotalarımıza şu anda şuna benzeyen basit, tek bir tepki yönlendirici bileşen olan apt.

Bu rotaların screen container olarak adlandırdığımız noktaya dikkat edin.

Tamponlarda tepki bileşenlerimiz aslında işlevlerine bağlı olarak 3 farklı dizine bölünür (4 yol dizinini eklerseniz). Bileşenleri organize etmenin bu yolu temel olarak doğrudan Twitter'dan çalındı, sanırım bunu Facebook'tan ve diğer projelerden ödünç aldım. Şuna benziyor:

  • bileşenleri, işlevsel kullanıcı arabirimi bileşenlerimizin yaşadığı yerdir
  • UI bileşenlerimiz için işlemcilerin yaşadığı yer burasıdır
  • ekranlar teknik olarak bunlar sadece konteynırlardır - ancak genellikle daha fazla üst düzey sayfa alma işlemi yaparlar ve işlem işleme ile daha az ilgilenirler.
YAN NOT Aslında ben sadece bir konteyner dizini ile başladım, “ekranlar” yok (bu, tepki topluluğunun etrafında gördüğümden oldukça yaygın). Nicolas’ın tavsiyesi üzerine bundan uzaklaştım ve “ekran” ekli dosyaları bir araya getirdiğim için “ekran” olmayan ekli dosyaları görmek beni rahatsız ediyordu.

Son iki dizin “store” dizini ve “constants” dizinidir. “Mağaza” eylemler, redüktörler, seçiciler, api uç noktaları vb. Gibi tüm redux mantığımızı (aşağıda daha derinlemesine gireceğim) içerirken, “sabitler” dizini… iyi… sabitleri içerir.

UI Bileşenleri

UI bileşenlerimiz oldukça standart, işlevsel, durumsuz, sunumlu, reaktif bileşenlerdir. İşte standart bir bölüm bileşeni (daha birçok küçük, standart, işlevsel, durumsuz, sunumlu, reaktif bileşenlerden oluşur).

Yukarıda bahsettiğim gibi, css'imizi oluşturmak için Khan Academy’nin Afroditini kullanıyoruz.

HIZLI DİKKAT Başlangıçta, stil yükleyici paketini kullanarak uygulamayı yazdım, ancak inandırıcı bir sunucu stratejisi (sonunda keşfetmek istediğim bir şey) sağlayamamam, başka bir şeyi denemem için yeterliydi. (Ayrıca rutin olarak React-Native'i de düşündüm, Nicolas sürekli bana daha iyi olduğunu hatırlatıyordu, ne yazdıysa bağımsız olarak hangi çözüme ulaştığımı.

Bununla birlikte, stillerimi javascript'e yazmak oldukça doğal geldi ve yeni ES6 özellikleri sayesinde oldukça şık bir hale getirilebilir.

Medium'da çalıştığımda yaptığımıza benzer bir stil elde edebildim, tip ölçekleri, renk ölçekleri, zIndex ölçekleri vb. Yarattım. Ayrıca medya sorgularımı değişkenlere soyutlamak için ES6 hesaplanmış özellik adları özelliğini bile kullanabildim. .

Giremediğim tek şey, tüm sınıflarımı "kutu" veya "konteyner" veya "ana" veya "kök" gibi genel olarak adlandırmaktı. Tüm yerel kapsamlı css meme'leri alıyorum; ancak hata ayıklama fiyatına sahip görünüyor. Bunun yerine, aslında SuitCSS'de belirtilenden çok uzak olmayan bir adlandırma anlamına inmiştim, javascript için biraz değiştirdim (“-” yerine “_” kullanarak). Uygulamada bu şuna benziyordu:

Çabucak anlatacağım son bir şey, tüm ilgili dosyalarımızın kendi bileşen dizinlerinde yaşadığıdır.

Stiller, doğrudan svg-react-loader kullanılarak içe aktarılan ilgili svg varlıklarının yanı sıra, style.js adlı ayrı bir dosyaya yerleştirilir. Bunu yapmak, bileşenleri / özellikleri silmeyi çok kolaylaştırır ve sürekli olarak kendinize sormaktan vazgeçmeyin: bekleyin, hala bu css'e ihtiyacım var mı? Hala bu svg'ye ihtiyacım var mı?

Konteynerler Intermission

Açıkçası, container ™ hakkında hiçbir şey söylemeyeceğim. Burada zaten yukarıda ele aldığım ekran / konteyner dizinlerini ayırmanın ötesinde özel bir şey yapmıyoruz.

Ancak sizin için başka bir resim çizdim (vay, şurada) çünkü konteynırlar hakkında söyleyecek fazla şeyim olmadığı için kendimi kötü hissettim. Ve belki de biraz mola vermenin iyi bir zaman olduğunu düşünmüştüm. Uzatmak?

Üzgünüm.

mağaza

~ ALRIGHT ~. Bu mağaza bölümü kolayca “KENDİ TÜM MADDE be olabilirdi, ama hepiniz için onu aramaya çalışacağım… bu yüzden benimle ayı. Aynı zamanda adil uyarı - DENSE almak üzere.

YAN NOT: Redux’a aşina olmadığınız sürece, kesinlikle aşağıdakileri kesinlikle sıfır bir anlam ifade edecektir (http://redux.js.org/). Eğer Redux hakkında daha fazla bilgi edinmek ve tepki uygulamalarınızdaki durumu yönetmek için kullanmak istiyorsanız - bu hakaretli öğreticilere göz atmanızı öneririm, ücretsiz ve hepsi oldukça iyi düşünüyor -Başladı-ile-Redux

Mağazamız 4 en üst düzey dosyadan oluşmaktadır (her biri için daha fazla ayrıntıya giriyorum, ancak çok hızlı)

  • index.js - mağaza başlatıcımız
  • reducer.js - tüm redüktörleri farklı nesnelerden dev bir "combineReducers" yöntemine çeker
  • schema.js - tüm normalizr modellerimiz
  • api.js - mağazamız için bir api yardımcısı

Bunun ötesinde mağazamız, geleneksel redux üst düzey fonksiyonel dizin eylemler hiyerarşisi /, redüktörler /, seçiciler /, kanamalar yerine, kullanıcılar, bilgi istemleri, vb. Gibi dizinlerle modeller etrafında yapılandırılmıştır.

Elbette redux'un gerektirdiği geleneksel eylem, redüktör vb. Ayrıştırma işlemlerine sahibiz - ancak bu şimdi model seviyesinde dizinde bulunan dosya seviyesinde yapılır (görüntüdeki genişletilmiş kullanıcı klasörüne bir göz atın. ne demeye çalıştığımın bir örneği).

Tamam, ama neden tho? Bu uygulamayı oluştururken kendimi sürekli şöyle şeyler söyleyerek buldum: “dang rly, kullanıcı şeyler üzerinde çalışmak istiyorum” ve neredeyse asla şöyle bir şey söylemedi: “dang, rly her seferinde bir miktar redüktörü değiştirmek istiyorum, elbette memnunum hepsi bu devasa fevkalade indirgeyiciler dizininde ”.

YAN NOT Bu stratejiyi ilk nerede gördüğümü hatırlayamıyorum… ama onu icat etmediğime eminim. Bunu yapan veya iyi bir şekilde açıklayan birini tanıyorsanız, bir not bırakın; ben de insanları bu konuya yönlendirmekten mutluluk duyarım. Ayrıca sanırım twitter da benzer şeyler yapıyor. Ama bunu telafi ediyor olabilirim.

Kök seviye dosyalarının nitty gritty

Pekala, mağazanın index.js'si (yukarıda kısaca bahsedilmiştir) 3 ana görevden sorumludur:

  1. Önceden getirilmiş, gömülü verileri redux mağazamıza içe aktarma ve mağazanın başlangıç ​​durumunu belirleme (Arka uç, kullanıcı bumpers.fm/fat gibi bir şeye eriştiğinde verileri önceden alır, böylece tepki uygulaması yüklendiğinde hemen xhr kullanıcı verisi için istek ve bunun yerine sadece hızlı bir şekilde sayfa doldurabilir).
  2. redux mağazamızı kök düşürücülerimizle başlatıyorum.
  3. thunk gibi ara katman yazılımları kullanmak, yönlendiricinin tarayıcı tarihçesine, geliştiricilere ve daha fazlasına tepki vermek

Uygulamada, bunların hepsi aşağıdaki yöntem gibi bir şeye bakıyordu - ama ne sebeple olursa olsun beni çok fazla üzdü:

Daha sonra kısaca, diğer dizinlerden düşürücüleri çeken ve bunları tek bir devir düşürücü şelale olayı olarak ortaya çıkaran tek bir birleşikReducers yöntemi olan indirgemec.js dosyamızı kısaca ziyaret edelim. tbqh, bu dosya oldukça sıkıcı ve muhtemelen sadece index.js put içine koyabilirdim. hoppala.

Ancak! Burada görülmeye değer bir şey, “varlık” düşürücümüzün (yukarıda görüldüğü gibi) mağazamızın önbelleği gibi çalışmasıdır.

Bunu kaldırmak için, derinden iç içe geçmiş JSON api yanıtlarımızı daha yönetilebilir / önbelleklenebilir ID indeksli nesnelere zorlamak için normalizr (https://github.com/paularmstrong/normalizr) adlı bir proje kullandık. Yani, daha geleneksel bir api cevabı ile başlıyoruz ve daha sonra onu bir saucier, ID indeksli varlık hashına dönüştürüyoruz:

Tahmin edebileceğiniz gibi, bu önbellek tekniği ~ süper yararlıdır ~ bir reaksiyon uygulaması etrafında gezinmeye başladığınızda - bir kimliğe bürünmüş gibi, muhtemelen kimliğini kullanarak araştırabileceğiniz bir kullanıcı (yazar olarak) almışsınızdır Seçici yöntemlerden biri, arka ucunuzu vurmak zorunda kalmadan (okuma: neredeyse anlık gezinmeler. vay).

Daha sonra schema.js, önbellek için (ve normalizr için) yukarıdaki varlık dönüşümlerini kaldırma mantığını belirlediğimiz yerdir. Bu ilişki eşlemeleri yazmak oldukça basit - ama kesinlikle unutması kolay. Redux önbellek rotasına gidecekseniz, bunlara bir bakış atmaya değer.

YAN DİKKAT Yukarıda resimde gösterilmemiştir, Schema.js ayrıca tamponlar için özel olarak yazdığımız özel bir mergeStrategy içerir. Sebep ne olursa olsun, normalizr tarafından sağlanan varsayılan birleştirmeStrateji kendi kendine açıyordu, ama ben bu işe girmeyeceğim çünkü neredeyse kesinlikle kullanıcı hatasıydı . (Eğer benzer sorunlar yaşıyorsanız, bir not bırakın ve indiğimiz yeri paylaşmaktan mutluyum dedi.)

Mağaza dizinindeki son kök dosyamız api.js.

Kafamı çok sıktıktan sonra, thunk ara katman yazılımının (async eylemleri için güvendiğimiz) tüm redux eylemlerinize (gönderme ve getState'e) ekstra bir argüman aktarmamıza izin verdiğini fark ettim.

Bunu store / index.js sitesinden hatırlayın

Bu inanılmaz derecede güçlü ve onu tüm eylemlerimize global bir api yardımcısını iletmek için kullandım. Bu api yardımcısı (api.js'de tanımlanmıştır), JSON ayrıştırma, hata denetimi ve daha fazlası için ek yardımcılarla tüm api uç noktalarımıza hızlı erişim sağlar. Bunu, eylemde… eylemde… dosyalara girdiğimizde…

Redüktörler

Redux redüktörlerimiz 3 ana fonksiyona sahip oldu.

  1. Bir başlangıç ​​durumu tanımla
  2. Bir preloadData işleyicisi tanımlayın (gömülü verilerimiz için)
  3. Eylem işleyicisi azaltıcılarını gösterin

İlk durumumuz, genellikle istek durumu ve etkin kimliği için durum sabitleri olan böyle bir şeye benziyor:

Önceden yükleme işleyicilerimiz ham veri nesnelerimizi alır ve bu durumda varsayılan bir aktif kullanıcı ayarlayarak veri varlıklarını paketinden çıkarır:

Tipik bir redüktör buna benzer bir şey gibi gözükür (hesaplanan özellik adlarının kullanılmasına dikkat edin (Es2015). Bunları doğrudan aşağıda belirtilen işlem tanımlarından alırız).

Eylemler

Eylem dosyalarımızın içinde bazı sihirli şeyler var. Öncelikle eylem isimlerimizi tanımlamak için “redux-actions” createActions metodunu kullanıyoruz:

Bunu, redüktör dosyamızda, hesaplanan mülk isimlerini (daha önce bahsettiğimiz) sadece bir yerde tanımlanmış eylem isimlerimizi kullanmak için kullanabiliriz. Ayrıca eylemlerimizin isimlendirilme biçimine de bir göz atın: method + object + property. Bu, tüm redüktör anahtarlarınızı okunabilir ve benzersiz tutmak için ~ süper ~ önemlidir. Anahtarlar için “username” veya “setUsername” gibi tembel, jenerik isimler kullanan insanların web çevresinde pek çok örnek gördüm… güven, sen bunu yaparsan çok kötü zaman geçireceksin (unutmayın, küresel ve isimlendirme çatışmalarının yol açtığı hatalar izini sürmek için büyük bir pidedir).

Eşzamansız eylemler için redux thunk ve yukarıda bahsettiğimiz api yardımcısını kullanıyoruz. Bu, zaman uyumsuz yöntemlerimizin çok sıkı ve odaklanmış kalmasını sağlar.

Yukarıdaki örnekte, isFetching'i kullanıcı nesnesine koyarız, api'mize bir istek göndeririz, bir hata durum kodunun cevabını kontrol ederiz, jwt token'imizi ayarlar, yanıtı json'a dönüştürür, normalizr kullanarak cevabı normalleştirir (önbellekleme için) ve ardından etkin kullanıcı durumunu ayarlayın.

Bu, daha önce hiç görmediğim redux'da zaman uyumsuz yöntemlerin en temiz yoludur.

Endpoints

Bu son nokta dosyalarını yapan başka birini görmedim - ancak ilgili api'nizin tüm canlıları tek bir yerde yaşamasını sağlamanın gerçekten temiz bir yolunu buldum (bahsetmeksizin, saplama testlerini çok kolay hale getirdiği söylenebilir). Ayrıca “izomorfik getir” e de dikkat edin - yemin ederim bir gün bu işleri sunucuda yapacağız . Bu arada, fetch kullanımıyla ilgili en güzel şey, bir söz vermesi ve zaman zaman async hareketlerimize dahil edildiğinde oldukça temiz bir API olmasını sağlıyor.

Seçiciler

Son olarak, seçici dosyamız önbellekten daha uygulanabilir verileri yeniden oluşturmak için denormalizr kütüphanesini (https://github.com/gpbl/denormalizr) (normalizr’in kardeş projesi) kullanır. Temel olarak sadece iç içe geçmiş bir nesneyi yeniden yapılandırmak için ad modellerini kullanır - bunu yapmak zorunda değilsin, ancak veriyle bu şekilde çalışmayı daha eğlenceli / öngörülebilir buldum.

Bunun dışında, seçici yöntemlerimiz beklediğiniz gibi görünüyor:

Sonuç

VAY. Tamam, bu ciddi bir yolculuk gibi geldi. Ve bu mağaza malzemesi muhtemelen çok sıkıcıydı ve okuyucuların% 90'ı gibi kaybediyordu, özür dilerim.

Okuduğunuz için çok teşekkürler ve bu yazı kabul edilemezse üzgünüm. Sadece kendime böyle bir şey yayınlayacağıma söz verdim, çünkü bütün bu saçmalıkları çok delice dağılmış / zor bulmayı öğrendim.

Herhangi bir şey hakkında herhangi bir sorunuz varsa, yorum veya not bırakın ve cevaplamak için elimden geleni yapacağım.

❤ şişman

BAZI Q / A

Ya, kesinlikle mutluyum! Büyük bir pide olduğunu söylemezsem yalan söylemiş olurdum, ama Bumpers temelde sadece büyük bir ses çalar uygulamasıydı - ve navigasyonlar boyunca ve her şeyden önce aldığımız birçok küçük geri bildirim öğesinde durumu tersine çevirmek zor olurdu.

Onlara sahip olduğunuzda “tanıdık” araçları kullanma hakkında söylenecek bir şeyler olduğunu düşünüyorum - ve Bumpers'da daha fazla ön büro kiralayabileceğimizi, tamamen hissetmeden kolayca rahatça dalabileceklerini umuyorum. boğulmuş (ve sıfırdan her şeyi öğrenmeleri gerektiği gibi).

Evet, hemen hemen. Ben de orada iken Medium'da da benzer bir şey yaptık. Senaryo enjeksiyonu hackleri nedeniyle nasıl yaptığınıza dikkat etmelisiniz, ancak sunucuda tepki şablonlarını oluşturmak zorunda kalmadan “sunucu tarafı oluşturma” hissi gibi bir şeye yaklaşmanın oldukça iyi bir yoludur.