Blog.

Sunucu-tarafl─▒ React ComponentÔÇÖleri ve StreamÔÇÖler

Cover Image for Sunucu-tarafl─▒ React ComponentÔÇÖleri ve StreamÔÇÖler
Baris Guler
Baris Guler

Bundan birka├ž sene ├Ânce componentÔÇÖlerin cloud ortam─▒ndan servis edilmesine dair bir yaz─▒ ├╝zerinden k├╝├ž├╝k bir deneme yapm─▒┼čt─▒m. Temelde lambda fonksiyonlar─▒ halinde organize edilmi┼č UI componentÔÇÖlerinin hem bundle costÔÇÖunu minimuma ├žekerek render edebilmek, buradan da cloudÔÇÖta decouple edilmi┼č client i┼člem par├žalar─▒n─▒ HTML i├žeren stringÔÇÖler olarak farkl─▒ uygulamalara embed edebilmekten bahsetti─čim yaz─▒da, o zamanlar yeni yeni serpilen IaC (infrastructure as code) arac─▒ olan ve g├╝n├╝m├╝zde serverlessÔÇÖ─▒n bir muadili olma niyetindeki dawson adl─▒ bir library kullanm─▒┼čt─▒m. B├Âylece SSR yan─▒nda bedava gelen First Contentful Paint ve / veya Largest Contentful Paint gibi parametrelerin s├╝relerini azaltmak istemi┼čtim.

AWS-rendered React chocolate chips with DawsonÔÇö Part 1: A simple service for serving components

Haber uygulamalar─▒ndaki anl─▒k widgetÔÇÖlar─▒ (dolar kuru, hava durumu, vb.) referans alarak biraz zaman harcam─▒┼čt─▒m. Akl─▒mdakini tam olarak uygulam─▒┼č olsam da, bu t├╝r bir metodun clientÔÇÖtaki stateÔÇÖin durumunu bozmadan, embed edilen uygulama i├žerisinde uyumlu ┼čekilde run edilebilmesi i┼či i├žin componentÔÇÖleri render ederken atanm─▒┼č eventÔÇÖleri varsa onlar─▒ korumak ve componentÔÇÖler aras─▒ ileti┼čim ├╝zerine kafa yoramam─▒┼čt─▒m.

Ge├žti─čimiz ay, React Server Components ├╝zerine ├žal─▒┼č─▒ld─▒─č─▒n─▒ farketti─čimde acaba ne yap─▒lmak isteniyor diye ├žok merak ettim ve kod ile g├╝n├╝m├╝z uygulamalar─▒na nas─▒l entegre edilebilece─čini biraz ara┼čt─▒rd─▒m. Tan─▒t─▒m videosu ve demo ile birka├ž blog postÔÇÖta yapt─▒─č─▒m gezinti bana baz─▒ konularda yeni ┼čeyler ├Â─črenmemin de kap─▒s─▒n─▒ aralam─▒┼č oldum ve bunu payla┼čmak istedim.

https://twitter.com/reactjs/status/1341072021099327489

reactjs/rfcs

Att─▒klar─▒ tweet ile duyurduklar─▒ blog postÔÇÖlar─▒nda ve RFC ├╝zerinden kabaca bir g├Âzatt─▒─č─▒mda React componentÔÇÖlerini bir nevi sunucudan servis etmek ├╝zerine kurulu bir stratejisi oldu─ču anla┼č─▒l─▒yordu. React kodunun sunucuda execute edilip i├žermek istenilen veri ile istemciye sunulmas─▒ demek. Temelde ise ├ž├Âzmek istenen sorun, request waterfall problemi. Bunu da birbiri i├žerisine ge├žmi┼č componentÔÇÖlerin bir ┼čekilde ba─č─▒ml─▒ olduklar─▒ veriyi waterfall s─▒ras─▒na client ├╝zerinde sokmadan, clientÔÇÖa sunucundan ilgili React componentÔÇÖin veri ile birlikte HTML ├ž─▒kt─▒s─▒ halinde alabilmeyi ama├žl─▒yor.

Asl─▒nda tek ├ž├Âzmek istedi─či bu de─čil. ─░├ži i├žine ge├žmi┼č React componentÔÇÖleri ayn─▒ zamanda ba┼čka destekleyici k├╝t├╝phanlerdeki i┼člevleri de i├žermeleri s├Âz konusu. Bu istemcide y├╝klenecek olan bundle boyutunun b├╝y├╝mesi demek.

Server Components run only on the server and have zero impact on bundle-size. Their code is never downloaded to clients, helping to reduce bundle sizes and improve startup time.

React ekibi (anla┼č─▒ld─▒─č─▒ kadar─▒yla) ReactiÔÇÖi ES ModulesÔÇÖe uyumlu hale getirmek yerine, ├ž─▒kt─▒ kodunu k├╝├ž├╝ltecek alternatif bir yol benimseyerek (date picker, date/time conversion ya da uluslararas─▒la┼čt─▒rma (i18n) veyahut lokalle┼čtirme (l10n) gibi i┼člevselliklerin ├ž├Âz├╝m├╝nde tercih edilebilece─čini d├╝┼č├╝nd├╝─č├╝m) ek k├╝t├╝phanelerin kod ├ž─▒kt─▒s─▒nda yarataca─č─▒ y├╝k├╝ azaltmay─▒ da d├╝┼č├╝n├╝yor. B├Âylece kod direk olarak sunucuda i┼členece─či ve transpile edilerek belli bir formatta (buna sonra de─činece─čiz) clientÔÇÖa geri g├Ânderilece─činden s─▒f─▒r-ba─č─▒ml─▒l─▒k gibi bir kazanc─▒ da sa─člamas─▒, kodun browserÔÇÖda execute edilmesini de h─▒zland─▒raca─č─▒ d├╝┼č├╝n├╝lm├╝┼č. Ek olarak, sunucuda (herhangi bir remote kayna─ča) yap─▒lacak bir requestÔÇÖin clientÔÇÖta yap─▒lacak bir requestÔÇÖten daha h─▒zl─▒ sonu├ž verece─či ├Ân kabul├╝nden hareketle istemcide kodun y├╝klenme s├╝ratinden de bir kazan├ž ortaya ├ž─▒kaca─č─▒n─▒ d├╝┼č├╝nebiliriz.

React Server Components ile kod organizasyonunda da birka├ž de─či┼čikli─če gidilmesi planlanm─▒┼č. Buna g├Âre, clientÔÇÖa serve edilecek componentÔÇÖlerin dosya isimleri ComponentName.client.js, sunucudaÔÇÖkilerin ise ComponentName.server.js oluyor. Bunlar─▒n yan─▒ s─▒ra, her iki k─▒s─▒mda da kullan─▒labilecek ortak/shared componentÔÇÖler tan─▒mlanabiliyor. PropÔÇÖlar ├╝zerinden aralar─▒ndaki ÔÇťtop-to-downÔÇŁ veri aktar─▒m─▒ ise serializable olan her veri tipi i├žin ge├žerli, aksi durum networkte maceras─▒na ba┼člayan requestÔÇÖin i┼členmesi i├žin m├╝mk├╝n olmayan bir durum, zira videodaki ├Ârnekte de (├Ârne─čin) bir fonksiyonun prop olarak kullan─▒lamayaca─č─▒ belirtilmi┼č. ├ľzetle, HTTP iste─činiz ile response olarak ilgili client componentÔÇÖinize property olarak paslayaca─č─▒n─▒z her ne ise bu (├Ârne─čin) bir javascript fonksiyonu olam─▒yor:

Bu ├Ârne─čin kar┼č─▒s─▒na ise, sunucudan geri g├Ânderilen response ├╝zerinde serialize edilebilen JSXÔÇÖi g├Ândermeyi koymu┼člar. Bu da JSX kodunun React Server ComponentÔÇÖinin clientÔÇÖa response g├Ânderilmeden ├Ânce render edilmesi ve ├Âyle g├Ânderilmesi anlam─▒na geliyor.

Bu noktada, React Server Components asl─▒nda JSXÔÇÖin sunucu-tarafl─▒ render edilmesi yerine, daha ba┼čka bir formatta, clientÔÇÖtaki component a─čac─▒n─▒ client stateÔÇÖini bozmadan clientÔÇÖa aktarmayi sa─člayan bir ara birime d├Ân├╝┼č├╝yor. Yani bir taraftan clientÔÇÖtaki componentÔÇÖleriniz sorunsuz ┼čekilde ├žal─▒┼čmaya devam ederken server componentÔÇÖinizden gelecek ├ž─▒kt─▒ ile uygulaman─▒zda browserÔÇÖ─▒n refresh edilmesi gibi bir sorun olmuyor ├ž├╝nk├╝ sadece sunucudan gelen sonu├ž (component ve data) ile uygulaman─▒n sadece k─▒smen g├╝ncellenmesi sa─član─▒yor ve geri kalan─▒ ayn─▒ kalabiliyor. Sunum videosunda RSC ile SSRÔÇÖ─▒n birbirinden ayr─▒ ama birlikte kullanabilecek uyumlu konseptler oldu─čundan da bahsediliyor. Buna ek olarak, sunucuda varolan bir dosyan─▒n i├žeri─činin yine sunucuda okunup server componentÔÇÖinin JSX a─čac─▒na embed edilerek render edilip clientÔÇÖa g├Ânderilmesi gibi bir use-case de anlat─▒lm─▒┼č.

Bu fikrin ilgin├ž ba┼čka bir yan─▒ ise React Server ComponentÔÇÖlerini Next.js ve Nuxt gibi hibrit geli┼čtirme ara├žlar─▒n─▒n temelde yapt─▒─č─▒ rehydrating i┼čleminin sadece React kullan─▒larak (bir nevi farkl─▒ bir format ve y├Ântem kullanarak) ortaya konabilen versiyonlar─▒ olmalar─▒. ├ľrne─čin, Next.js ile sunucudan al─▒nm─▒┼č bir verinin b├╝t├╝n sayfa yenilenmeden DOM a─čac─▒n─▒n yenilenerek ve React componentÔÇÖlerinin ayn─▒ ┼čekilde i┼člemeye devam etmesini sa─člayabiliyoruz. Buna yard─▒mc─▒ olarak NextÔÇÖin ├Ânemli ├Âl├ž├╝de (getInitialProps arac─▒l─▒─č─▒yla) clientÔÇÖta update etti─či ve HTMLÔÇÖe g├Âm├╝lm├╝┼č bir karakterize-JSONÔÇÖdan (stringified ceys─▒n ­čö¬) faydalan─▒l─▒yor. Bu da ayn─▒ anda, hem sunucunun ├╝zerinde render edilmi┼č sayfalar─▒n SEO ihtiya├žlar─▒n─▒n kar┼č─▒lanmas─▒ s├Âz konusuyken, bir taraftan da istemcideki uygulama ak─▒┼č─▒n─▒n da bozulmamas─▒ anlam─▒na geliyordu. Bunun yan─▒s─▒ra bu konuda Next.js ile React Server ComponentsÔÇÖ─▒n ayr─▒┼čt─▒─č─▒ noktalar─▒ ┼čuradan genel olarak ├Â─črenebildim, faydal─▒ oldu ve merak─▒m giderek artt─▒. Bir g├Âz atman─▒z─▒ tavsiye ederim.

B├╝t├╝n bu detaylar ─▒┼č─▒─č─▒nda merak─▒m iyice artt─▒ ve kodun i├žerisine girip daha iyi anlamak istedim. Uygulamalar─▒ analiz etmeye ├žal─▒┼čt─▒─č─▒mda ilk bakt─▒─č─▒m detay / nokta genelde sunucu ile nas─▒l ileti┼čime girdi─či ve burada nas─▒l bir yol izlendi─či oluyor. Bunun belli ba┼čl─▒ sebepleri var ve ├žok detaya girmeden ┼čunu diyebilirim ki, bir uygulaman─▒n veri al─▒┼čveri┼či i├žin sahip oldu─ču ileti┼čim modeli, o uygulaman─▒n ne i├žin ya da nas─▒l geli┼čtirildi─čini anlatt─▒─č─▒n─▒ d├╝┼č├╝n├╝yorum. Anl─▒k ya da de─čil, event bazl─▒ ya da de─čil ve daha bir├žo─ču. Burada da de─čindi─čin detay, bir chat ya da notification featureÔÇÖ─▒ i├žeren bir uygulamay─▒ tasarlaman─▒z ile statik export ile sadece dinamik routing nitelikleri kazand─▒rman─▒z─▒n yeterli oldu─ču uygulamalar aras─▒ndaki farklar gibi ve benzeri. ├ľzellikle g├╝n i├žerisinde i┼činize odaklanm─▒┼čken ├Ân├╝n├╝zdeki uygulamay─▒ anlamaya ├žal─▒┼č─▒yorsan─▒z, legacy uygulamalar─▒n en le┼č k─▒s─▒mlar─▒na bug fix yapman─▒z gerektiyse / gerekiyorsa ya da system design konular─▒na merak─▒n─▒z varsa, b├╝y├╝k bir ihtimalle sizin de kendinize has y├Ântemleriniz vard─▒r. Bu da sadece onlardan birisi.

O nedenle, ben de RSC demo uygulamas─▒n─▒ lokalde run etti─čimde gidip bakt─▒─č─▒m ilk yer buras─▒yd─▒. Uygulama ile girdi─čim ilk ileti┼čimde yapt─▒─č─▒m bir de─či┼čikli─čin akabinde sunucu k─▒sm─▒yla nas─▒l konu┼čtu─čuna bakmakla incelememe ba┼člam─▒┼čt─▒m. Buna g├Âre; uygulamada sol paneldeki her bir farkl─▒ itemÔÇÖ─▒n se├žilmesiyle ÔÇť/reactÔÇŁ endpointÔÇÖine bir request yap─▒l─▒yor. O anki state ise bir query stringÔÇÖe populate ediliyordu:

Bu endpointÔÇÖe gidip gelen her bir request/responseÔÇÖta da a┼ča─č─▒daki gibi bir custom header update oluyor.

X-Location: {ÔÇťselectedIdÔÇŁ:4,ÔÇŁisEditingÔÇŁ:false,ÔÇŁsearchTextÔÇŁ:ÔÇŁaÔÇŁ}

APIÔÇÖyi run eden bir express sunucusu ise HTTP endpoint callbackÔÇÖi ├╝zerinden bunu yakal─▒yor ve requestÔÇÖin i├žeri─čini sendResponse metoduna delege ediyor:

https://github.com/reactjs/server-components-demo/blob/4ecf1f2641ecc822fe8f933ac3cd23f4ed4629d7/server/api.server.js#L92

Dikkat ederseniz, sendResponse metodunun 3. arg├╝man─▒ null. Normal ┼čartlarda bu arg├╝man ÔÇťselectedIdÔÇŁ olarak set edilmi┼č bi├žimde sadece tek bir POST requestÔÇÖinde (/notes) g├Ânderiliyor. Sebebi ise sunucudan React treeÔÇÖsi render edilip geri clientÔÇÖa (id ve chunk urlÔÇÖlerinden olu┼čan bir JSON mapÔÇÖi olarak) d├Ând├╝r├╝l├╝rken varolan stateÔÇÖi orada da korumak. Bu response al─▒n─▒nca client taraf─▒nda bu ÔÇťselectedIdÔÇŁ okunuyor. Detaylar geliyorÔÇŽ

B├╝t├╝n bunlara ek olarak, uygulamay─▒ browserÔÇÖda execute edecek k─▒s─▒mlar─▒ bulmaya ├žal─▒┼čt─▒─č─▒mda package.jsonÔÇÖdan uygulamay─▒ bundleÔÇÖa export eden sat─▒rlar─▒n build.jsÔÇÖi run etti─či yeri buldum. O da entry olarak index.client.jsÔÇÖi i┼čaret ediyor. Ard─▒ndan dosya i├žerisine import edilen Root.client.jsÔÇÖteki Root componentÔÇÖini render ediyor. O da ayn─▒ dosyadaki ve LocationContext.Provider ile wrap edilmi┼č ve RootÔÇÖun i├žeri─čini update eden Content componentÔÇÖini kullan─▒yor. Uygulaman─▒n tamam─▒ bu arkada┼č─▒n sa─člad─▒─č─▒ i├žerikten render oluyor diyebiliriz.

https://github.com/reactjs/server-components-demo/blob/3a505efea0b1191496a832e23f3de46a0db69915/src/Root.client.js#L25

┼×imdi ana ak─▒┼č─▒ bozmadan devam edelim ve sunucu taraf─▒nda ÔÇť/reactÔÇŁ endpointÔÇÖinde execute edilen sendResponse fonksiyonunu inceleyelim. Bu fonksiyon asl─▒nda bir decorator gibi ├žal─▒┼č─▒yor ve o anda al─▒nan requestÔÇÖin durumuna g├Âre responseÔÇÖun i├žeri─čini ÔÇťdekore ediyorÔÇŁ / belirliyor. Yukar─▒da bahsetti─čimiz o anki client stateÔÇÖini location query stringÔÇÖinden al─▒yor ve responseÔÇÖun headerÔÇÖ─▒ndaki X-Location olarak set ediyor. E─čer 3. arg├╝man null de─čilse selectedIdÔÇÖye set ediyor ve son a┼čama olarak renderReactTree metodunu ├ža─č─▒r─▒yor.

https://github.com/reactjs/server-components-demo/blob/4ecf1f2641ecc822fe8f933ac3cd23f4ed4629d7/server/api.server.js#L79

renderReactTree metodu, asenkron waitForWebpack fonksiyonunun sonlanmas─▒n─▒ bekleyip en ba┼čta run edilen build commandÔÇÖinin export etti─či bir manifest (./build/react-client-manifest.json) dosyas─▒ndaki i├žeri─či bir de─či┼čkene (moduleMap) set ediyor:

https://github.com/reactjs/server-components-demo/blob/4ecf1f2641ecc822fe8f933ac3cd23f4ed4629d7/server/api.server.js#L71

moduleMap de─či┼čkeninin i├žeri─či ise sadece client componentÔÇÖlerinin meta bilgisini i├žeren bir Map setÔÇÖi:

Her bir map k─▒r─▒l─▒m─▒n─▒n bir client componentÔÇÖine denk geldi─či g├Âzlerden ka├žmad─▒!

ResponseÔÇÖun clientÔÇÖa g├Ânderilmeden ├Ânceki a┼čama ise App.server.jsÔÇÖte olu┼čturulmu┼č b├╝t├╝n bir React treeÔÇÖsinin eski/yeni veya update edilmi┼č / edilmemi┼č propÔÇÖlar ile manifestÔÇÖe (moduleMap) g├Âre yeniden olu┼čturulmas─▒ a┼čamas─▒.

https://github.com/facebook/react/blob/master/packages/react-server-dom-webpack/package.json#L28

pipeToNodeWritable metodu react-dom-server-webpack packageÔÇÖ─▒n─▒n i├žerisindeki writer aliasÔÇÖ─▒ndan geliyor. Bu da sunucuda run olan ReactFlightDOMServerNode.js dosyas─▒na refere ediyor. Kaynak koddan g├Âr├╝lece─či gibi, pipeToNodeWritable metodu 3 arg├╝man alacak ve herhangi bir d├Ân├╝┼č de─čeri bulunmayacak ┼čekilde organize edilmi┼č:

https://github.com/facebook/react/blob/master/packages/react-server-dom-webpack/src/ReactFlightServerWebpackBundlerConfig.js

G├Âr├╝ld├╝─č├╝ ├╝zere, manifest dosyas─▒n─▒n i├žeri─či ├Âzelde webpackÔÇÖin bundle edece─či kodu da update edecek bir referans noktas─▒.

pipeToNodeWritableÔÇÖa g├Ânderilen ReactModel, Writable ve BundlerConfig tiplerindeki model, destination ve webpackMap arg├╝manlar─▒yla bu sefer createRequest (react-server/src/ReactFlightServer.js) ├ža─č─▒r─▒l─▒yor. Bu metod ise kendi i├žerisinde bu de─čerleri kullanarak createSegment isimli bir fonksiyonu ├ža─č─▒r─▒yor ve geriye ┼ču a┼ča─č─▒daki objeyi d├Ând├╝r├╝yor:

https://github.com/facebook/react/blob/b99ac3d6dffbe57f94d368cc4f4e0ddf089e4f53/packages/react-server/src/ReactFlightServer.js#L183

Manifest dosyas─▒ i├žerisinde yer alan objenin her bir eleman─▒ i├žin, request a┼ča─č─▒daki gibi bir request de─či┼čkenine assign edilerek olu┼čturuluyor ve pipeToNodeWritable metoduna geri d├Ând├╝r├╝l├╝yor:

https://github.com/facebook/react/blob/b99ac3d6dffbe57f94d368cc4f4e0ddf089e4f53/packages/react-server/src/ReactFlightServer.js#L74

Tam da bu noktada ilgin├ž bir hamle geliyor ve pingedSegments dizisine ┼ču ┼čekilde push ediliyor:

https://github.com/facebook/react/blob/b99ac3d6dffbe57f94d368cc4f4e0ddf089e4f53/packages/react-server/src/ReactFlightServer.js#L117

Yani asl─▒nda request.pingedSegments burada Segment tipinde objelerin store edildi─či bir dizi:

https://github.com/facebook/react/blob/b99ac3d6dffbe57f94d368cc4f4e0ddf089e4f53/packages/react-server/src/ReactFlightServer.js#L68

Her bir createSegment fonksiyon callÔÇÖu ise pingSegment ad─▒nda ba┼čka bir metod ├ža─č─▒r─▒yor. Bu metod ise o anda hen├╝z i├ži doldurulmaya ba┼članm─▒┼č request objesinin pingedSegments dizisine her yeni gelen segmentÔÇÖleri push ediyor. Segmentlerin tam olarak ne olduklar─▒na az sonra bakaca─č─▒z:

https://github.com/facebook/react/blob/b99ac3d6dffbe57f94d368cc4f4e0ddf089e4f53/packages/react-server/src/ReactFlightServer.js#L175

Buradaki performWork fonksiyonuna bir bakmak faydal─▒ olabilir. ─░├žeri─čine ├žok girmeden, bu fonksiyon temelde pingedSegment metodunun populate etti─či pingedSegments arrayÔÇÖinin i├žeri─činin en primitif noktas─▒na denk geliyor. Yani asl─▒nda segmentÔÇÖler, React componentÔÇÖlerinin ├Âzel bir notasyonda olu┼čturulmu┼č halleri. A┼ča─č─▒daki kod blo─čundan da g├Âr├╝lece─či ├╝zere, ├Ânce retrySegment kendisine arg├╝man olarak g├Ânderilmi┼č segmentin ger├žekten bir React component olup olmad─▒─č─▒n─▒ kontrol ediyor.

https://github.com/facebook/react/blob/b99ac3d6dffbe57f94d368cc4f4e0ddf089e4f53/packages/react-server/src/ReactFlightServer.js#L392

ve e─čer ├Âyleyse bu i├žeri─či processModelChunkÔÇÖtan d├Ânen sonu├ž ile completedJSONChunks i├žerisine pushÔÇÖluyor:

https://github.com/facebook/react/blob/b99ac3d6dffbe57f94d368cc4f4e0ddf089e4f53/packages/react-server/src/ReactFlightServer.js#L650

B├╝t├╝n segmentÔÇÖler pushÔÇÖland─▒─č─▒nda e─čer request.flowing de─čeri truthy ise request flushCompletedChunks ile destination olarak isimlendirilmi┼č response arg├╝man─▒n─▒n i├žeri─či streamÔÇÖde g├Ânderildiklerinde ilk olarak call edilsinler diye writeChunk metodu ile i┼členiyor, ard─▒ndan completedJSONChunks i├žeri─či i┼čleme al─▒n─▒yor ve pendingChunksÔÇÖ─▒n i├žeri─či bo┼čalt─▒l─▒yor, son olarak da completeWriting metodu ├ža─čr─▒larak clientÔÇÖta geri d├Ând├╝r├╝lecek olan responseÔÇÖun b├╝t├╝n bufferÔÇÖlanm─▒┼č dataÔÇÖs─▒ temizleniyor ve son haline kavu┼čuyor.

B├╝t├╝n bu eldeki veri, Transfer-Encoding response headerÔÇÖ─▒ chunked olarak set edilip en ba┼čta anlatt─▒─č─▒m─▒z ÔÇť/reactÔÇŁ endpointinden ├ža─č─▒r─▒lan sendResponse, ard─▒ndan call edilen renderReactTree ve en sonunda b├╝t├╝n bir React treeÔÇÖsini webpack notasyonunda tekrar streamÔÇÖe geri d├Ând├╝r├╝lmek ├╝zere execute edilen pipeToNodeWritable ├ža─č─▒r─▒l─▒yor ve client k─▒s─▒mda yap─▒lan ve sunucuyu pingÔÇÖleyen herhangi bir anda tekrardan build edilip yine bir stream responseÔÇÖu olarak clientÔÇÖa geri d├Ând├╝r├╝l├╝yor. ChunkedÔÇÖlanm─▒┼č sunucu cevab─▒m─▒z ise ┼ču ┼čekilde oluyor:

Tam da bu noktada de─činmek istedi─čim ba┼čka bir konu daha var, o da b├╝t├╝n bu mekanizman─▒n ├╝zerine kuruldu─ču HTTP 1.1 ile uygunlamaya sunulan chunk tabanl─▒ veri al─▒┼čveri┼čini network seviyesine ta┼č─▒yan streamÔÇÖler. Bu konuyu direk olarak Node.js Streams ├╝zerinden anlatmay─▒ tercih ettim, en nihayetinde ÔÇťkodÔÇŁ dilinde konu┼čuyoruz. Streams ├Âzellikle dosya okuma/yazma, network ├╝zerinden ileti┼čim gibi i┼člemlerin memory ve performans konusunda yaratt─▒klar─▒ sorunlardan muzdarip olanlar─▒n tercih ettikleri bir Node.jsÔÇÖin featureÔÇÖ─▒. Ger├žek hayattan bir ├Ârnek vermek gerekirse, okunup i├žeri─či ├╝zerinde bir transform i┼člemi ger├žekle┼čtirilecek olan bir dosyan─▒n stream ile a├ž─▒l─▒p okunmas─▒ daha efektif ├ž├╝nk├╝ Streams bu dataÔÇÖy─▒ chunkÔÇÖlar halinde i┼čliyor ve her bir i┼člem sonras─▒nda haf─▒zadan bir ├Ânceki versiyonu/i├žeri─či siliyor. Bunu HTTP protokol├╝ne uygulad─▒─č─▒m─▒zda ├Ârnek verebilece─čim di─čer bir durum da video streaming.

Mesela, e─čer Youtube ├╝zerinden izledi─činiz bir videonun tamam─▒n─▒n y├╝klenerek g├Âsterildi─čini d├╝┼č├╝n├╝yorsan─▒z yan─▒l─▒yorsunuz ├ž├╝nk├╝ bir videonun sayfas─▒na girdi─činizde ilk elden g├Âsterilmeye ba┼članmas─▒n─▒n sebebi, YoutubeÔÇÖun sunucular─▒ndan video verisinin k├╝├ž├╝k par├žalar halinde clientÔÇÖa servis edilmesi.

youtube.com -> right click on video -> Stats for nerds -> Network Activity

Konuyu biraz daha ├Âzele inerek a├žal─▒m. Gulp ÔÇś─▒ hemen hemen herkes hat─▒rl─▒yordur, hatta hala kullan─▒labilir bir tool oldu─čunu d├╝┼č├╝n├╝yorum. Gulp da bir build toolÔÇÖu oldu─čundan asl─▒nda arka planda streamÔÇÖlerle ├žal─▒┼č─▒yor. Hani ┼ču s─▒ras─▒yla minify edilen bundleÔÇÖlar─▒n─▒z var ya, i┼čte onlar─▒n hepsi Gulp ├╝zerinde sanal olarak olu┼čturulmu┼č object streamÔÇÖlere yaz─▒l─▒yor ve yeri geldi─činde kullan─▒l─▒p haf─▒zadan siliniyor. Boyutu b├╝y├╝k olan kod dosyalar─▒n─▒z ├╝zerinde bu i┼člemler uygulan─▒rken tipki unix pipe (|) operat├Âr├╝ gibi bir readable streamÔÇÖden sonraki bir writable streamÔÇÖe ge├žit a├žabiliyoruz.

StreamÔÇÖler yine Node.jsÔÇÖin EventEmitter classÔÇÖ─▒ndan t├╝retilmi┼č s─▒n─▒flar ve 4 ├že┼čitten olu┼čuyor. Verinin yaz─▒labildi─či Writable, okunabildi─či Readable, hem yaz─▒l─▒p hem okunabilen Duplex ve Duplex streamÔÇÖleri i├žerisine hem yaz─▒l─▒p hem okubilen verinin ayn─▒ zamanda editlenebildi─či Transform. ├ľrne─čin, Node.jsÔÇÖteki request objesi bir okunabilir stream. Ayn─▒ zamanda bir stream olan response objesinin kullan─▒m─▒ndan da g├Âr├╝lece─či ├╝zere, sanki eventÔÇÖler i├žerisinde i┼člem yap─▒yormu┼čsunuz gibi bir his vermesinin sebebi de tam da bu, yani kendisinin EventEmitterÔÇÖdan t├╝retilmi┼č bir Stream olmas─▒.

├ľzellikle asenkron data aktar─▒m─▒ konusunda yard─▒mc─▒ bir Node.js featureÔÇÖ─▒ olan StreamÔÇÖler, b├Âylece bir sonraki i┼čleme ge├žmeden ├Ânce mevcut i┼člemin ger├žekle┼čmesi s─▒ras─▒nda durdurulmas─▒ m├╝mk├╝n oluyor. React Server Components konsepti StreamÔÇÖlerin ├╝zerine kurulu oldu─ču ileti┼čim modelinden faydalan─▒yor. Chunk-tabanl─▒ bir istemci-sunucu networking ileti┼čim niteli─či olarak stream ile her bir de─či┼čen DOM statusÔÇÖ├╝n├╝ sunucu taraf─▒na g├Ânderip, onu sunucuda anlaml─▒ bir React tree halinde tekrar olu┼čturup, (├Ârne─čin) veritaban─▒nda yap─▒lan bir de─či┼čiklik ile varolan component ve state yap─▒s─▒n─▒ bozmadan bir responseÔÇÖta geri d├Ânderiyor.

Kald─▒─č─▒m─▒z yerden devam edecek olursak, clientÔÇÖa da bir g├Âzatmam─▒z gerekecek. Backend taraf─▒nda d├Ânen b├╝t├╝n bu hadiselerin gelip netle┼čti─či yer client entegrasyonu. /react endpointÔÇÖinin call edildi─či yer Cache.client.js dosyas─▒. location keyÔÇÖi olmadan request yap─▒l─▒rsa uygulaman─▒n React treeÔÇÖsini tekrardan update edilecek bir neden yok. Ancak bir location idÔÇÖsi varsa, response react-server-dom-webpack packageÔÇÖi ile i┼členip yeni bir bundle ├╝retiliyor, React treeÔÇÖsi update ediliyor.

https://github.com/facebook/react/blob/5fd9db732dff1b99c096bffef9a565b594c788de/packages/react-server-dom-webpack/src/ReactFlightDOMClient.js#L46

ResponseÔÇÖun do─čru bir ┼čekilde okunup uygulaman─▒n update edilmesi i├žin kullan─▒lan readRoot fonksiyonunu sa─člayan ise react-server-dom-webpack pluginin createFromFetch fonksiyonu. O da startReadingFromStream isimli ba┼čka bir fonksiyonu ├ža─č─▒r─▒yor. Bu fonksiyonun yapt─▒─č─▒ ise basit├že bir stream reader olu┼čturmak ve responseÔÇÖun i├žeri─čini okumak ve ├ža─čr─▒ld─▒─č─▒ yere geri d├Ând├╝rmek:

B├╝t├╝n bu d├Âng├╝, gerisin geriye uygulamay─▒ besliyor ve client treeÔÇÖsi bozulmadan sunucu taraf─▒ndaki g├╝ncellemeler istemciye sa─članm─▒┼č oluyor. Kodun kendisi de─čil, sadece ihtiya├ž olunan meta verinin pure kod olarak de─čil de kendisine ├Âzg├╝ bir formatta, bu gibi bir k├╝t├╝phane niteli─či (feature) i├žin HTMLÔÇÖden daha zengin bir i├žerik sa─članm─▒┼č oluyor.

Daha bir├žok ├Ârnek verilebilir ve bu gibi bir featureÔÇÖ─▒n nas─▒l geli┼čtirildi─čine deep-dive yukar─▒daki linklerden faydalanarak detayl─▒ca girebilirsiniz. Ne de olsa kod geli┼čtirmenin yazmaktan ├žok kod okumaktan ibaret olmas─▒ gibi bir durum var. ­čĄŽ­čĆ╗ÔÇŹÔÖé´ŞĆ

├ľzetle, React ekibi stream tabanl─▒ bir web ileti┼čim ÔÇťmodeliÔÇŁ kullanarak sunucu tarafl─▒ g├╝ncellemeleri clientÔÇÖa ula┼čt─▒r─▒p b├╝t├╝n uygulaman─▒n g├╝ncellenmesini ger├žekle┼čtirmek istemi┼č. G├╝n├╝m├╝z server-side renderingÔÇÖin sa─člad─▒─č─▒ imkanlar─▒n streamÔÇÖler ile ÔÇťa┼č─▒larakÔÇŁ sadece ReactÔÇÖe ├Âzg├╝ bir ├ž├Âz├╝m geli┼čtirilmek istenmi┼č. Hen├╝z deneysel bir ├žal─▒┼čma elbette, gelecek g├╝ncellemelerle daha robust bir yap─▒ya kavu┼čturulursa ne gibi imkanlar yarat─▒r diye d├╝┼č├╝nd├╝─č├╝mde ┼čunlar akl─▒ma geliyor:

  • Front-end engineering alan─▒ sunuculara olan ilgisi ile performans ve SEO ihtiya├žlar─▒ kar┼č─▒lan─▒rken Back-end engineering alan─▒ daha ├žok GraphQL gibi ara├žlarla client k─▒sm─▒na daha ├žok kafa yormalar─▒n─▒n ├Ân├╝ a├ž─▒ld─▒. Bu da akl─▒ma geli┼čtiricilerin bir s├╝redir sekt├Ârdeki geli┼čmeler ve ara├žlarla daha ├žok ÔÇśmiddle stackÔÇÖ gibi bir yerde toparlan─▒yor olmalar─▒ detay─▒n─▒ getiriyor. Daha computation / algoritma / talep yo─čun yaz─▒l─▒m geli┼čtirmenin makine ├Â─črenmesi gibi alanlara kayd─▒─č─▒ g├Âr├╝┼č├╝ndeyim. React Server ComponentsÔÇÖ─▒n da bu ÔÇśmiddle stackÔÇÖ alan─▒nda dolduraca─č─▒ yer yine sunucu ile istemciyi bir noktada bulu┼čturmak ve sunucu-bazl─▒ etkile┼čimi yo─čun uygulamalar olacak gibi duruyor. Bug├╝n websocketÔÇÖlerin kullan─▒ld─▒─č─▒ uygulamalar─▒n artmas─▒ buna dair k├╝├ž├╝k bir detay.
  • Front-end geli┼čtirme ├žoktan full-stack bir d├╝zeleme ├žoktan ta┼č─▒nm─▒┼čt─▒. Bunu clientÔÇÖtan direk olarak ba┼čka kaynaklara direk ba─člant─▒ kuran ve b├Âylece daha h─▒zl─▒ uygulama geli┼čtirmeyi sa─člayan bir├žok ara├ž ile deneyimlemi┼čtik / deneyimliyoruz. Bu nedenle bu ivme, React Server ComponentÔÇÖleri ile daha da artacak.
  • JAMStack entegrasyonu ile headless CMSÔÇÖlerden feed edilen verilerin statik uygulamalarda interaktivite oran─▒n─▒ y├╝kseltir.
  • ├çok fazla API ileti┼čimi olan uygulama ortamlar─▒nda clientÔÇÖin stabil kalabilmesiyle statik sayfalar ├╝zerinden kurgulanm─▒┼č uygulamalarda dinamizm katabilir.
  • Server componentÔÇÖleri ├╝zerinden uygulama ortamlar─▒nndan decouple edilmi┼č widgetÔÇÖlar mobil uygulamalara embed edilebilir. Performans ve zero-bundle size bu noktadan da bir kazan├ž sa─člayabilir.
  • Read / Update i┼člemlerinin ├žok├ža client ÔÇö sunucu aras─▒nda ileti┼čimi gerektiren uygulamalarda tek bir component ile en g├╝ncel verinin her iki layerÔÇÖda da g├╝ncel kalmas─▒ sa─članabilir. ├ľrnek, ├╝r├╝nlerin / ┼čirketlerin / topluluklar─▒n kat─▒l─▒mc─▒ d├Âk├╝mantasyon uygulamalar─▒, e-ticarette ├╝r├╝n detay sayfalar─▒n─▒n yorum ve oylama sayfalar─▒, vb. gibi kullan─▒m alanlar─▒ akla gelebilir.
  • Lazy-loaded componentÔÇÖler sunucu taraf─▒nda code-splitting sa─člayabilir.
  • React frameworkÔÇÖleri ya da geli┼čtirme ara├žlar─▒ i├žin yeni bir geli┼čme alan─▒ ├ž─▒kar─▒r. Mesela ┼ču andan Next.jsÔÇÖe bir entegrasyonun gelece─čini tahmin edebiliriz. Zaten hackernews postÔÇÖunda de meta-frameworkÔÇÖlerin zaten ba┼čard─▒klar─▒ i┼či daha da iyile┼čtirmek oldu─ču belirtiliyor.
  • React componentÔÇÖlerinin onceden uygulamalarda payla┼čt─▒─č─▒ roller (datepicker, media, vs.) ├╝zerinden tan─▒ml─▒yorduk. ┼×imdi bir de componentÔÇÖlerin server/client versiyonlar─▒ geli┼čtirilmeye ba┼članabilir. ÔÇśreact-server-neguzelolduÔÇÖ ya da ÔÇśreact-client-css-animationÔÇÖ gibi standalone componentÔÇÖlerin npmÔÇÖe publish edildi─čini g├Ârebiliriz. Burada belirtilmi┼č mesela. Benim favorilerim bunlar bu arada ­čśé

B├╝t├╝n bunlar bir yana, bu gibi bir featureÔÇÖ─▒ React gibi kullan─▒m alan─▒ olduk├ža artm─▒┼č bir k├╝t├╝phaneye entegre etmek zaman alacakt─▒r. Umar─▒m geli┼čmelerin ne olaca─č─▒ ilerleyen aylarda belli olur, bu alandaki geli┼čimler di─čer k├╝t├╝phane ya da frameworkÔÇÖlere de zaten ├žoktan ilgisini yeterince ├žekmi┼č daha ÔÇťmiddle stackÔÇŁ ├ž├Âz├╝mlere yol a├žar.

Zaman ay─▒r─▒p okudu─čunuz i├žin te┼čekk├╝rler. Geri bildirimlerinizi sab─▒rs─▒zl─▒kla bekliyorum. ­čĹő