- Mux ย้าย mux.com และ docs.mux.com ไปใช้ React Server Components และพบว่าการแบ่งขอบเขตการรันระหว่างเซิร์ฟเวอร์กับไคลเอนต์ส่งผลโดยตรงต่อขนาด bundle และต้นทุน hydration
- RSC ช่วยให้ component ดึงข้อมูลโดยตรงจากเซิร์ฟเวอร์และ streaming ผลลัพธ์ได้ ทำให้สามารถแสดงบางส่วนของหน้าจอก่อนได้ แม้จะมีการเรียกข้อมูลที่ช้า
- อุปสรรคใหญ่ที่สุดในการ migration จริงคือ การไม่รองรับ CSS-in-JS, ข้อจำกัดของ React Context ใน Server Components และความซับซ้อนที่ต้องคอยติดตามขอบเขต server/client ตลอดเวลา
- ใน app directory ของ Next.js 13 ค่าเริ่มต้นคือ Server Component และสามารถ นำมาใช้แบบค่อยเป็นค่อยไป ได้ด้วยการค่อยๆ ย้าย
use clientจาก root ลงไปยังระดับล่าง - ควรใช้แพตเทิร์นอย่าง Suspense,
loading.js, การคงไลบรารีไว้เฉพาะฝั่งเซิร์ฟเวอร์ และserver-onlyอย่างระมัดระวังเฉพาะจุดที่ต้องการประโยชน์ด้าน performance และต้องคำนวณ ต้นทุนทางความเข้าใจ ของทีมไปพร้อมกัน
ขอบเขตที่ Mux ย้ายไปใช้ RSC
- Mux ย้าย mux.com และ docs.mux.com ไปใช้ Server Components ระหว่างการปรับโครงสร้างเว็บไซต์เอกสารและการรีแบรนด์
- React Server Components สามารถนำไปใช้ได้กับ codebase จริงและอาจคุ้มค่า แต่ก็มาพร้อมข้อจำกัดและความซับซ้อน
- ประสบการณ์นี้สรุปโดยเน้นว่าเหตุใดจึงต้องมี RSC, เหมาะกับงานแบบใด, ยากในสถานการณ์ไหน และจะค่อยๆ นำเข้าสู่ codebase จริงได้อย่างไร
ปัญหาที่ RSC มุ่งแก้หลังยุค CSR, SSR/SSG
- วิธี server rendering ในยุคแรกๆ ใช้เทคโนโลยีอย่าง PHP ดึงข้อมูลและประมวลผลงาน CPU หนักๆ บนเซิร์ฟเวอร์ ก่อนส่ง HTML ที่เบาไปยังไคลเอนต์
- CSR/SPA ส่งโค้ด rendering เป็น JavaScript ไปยังไคลเอนต์ ทำให้จัดการ interaction ได้รวดเร็ว แต่จุดอ่อนจะปรากฏในสถานการณ์ที่ search engine ไม่รัน JavaScript, ต้องเก็บ secret ไว้บนเซิร์ฟเวอร์ หรือเจออุปกรณ์ประสิทธิภาพต่ำและการเชื่อมต่อช้า
- SSR/SSG เป็นวิธีที่ใช้เครื่องมืออย่าง Next.js และ Gatsby สร้าง HTML พร้อม JavaScript บนเซิร์ฟเวอร์แล้วส่งไปยังไคลเอนต์
- ผู้ใช้เห็น HTML ได้ทันที
- เมื่อ JavaScript โหลดแล้ว เว็บไซต์จะ interactive ได้
- search engine ก็อ่าน HTML ได้
- SSR/SSG แบบเดิมยังมีต้นทุนอยู่
- ต้องส่ง JavaScript ส่วนใหญ่ที่ใช้สร้างหน้าไปยังไคลเอนต์ และต้องให้ไคลเอนต์รันซ้ำเพื่อผสานกับ HTML หรือที่เรียกว่า hydration
- หาก server rendering ใช้เวลานานเพราะเรียกฐานข้อมูลช้าหรือรันโค้ดจำนวนมาก ผู้ใช้ก็ต้องรอ
จุดที่ React Server Components เปลี่ยนแปลง
- React Server Components คือ React component ที่รันบนเซิร์ฟเวอร์ ไม่ใช่บนไคลเอนต์
- framework ที่รองรับ RSC ช่วยให้ระบุแยกได้อย่างชัดเจนว่าโค้ดจะรันที่ไหน
- Server Components: โค้ดที่ควรรันเฉพาะบนเซิร์ฟเวอร์
- Client Components: โค้ดที่ควรรันบนไคลเอนต์
- เมื่อแยกตำแหน่งการรันแล้ว JavaScript ที่ส่งไปยังไคลเอนต์จะลดลง และงานที่ต้องทำระหว่าง hydration ก็ลดลงด้วย
- Server Component สามารถดึงข้อมูลโดยตรงภายใน component ได้
- สามารถใช้ Node library หรือ
fetchได้ - ลดการดึงข้อมูลรวบทีเดียวที่ระดับหน้าเว็บด้วย
getServerSidePropsแล้วส่งต่อผ่าน props ลงไปเรื่อยๆ - ลดกรณีที่ต้องจัดการ loading state ซับซ้อนด้วย
useEffect
- สามารถใช้ Node library หรือ
- เมื่อ Server Component ดึงข้อมูลเสร็จแล้ว สามารถ streaming ผลลัพธ์ไปยังไคลเอนต์ได้
- สามารถแสดงส่วนอื่นของเว็บไซต์ก่อนได้ระหว่างรอ component ที่ช้า
- การตอบสนองต่อการกระทำของผู้ใช้ฝั่งไคลเอนต์ด้วยการดึงข้อมูลบนเซิร์ฟเวอร์และ streaming คำตอบก็เป็นไปได้ แต่โดยเคร่งครัดแล้วสิ่งนี้ไม่ใช่ RSC แต่เป็น React Actions
ส่วนที่ RSC ใช้งานยาก
- CSS-in-JS ยังไม่ทำงานใน Server Components ในปัจจุบัน
- ในการย้ายไปใช้ RSC ของ Mux งานที่กินสัดส่วนมากที่สุดคือการย้ายจาก styled-components ไปเป็น Tailwind CSS
- หาก codebase พึ่งพา CSS-in-JS อย่างมาก จะต้องมีงาน migration แยกต่างหาก
- React Context เข้าถึงได้เฉพาะใน Client Components
- หากต้องแชร์ข้อมูลระหว่าง Server Components โดยไม่ใช้ props มีแนวโน้มสูงว่าจะต้องใช้ module ปกติ
- Server Components ไม่มีกลไกที่ดีในการจำกัดข้อมูลไว้เฉพาะ subtree บางส่วนของ React application
- ในเว็บไซต์เอกสารของ Mux พื้นที่ที่ใช้ Context มี interaction มากและอย่างไรก็ต้องส่งไปยังไคลเอนต์อยู่แล้ว จึงไม่ใช่ปัญหาใหญ่
- ในเว็บไซต์การตลาด การแชร์ theme กลายเป็นปัญหา
- component แต่ละตัวใน pre-footer ต้องรู้ว่าตัวเองอยู่บนพื้นหลังสีเขียว จึงจะใช้ขอบสีเขียวเข้มได้
- แก้ทางอ้อมด้วยการใช้ CSS custom properties อย่างจริงจังแทน Context
- RSC ให้ความยืดหยุ่นด้านตำแหน่งการรันและวิธีดึงข้อมูล แต่ก็เพิ่ม ความซับซ้อน ตามมา
- นักพัฒนาใหม่ต้องคอยตรวจสอบว่า “อะไรทำงานบนเซิร์ฟเวอร์ และอะไรทำงานบนไคลเอนต์” อยู่เสมอ
- ในแต่ละ PR จะมี feedback เกี่ยวกับโค้ดที่ถูกส่งไปยังไคลเอนต์โดยไม่จำเป็น
- ระหว่างพัฒนา มักต้องใส่
console.logเพื่อดูว่า log ออกมาจากเซิร์ฟเวอร์หรือเบราว์เซอร์ - caching ก็เพิ่มความซับซ้อนอีกชั้นหนึ่ง
วิธีพื้นฐานในการใช้ RSC ใน Next.js 13
- ณ เวลาที่เขียน implementation ของ RSC ที่พร้อมใช้ใน production คือ app directory ของ Next.js 13
- ใน app directory ของ Next.js 13 component ที่เขียนโดยค่าเริ่มต้นจะเป็น Server Component
- ในสถานะเริ่มต้น โค้ดของหน้าจะไม่ถูกส่งไปยังไคลเอนต์
- ไคลเอนต์จะได้รับเฉพาะ HTML
- หากใส่
asyncให้ Server Component ก็สามารถดึงข้อมูลภายใน component ได้ - Server Component ที่มีการดึงข้อมูลช้าสามารถห่อด้วย
React.Suspenseได้- ไคลเอนต์จะแสดง fallback UI ก่อน
- เมื่อเซิร์ฟเวอร์ดึงข้อมูลและ render เสร็จแล้ว component ผลลัพธ์จะถูก streaming มา
- Suspense boundary ไม่ได้ใช้เฉพาะ data streaming เท่านั้น แต่ยังอาจใช้กับ selective hydration เพื่อปรับลำดับความสำคัญของ hydration ในบางพื้นที่ตาม interaction ของผู้ใช้ได้ด้วย
- โค้ดที่ต้องรันบนไคลเอนต์ให้เพิ่ม
"use client"ไว้ด้านบนของไฟล์- ใช้กับ component ที่ต้องมี client state และ interaction เช่น
onClicklistener หรือuseState - component ทั้งหมดที่ถูก import โดย component ที่มี
"use client"ก็จะถูกส่งไปยังไคลเอนต์ด้วย
- ใช้กับ component ที่ต้องมี client state และ interaction เช่น
- ไลบรารีที่ไม่รองรับ RSC สามารถ import ใน Client Component เพื่อรวมไว้ใน client bundle ได้
- ตัวอย่างคือ component
ClientMuxPlayerที่ห่อ@mux/mux-player-react
- ตัวอย่างคือ component
เกณฑ์เลือก Server Component กับ Client Component
- Server Components เหมาะกับโค้ดที่ไม่จำเป็นต้องส่งไปยังไคลเอนต์
- การ render เนื้อหาบทความบล็อก
- งานที่มีต้นทุนสูง เช่น syntax highlighting ของ code block
- การดึงข้อมูล
- Client Components เหมาะกับ UI ที่ตอบสนองต่อ input ของผู้ใช้หรือมี state เปลี่ยนไปตามเวลา
useState- event listener
- interaction ฝั่งไคลเอนต์
- หากทำทั้งแอปเป็น Client Components ทั้งหมด ก็จะทำงานคล้าย framework SSR แบบเดิม
- ไม่จำเป็นต้องเปลี่ยนทั้งแอปเป็น Server Components ในครั้งเดียว สามารถค่อยๆ นำมาใช้จากจุดที่ได้ประโยชน์มากก่อน
3 ขั้นตอนในการค่อยๆ นำเข้าสู่ codebase จริง
- playbook ที่ Mux ใช้มีสามขั้นตอน
- เพิ่ม directive
"use client"ที่ root ของแอป - ย้าย directive ลงไปใน render tree ให้ต่ำที่สุดเท่าที่ทำได้
- ใช้แพตเทิร์นขั้นสูงเมื่อพบปัญหา performance
- เพิ่ม directive
- ในขั้นตอนที่ 1 เพิ่ม
"use client"ในpage.tsxระดับบนสุดของ Next.js 13 เพื่อให้ทำงานเหมือนเดิม - หากต้องดึงข้อมูลฝั่งเซิร์ฟเวอร์ ให้เพิ่ม Server Component เป็น parent ของ Client Component
- Server Component ดึงข้อมูล
- ส่งข้อมูลที่ดึงมาให้ Client Component ผ่าน props
- สามารถแทนบทบาทเดิมของ
getServerSidePropsได้
- ในขั้นตอนที่ 2 ย้าย
"use client"จาก component ระดับบนสุดลงไปยัง child component- ใน
<Title />ที่ไม่ต้องใช้ client code สามารถลบ directive ออกและส่งเป็น HTML ล้วนได้ - ใน
<Player />ที่ต้องใช้ client code จะเกิดข้อผิดพลาด จึงต้องคง"use client"ไว้
- ใน
- วิธีนี้ทำให้เริ่มพิจารณา Server Components กับ component ใหม่และงาน refactor เดิม และช่วยลดขนาด bundle ได้บางส่วน
แพตเทิร์นที่ใช้เมื่อมีปัญหา performance
- เว็บไซต์เอกสารของ Mux ส่วนใหญ่ถูก generate แบบ static แต่ changelog sidebar ดึงมาจาก CMS
- หากห่อ sidebar ด้วย Suspense ส่วนอื่นของแอปไม่จำเป็นต้องรอจนกว่า CMS fetch จะเสร็จ
- convention loading.js ของ Next.js 13 ก็ใช้ Suspense และ streaming ภายในเช่นกัน
- หากต้องการเก็บไลบรารีขนาดใหญ่ไว้บนเซิร์ฟเวอร์ ต้องปรับการวาง Client Components และ Server Components
- ตัวอย่างคือการคงไลบรารี syntax highlighting Prism ไว้บนเซิร์ฟเวอร์
วิธีผสม Server Component ไว้ภายใน Client Component
- component ที่ Client Component import เข้ามาจะกลายเป็น Client Component ไปด้วย
- หากต้องการให้ Server Component เป็น child ของ Client Component ต้องไม่ import โดยตรง แต่ส่งผ่าน
childrenหรือ props- Server Component จะถูก render บนเซิร์ฟเวอร์
- ผลลัพธ์ที่ serialize แล้วจะถูกส่งให้ Client Component
- วิธีที่ผิดคือ import Server Component โดยตรงในไฟล์ Client Component
- วิธีที่ถูกคือย้อนขึ้นไปยัง Server Component parent ที่ใกล้ที่สุด แล้วส่ง Server Component ให้ Client Component เป็น child หรือ prop
ไม่สามารถแบ่งไฟล์เดียวให้ครึ่งหนึ่งเป็นเซิร์ฟเวอร์และครึ่งหนึ่งเป็นไคลเอนต์ได้
- ไม่สามารถทำให้ครึ่งหนึ่งของไฟล์เดียวเป็น Server Component และอีกครึ่งหนึ่งเป็น Client Component ได้
- Mux ใช้แพตเทิร์นแบ่งฟังก์ชันออกเป็นสองไฟล์อยู่บ่อยครั้ง
CodeBlock.server.js: import ไลบรารี syntax highlighting ขนาดใหญ่และ render บนเซิร์ฟเวอร์CodeBlock.client.js: ใช้useStateและonClickเพื่อให้ผู้ใช้สลับตัวอย่างโค้ดได้
- ตัวอย่างที่ render บนเซิร์ฟเวอร์ถูกส่งผ่าน props ไปยัง Client Component ดังนั้นงานเฉพาะเซิร์ฟเวอร์จึงไม่หลุดเข้าไปใน client bundle
- หาก re-export
CodeBlock.server.jsจากindex.jsผู้ใช้ก็ import แค่CodeBlockได้โดยไม่ต้องรับรู้การแยก server/client ภายใน
วิธีรับประกันให้รันเฉพาะบนเซิร์ฟเวอร์
- ช่วงแรกระหว่างพัฒนา มีการเพิ่ม
console.logเพื่อดูว่า log ออกมาจากเซิร์ฟเวอร์หรือเบราว์เซอร์ - หากต้องการรับประกันว่าโค้ดเฉพาะเซิร์ฟเวอร์จะไม่ถูกรวมเข้า bundle สามารถ import server-only package ได้
server-onlyมีประโยชน์ในการป้องกันไม่ให้ไลบรารีขนาดใหญ่หรือ secret key ถูกย้ายไปผิดที่- Next.js มี guard เพื่อป้องกันไม่ให้ environment variable ถูกใส่เข้า browser bundle โดยไม่ตั้งใจ
server-onlyที่ด้านบนของไฟล์ยังช่วยด้าน maintenance ด้วย- ผู้ดูแลจะรู้ได้ทันทีว่าไฟล์นั้นรันบนเซิร์ฟเวอร์
ต้นทุนและประโยชน์เมื่อพิจารณาว่าควรนำมาใช้หรือไม่
- React Server Components ไม่ใช่ฟีเจอร์ที่ได้มาฟรีๆ
- ต้นทุนไม่ได้มีแค่ข้อจำกัดของ CSS-in-JS และ React Context แต่ยังรวมถึงสิ่งต่อไปนี้
- การเข้าใจตำแหน่งการรันของเซิร์ฟเวอร์และไคลเอนต์
- การเข้าใจ hydration
- ต้นทุน infrastructure
- ความซับซ้อนของโค้ดจากการผสม Client Components กับ Server Components
- ความซับซ้อนเพิ่มพื้นผิวที่ทำให้ bug เข้ามาและทำให้ maintainability ของโค้ดลดลง
- framework ช่วยลดความซับซ้อนได้ แต่ไม่ได้กำจัดออกไป
- ประโยชน์ที่คาดหวังได้มีดังนี้
- ขนาด bundle เล็กลง
- การรันเร็วขึ้น
- performance ที่ดีขึ้นซึ่งสำคัญต่อ SEO
- แพตเทิร์นการโหลดข้อมูลขั้นสูงสำหรับเว็บไซต์ที่ซับซ้อนและมีข้อมูลมาก
- หากทีมพร้อมรับต้นทุนทางความเข้าใจเพิ่มเติมและประโยชน์ด้าน performance มีมากพอ RSC ก็อาจเป็นตัวเลือกที่เหมาะสม
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ในการเรนเดอร์ฝั่งเซิร์ฟเวอร์ ไคลเอนต์จะได้รับ HTML ที่มองเห็นได้ทันที
ผมก็รู้สึกแบบนั้นเหมือนกัน ถ้าอัปโหลด ไฟล์ข้อความธรรมดา ขึ้นเซิร์ฟเวอร์ มันจะถูกส่งไปยังเบราว์เซอร์ได้ค่อนข้างเร็ว
ถ้าอัปโหลดไฟล์ข้อความธรรมดาอีกไฟล์ที่ลงท้ายด้วย
.cssเบราว์เซอร์ก็รู้ว่าจะจัดการอย่างไร ทำให้องค์ประกอบบนหน้าจอแรกขยับและอาจดูดีขึ้นมากเป็นเคล็ดลับที่เจ๋งก็จริง แต่ก็ยังเป็นเรื่องรองเมื่อเทียบกับ เนื้อหาที่มีประโยชน์ ซึ่งอ่านได้บนหน้าจอแรก
ไม่รู้เหมือนกันว่า “มาเพิ่มฟีเจอร์ให้ไฮเปอร์เท็กซ์ทรงพลังขึ้นกันเถอะ” กลายมาเป็น “ตอนนี้ก็ไปสร้างแอปที่ใช้งานได้เองบนกองฟีเจอร์ขนาดใหญ่และไม่ค่อยสอดคล้องกันนี้แล้วกัน” ได้อย่างไร
ก่อนจะกระโดดเข้าไปใช้ RSC ควรหยุดคิดสักนิด
ไม่ว่ากำลังจะสร้างอะไร ฟูลสแตกเฟรมเวิร์กของจริง หรือเว็บเฟรมเวิร์กแบบคลาสสิกจัดการได้ง่ายกว่า เร็วกว่า และขยายได้ดีกว่ามาก
ใช้ Rails/Django/Laravel/… คู่กับ Turbolinks/Htmx/… หรือแค่โปรย JavaScript ฝั่งไคลเอนต์เล็กน้อยก็พอ
ถ้ารู้จัก Elixir/Phoenix ก็อาจได้ข้อดีหลายอย่างไปพร้อมกัน
ไม่ว่าคนจะทวีตกันมากแค่ไหน ก็ไม่ควรถลำลึกไปกับ RSC มากกว่านี้
คนที่มีประสบการณ์ในวงการน้อยกว่า 10 ปีจะได้เจอปัญหาพื้นฐานเดิม ๆ ที่เคยเกิดในไซต์ vanilla PHP สมัยก่อน และผมก็เคยเห็นการใส่ inline SQL hook ไว้ในคอมโพเนนต์ React มาแล้ว
รอบนี้ยังมี ความซับซ้อนโดยบังเอิญ เพิ่มเข้ามามากกว่าเดิมอีก
รักษาสติไว้ แล้วรีบออกผลิตภัณฑ์จริงให้ไวเพื่อหาเงินซื้อ Lamborghini ก็พอ
CORBA เป็นแนวทางที่ผสมคอมโพเนนต์โลคัลกับคอมโพเนนต์รีโมตเข้าด้วยกัน เป็นเทคโนโลยีที่โตเต็มที่และทำงานได้หลายภาษา
แล้วทำไมทุกคนถึงไม่ใช้กันล่ะ? นักพัฒนาส่วนใหญ่ในวันนี้อาจไม่เคยได้ยินชื่อด้วยซ้ำ
ถ้าอยากสร้างสถาปัตยกรรมคอมโพเนนต์แบบกระจายตัวขึ้นมาอีก ก็ควรศึกษาเหตุผลที่ CORBA และลูกหลานของมันไม่สามารถปักหลักแพร่หลายได้
คำใบ้คือ ขอบเขตของคอมโพเนนต์ที่ถูกซ่อนไว้ทำให้เกิด ความซับซ้อนที่ถูกซ่อนไว้
ขณะเดียวกัน ฝั่ง “HTML ที่เรนเดอร์บนเซิร์ฟเวอร์” กับ “HTML ที่เรนเดอร์บนไคลเอนต์” ต่างก็ทำงานได้ดีทั้งคู่
ผมว่าการที่แต่ละเว็บโปรเจกต์เลือกใช้ได้ทั้งสองทางถือว่าโชคดีมาก
หวังว่างานด้าน RSC จะไม่ทำให้การซัพพอร์ตของ React สำหรับแอปที่เรนเดอร์บนไคลเอนต์ล้วน ๆ มัวลง
ถ้าไม่ได้จำเป็นต้องมี แอปฟรอนต์เอนด์ริชแบบ progressive เต็มรูปแบบ จริง ๆ ส่วนใหญ่ LiveView ก็จัดการทุกอย่างผ่านคอมโพเนนต์ฝั่งเซิร์ฟเวอร์ได้ด้วย JavaScript ขั้นต่ำ
ไม่เพียงเท่านั้น ยังมีเธรดฝั่งเซิร์ฟเวอร์ผูกกับผู้ใช้แต่ละคน ทำให้สามารถผลักการเปลี่ยนแปลงไปยังฟรอนต์เอนด์ของผู้ใช้ได้เชิงรุก โดยไม่ต้องเขียน JavaScript handler แบบชัดเจน
เหมือนวงการติด ภาวะความจำเสื่อมทุก 10 ปี
มีเหตุผลที่สมเหตุสมผลมากที่เราเคยถอยห่างจาก UI ที่เรนเดอร์บนเซิร์ฟเวอร์
แน่นอนว่าเหตุผลเรื่องการปรับแต่งให้เหมาะกับเสิร์ชเอนจินมีอยู่เสมอ แต่ถ้ากังวลเรื่องนั้น ก็ทำไซต์เทมเพลตฝั่งเซิร์ฟเวอร์แบบดั้งเดิมไปเลยก็ได้
แอปแบบหน้าเดียวส่วนใหญ่ไม่จำเป็นต้องใช้ SSR และความซับซ้อนของมันเลย
อินเทอร์เฟซแดชบอร์ดช่วงแรกเรนเดอร์ด้วย Phoenix ทั้งหมด และเฉพาะหน้าที่ต้องการปฏิสัมพันธ์ฝั่งไคลเอนต์ขั้นสูงจริง ๆ เท่านั้นที่ฝัง React แยกเป็นรายหน้า
เพราะผลิตภัณฑ์แรกเป็นแดชบอร์ดวิเคราะห์ สถานการณ์นั้นจึงกลายเป็นแทบทั้งแดชบอร์ดในเวลาไม่นาน และการย้ายไปเป็นแอปหน้าเดียวเต็มรูปแบบโดยใช้ API ที่เราเปิดให้ลูกค้าอยู่แล้วก็เป็นเรื่องเป็นธรรมชาติ
ตอนนั้นคือปี 2016 จึงยังไม่มี LiveView แต่ถ้าจะสร้างผลิตภัณฑ์นั้นใหม่ตอนนี้ ก็ไม่แน่ใจเหมือนกันว่าจะตัดสินใจต่างไปหรือไม่
บล็อกโพสต์พูดถึงแอปพลิเคชันที่ขับเคลื่อนไซต์มาร์เก็ตติ้งสาธารณะ ซึ่งมีข้อกำหนดค่อนข้างต่างกัน แต่ผมอยากบอกว่าเราก็ใช้และชอบ Elixir/Phoenix เช่นกัน
รู้สึกเหมือนตัวเองแก่ลง
เฟรมเวิร์กสมัยนี้ใหญ่และซับซ้อนเกินไป
แค่เว็บ “Hello world” ง่าย ๆ ก็ต้องมี pipeline สำหรับ build และ compile ขนาดมหึมา และตอนนี้ยังมี server-side components พ่วงเข้ามาอีก
อยากรู้จริง ๆ ว่า overhead มันประมาณไหน
ไม่รู้เลยว่ากว่าจะรันตัวอย่าง Hello world ได้ ต้องผ่านโค้ดของเฟรมเวิร์กฝั่ง frontend และ backend กี่ชั้น
ผมคงกลับไปใช้ เฟรมเวิร์กคอมโพเนนต์เรียบง่ายขนาด 10KB ที่แค่กด F5 ก็ build ใหม่ให้แล้ว
เหมือนเป็นการ optimize เร็วเกินไปเพื่อฟีเจอร์หรูหราที่อาจจะมีหรือไม่มีในอนาคต
คล้ายกับการบอกว่าต้องจ้างวิศวกรมาทำ core sample และ seismic modeling ก่อนจะสร้างเล้าไก่
นักพัฒนารุ่นใหม่เริ่มมองเห็นแล้ว
เหมือนที่เราเคยทิ้งของแย่ ๆ อย่าง SOAP และ XML แล้วย้ายไปใช้เทคโนโลยีที่เรียบง่ายและใช้งานสะดวกกว่า คนรุ่นนี้ก็กำลังเรียนรู้อีกครั้งว่า ความซับซ้อนเป็นอันตราย
บางทีการพัฒนาซอฟต์แวร์อาจกลับมาสนุกอีกครั้งสักสองสามปี ก่อนที่คนรุ่นใหม่จะทำให้มันเละอีก
pipeline จะซับซ้อนหรือเรียบง่ายแค่ไหนก็ได้ตามที่ use case นั้นต้องการ
จะทำด้วย static files ล้วน ๆ ก็ได้ ใช้ Makefile เล็ก ๆ ที่เรียกคำสั่ง
esbuildแค่คำสั่งเดียวก็ได้ หรือจะเป็น config Webpack ขนาดมหึมาพร้อมปลั๊กอิน 30 ตัวก็ได้คุณเลือกได้เต็มที่ตามความต้องการและความซับซ้อนของสิ่งที่กำลังจะสร้าง
อีกอย่าง การประเมินเครื่องมือจากว่าทำ Hello world ง่าย ๆ ได้ง่ายแค่ไหน มีประโยชน์ก็ต่อเมื่องานจริงของคุณคือการทำแอปแบบนั้น
ทุกครั้งที่ SSR หรือสิ่งที่แตกแขนงจากมันถูกหยิบมาคุย ผมมักถามตัวเองเสมอว่ามันทำให้ซับซ้อนขึ้นโดยไม่จำเป็นหรือเปล่า
React server-rendered components ฟังดูเหมือนไปไกลเกินไป และสวนทางกับ flow ของ developer experience ที่เป็นธรรมชาติ
ถ้าความซับซ้อนของแอปเพิ่มเป็นสองเท่า มีกับดักในการเขียนโค้ดมากขึ้น ทำให้นักพัฒนาช้าลงและสับสนขึ้น แต่สิ่งที่ได้กลับมาเป็นแค่ performance ที่ดีขึ้นเล็กน้อย ก็สงสัยว่ามันคุ้มค่าหรือไม่
เว็บไซต์ PHP แบบเก่า ๆ และแอป Rails ที่ไม่มี single-page app ก็ทำงานได้ดีมาเป็นเวลานาน
ผมมีประสบการณ์จากการสร้างแอปใหม่ด้วย Next.js และโครงสร้างไดเรกทอรี
appแบบใหม่อย่างแรก ยากที่จะอนุมานว่าอะไรเกิดขึ้นบน server และอะไรเกิดขึ้นบน client
ถ้าจะรู้ก็ต้องไปสืบดู แต่เวลาที่เขียนโค้ดเร็ว ๆ ส่วนใหญ่ก็ไม่ได้ใส่ใจมากนัก
การเปลี่ยนแปลงเล็ก ๆ เพียงอย่างเดียวอาจทำให้ส่วนใหญ่ของหน้าเว็บย้ายจาก server ไป client อย่างกะทันหันได้ง่าย
รู้ดีว่าก่อน release จริงจะต้องตรวจสอบทีละหน้าอย่างระมัดระวังและใช้เวลามาก ซึ่งไม่ชอบเลย
อย่างที่สอง ไลบรารี React เดิมจำนวนมากใช้ hooks จึง สมมติว่ารันบน client
เรื่องนี้อาจลากโค้ดให้มาฝั่ง client ได้
จุดประสงค์ของการสู้กับ paradigm ใหม่นี้คือ server-side rendering เพื่อให้โหลดเร็วและทำ SEO ได้ดี แต่ถ้าไลบรารีที่นำเข้ามาไม่ร่วมมือ ก็เสียแรงเปล่าโดยสิ้นเชิง
อย่างที่สาม paradigm ไดเรกทอรี
appแบบใหม่ของ Next.js มีบั๊กมันยังใหม่และซับซ้อนมาก ทำให้ dynamic routes, parallel routes และการทำงานร่วมกันของพวกมันพังได้แบบสิ้นเชิง
ผมเคยเปิด issue ใน GitHub ของ Next.js เอง และมีคอมเมนต์ “ผมก็เป็นเหมือนกัน” ตามมาเยอะ
approach หนึ่งที่ผมใช้ เพิ่งถูกนักพัฒนาของ Vercel แก้เมื่อไม่นานนี้ แต่ตอนนั้นผมเปลี่ยนไปใช้ approach อื่นเพื่อหลบปัญหาแล้ว
สิ่งที่น่าหงุดหงิดที่สุดคือสภาพแวดล้อม development ใช้เวทมนตร์ของ lazy loading และ caching
ดูเหมือนมันพยายามคำนวณความต่างของหน้าแล้วส่ง partial update ผ่านอะไรคล้าย ๆ websocket แต่บางครั้งมันพังสนิทจนกู้กลับไม่ได้
บางครั้งการ recompile ทำให้เกิดการสื่อสารบางอย่างจาก server ไป client และพอกลับไปที่แท็บ Chrome แท็บนั้นก็ค้างสนิท จนต้องฆ่า process ผ่าน Chrome Task Manager
โดยรวมแล้วยังเป็นขั้นที่ใหม่มากและมี ขอบคม ๆ อยู่เยอะ
hooks จำนวนมากทำงานฝั่ง server ได้ด้วย และจริง ๆ แล้วไม่ได้ทำอะไรนอกจาก initialize ค่า
สิ่งที่น่าสนใจคือ PHP กับ JavaScript นั้นแทบจะมีไวยากรณ์ใกล้เคียงกันอยู่แล้ว
ต่างกันแค่ประมาณสัญลักษณ์
$หรือคีย์เวิร์ดvarแต่ NodeJS บอกว่า “เราอยากรัน JS บนเซิร์ฟเวอร์”แล้ว 15 ปีต่อมา JavaScript ก็ไล่ตามมาจนสุดท้ายแทบจะคล้าย PHP เพียงแต่มีคำย่อมากกว่า และเส้นโค้งการเรียนรู้ชันกว่า
แน่นอนว่าการสตรีมข้อมูลจากเซิร์ฟเวอร์ไปยังคอมโพเนนต์ฝั่งไคลเอนต์ด้วย Suspense นั้นเจ๋ง
ตอนนี้ใช้ NextJS 13 อยู่ และชอบที่มันทำให้ SSR ง่ายเหมือนที่ PHP ทำได้มาตลอด จึงขอแนะนำอย่างยิ่ง
ตอนที่ผู้คนเรียกมันว่า “แฟร็กทัลของการออกแบบที่แย่” ก็สมควร 100% และการมองหาทางออกจากที่อื่นเพื่อแก้ปัญหาก็สมเหตุสมผล
PHP ในปัจจุบันเป็นภาษาที่ดีขึ้นมาก และคุ้มค่าที่จะกลับไปพิจารณาใหม่ แต่ไม่ควรทำเหมือนว่ามันดีเท่านี้มาตั้งแต่แรก
และไม่ควรตัดสิน NodeJS โดยอิงจากระบบนิเวศของ React
ปริมาณ API และ wrapper มหาศาลที่ต้องใช้เพื่อรันระบบบน React นั้นเป็นความรับผิดชอบของชุมชน React
เป็น Stockholm syndrome แบบคลาสสิก
ความก้าวหน้าสมัยใหม่จำนวนมากเกิดขึ้นได้ก็เพราะเบราว์เซอร์ดีขึ้นก่อน
มันไม่เชิงวนกลับมาครบวงจรเสียทีเดียว แต่เป็นก้อนอะไรสักอย่างที่ถ้ามองจากไกลมาก ๆ จะดูคล้ายวงกลมมากกว่า
ด้านหนึ่ง มันเป็นนวัตกรรมเพราะทำให้สร้างแบ็กเอนด์ที่เร็วขึ้นได้ด้วยโมเดล concurrency แต่ก็ขาดฟีเจอร์หลายอย่างที่ภาษาแบ็กเอนด์เดิมอย่าง Java หรือ PHP มีอยู่แล้ว จึงมีการประดิษฐ์แพตเทิร์นเดิมขึ้นมาใหม่จำนวนมาก
ตัวภาษาเองก็ใช้เวลาหลายปีกว่าจะไปถึงระดับ “ความปลอดภัย” ที่ Java มีอยู่แล้วและ PHP กำลังก้าวไปหา
เทคโนโลยีที่ผ่านการพิสูจน์และเป็นมาตรฐานอย่าง XML รวมถึงการรับประกันเชิงสัญญาที่มันให้ได้ ก็ถูกทิ้งไปด้วยเหตุผลอย่างความหนักของมัน และเพราะ JSON อ่านเขียนได้ง่ายสำหรับมนุษย์
รู้สึกว่าเราสูญเสียเวลาและความพยายามไปมากจากการทิ้ง XML
การทำเอกสาร REST/JSON API ยังคงเป็นเรื่องเจ็บปวด
เมื่อ 20–25 ปีก่อน เราสามารถสร้าง data model และ parser จาก XML payload ได้แล้ว
ยังไม่เข้าใจเลยว่า XML มีปัญหาอะไรมากขนาดนั้น
มันหนักกว่า JSON บนสายสัญญาณเล็กน้อย แต่ถ้าใช้การบีบอัดหรือเปลี่ยนเป็นโปรโตคอลไบนารีด้วย EXI(https://www.w3.org/TR/exi/) ก็เป็นปัญหาที่แก้ได้
ไม่รู้ว่า EXI ได้รับการใช้งานจริงแค่ไหน แต่ตอนนั้นค่อนข้างคาดหวัง เพราะรู้ว่า XML ถูกส่งไปมามากเพียงใด
ในตอนนั้น non-blocking I/O ช่วยเพิ่มประสิทธิภาพได้มาก
แต่ในหัวของนักพัฒนาสมัยใหม่ ดูเหมือนมันถูกลดบทบาทเหลือแค่เครื่องมือโง่ ๆ สำหรับพ่น JSON หรือโฮสต์ toolchain เท่านั้น
ก็คงใช้สิ่งที่คุ้นเคยกัน แต่การใช้ React กับไซต์เอกสาร แทนที่จะใช้ static site generator หรือ CMS สำเร็จรูปที่มีแคช ดูเหมือนเป็นความสิ้นเปลือง
จากมุมมองนักพัฒนา React อาจสนุกกว่าก็ได้
Stripe เป็นผู้เริ่มกระแสการแสดง snippet โค้ดที่มี API key ของบัญชีอยู่ในนั้น เพื่อให้ทดลองได้ทันที
ไซต์เอกสารฝั่งฟรอนต์เอนด์แทบจะมีตัวอย่างที่รันได้และให้ลองเล่นได้โดยตรงในเอกสารเสมอ
การ bootstrap โปรเจกต์ใหม่นั้นง่ายและเร็วมาก แทบจะง่ายกว่าการเริ่มโปรเจกต์ HTML ล้วนด้วยซ้ำ
ผมพลาดอะไรไปหรือเปล่า?
อีกอย่าง อยากรู้มุมมองของคนที่กดไม่เห็นด้วยจริง ๆ
มันเป็น คอนเทนต์แบบสแตติก แล้วทำไมไม่ generate เป็น HTML พร้อม JavaScript นิดหน่อยสำหรับช่องค้นหาก็พอ
เหตุผลที่ใช้ React คือเพื่อใช้ ภาษาเดียว ทั่วทั้งฟรอนต์เอนด์
จะได้หลีกเลี่ยงการแยกกันเป็นบางคนใช้ React ในไซต์หนึ่ง ส่วนอีกคนใช้ Gatsby/Hugo ในอีกไซต์
Next.JS ทำสิ่งเดียวกับ Gatsby/Hugo ได้ แถมมีฟีเจอร์มากกว่าและอยู่บน React
ผมแก่พอที่จะจำยุคที่เซิร์ฟเวอร์เรนเดอร์ทุกอย่าง และใช้ CSS กับ Javascript แค่เสริมหน้าที่เรนเดอร์แล้วได้
เว็บกลายเป็นที่ที่มืดมนและ วิศวกรรมเกินเหตุ มาก
แทบไม่น่าเชื่อเลย
ดังนั้นวิธีที่ผมทำแอปคือให้เซิร์ฟเวอร์เรนเดอร์ก่อน แล้วค่อยเสริมทีหลัง
ดรอปดาวน์ที่พับได้หรือฟีเจอร์ลากแล้ววางของ jQuery เป็นของที่มีไว้ก็ดี แต่ผมยังจำ นรกของการจัดการสถานะ ในยุค js/jQuery ได้ชัด จึงไม่อยากกลับไปตรงนั้น
ล้อเล่นนะ หน้า Geocities แรกของผมเป็น HTML ที่มีแค่อะไรอย่างตัวนับผู้เข้าชมหรือ marquee
แอป/โปรเจกต์ PHP แรกที่ทำที่โรงเรียนก็ยังไม่ได้ใช้ JS ใช้เฟรมกับเมนูและเฮดเดอร์แบบสแตติก แล้วส่งข้อมูลไปแบ็กเอนด์ด้วยการ submit ฟอร์มเฉย ๆ
มันเคยมียุคแบบนั้นจริง ๆ
ตอนฝึกงานหนึ่งปีครั้งแรกที่เป็นส่วนหนึ่งของหลักสูตรมหาวิทยาลัย ผมใช้แบ็กเอนด์ Java, เทมเพลต JSX ในเลเยอร์การแสดงผล และใช้ PrototypeJS กับของอย่างไดอะล็อกหรือ accordion แบบแอนิเมชัน
ตอนนั้นแอนิเมชันก็คือ “เปลี่ยนความสูงขององค์ประกอบนี้กี่ครั้งต่อวินาที”
งานแรกของผมใช้ JS เยอะเพื่อเสริมหน้า เช่น ใส่สินค้าลงตะกร้า แครูเซลรูปภาพ และนั่นคือยุค jQuery
งานถัดมา ผมทำ UI ให้พนักงานซัพพอร์ตลูกค้าใช้ดูของแบบ SAP ด้วย BackboneJS อย่างย่ำแย่
งานต่อจากนั้นก็เป็นการทำฟรอนต์เอนด์ธนาคารเพื่อการลงทุนสำหรับลูกค้าใหม่ด้วย BackboneJS อีก
นั่นเป็นกรณีใช้งานที่เหมาะมากกับสิ่งที่ตอนนั้นเรียกว่า แอปพลิเคชันหน้าเดียว
ไม่ต้องทำ SEO, การเรนเดอร์ฝั่งฟรอนต์เอนด์ล้วน ๆ ก็เร็วพอ, เน้น API เป็นหลัก และเป็นช่วงที่ผู้คนเริ่มตระหนักว่าสามารถใช้ API เดียวกันกับเว็บและมือถือได้
ในความเห็นผม เครื่องมือพัฒนาเว็บไม่เคยดีเท่าตอนนี้ และประสบการณ์ผู้ใช้ก็ดีขึ้นอย่างมากตลอดหลายปี
สิ่งที่เราเคยเรียกว่า AJAX เติบโตจากลูกเล่นเสริมเล็ก ๆ ที่เรียบร้อย ไปเป็นองค์ประกอบพื้นฐานในชีวิตประจำวันในรูปของคอมโพเนนต์ฝั่งไคลเอนต์และแอปหน้าเดียว
เซิร์ฟเวอร์ยังทรงพลังได้ถ้าต้องการ แต่สำหรับแอปแบบโต้ตอบ เช่น แดชบอร์ด แผนที่ เกม ฟอรัม แอปออฟฟิศ และ IDE ออนไลน์ ความสามารถฝั่งไคลเอนต์ที่ทรงพลังเป็นเรื่องดี
สิ่งนี้ทำให้แอปในชีวิตประจำวันย้ายครั้งใหญ่จากแอปเดสก์ท็อปเฉพาะแต่ละระบบปฏิบัติการ ไปสู่แพลตฟอร์มสากลที่ครอบคลุมแล็ปท็อปและเดสก์ท็อปทั้งหมดได้
แน่นอนว่าพลังนั้นต้องแลกกับความซับซ้อนที่มากขึ้น
การเขียนบล็อกหรือหน้า landing page ด้วย HTML/CSS นั้นต่างจากการเขียนเว็บแอปเต็มรูปแบบมาก
Angular และ React ถูกสร้างขึ้นเพื่อช่วยพัฒนาแอปที่ซับซ้อนกว่าเดิมหลายเท่า ในยุคที่ JS runtime และตัวภาษาเองยังดิบกว่าภาษาเซิร์ฟเวอร์ไซด์ในตอนนั้นมาก
ช่วงปลายทศวรรษ 2010 มีช่วงที่เจ็บปวดจริง ๆ เมื่อเฟรมเวิร์ก JS หลายตัวแก้ปัญหาได้แค่เศษเสี้ยวเล็ก ๆ ของปัญหาเท่านั้น
ทุกวันนี้น้อยลงแล้ว
Next ชนะและกลายเป็นค่าเริ่มต้น ซึ่งก็สมเหตุสมผล
มันให้ระดับนามธรรมที่เหมาะกับแอปความซับซ้อนปานกลาง และช่วยผสมการเรนเดอร์ฝั่งเซิร์ฟเวอร์กับหน้าฝั่งไคลเอนต์ได้ดี
React Server Components ทำให้เส้นแบ่งนั้นกลายเป็นแนวคิดชั้นหนึ่งที่สะอาดขึ้น
แต่สิ่งนั้นจะมีความหมายก็ต่อเมื่อเกินระดับความซับซ้อนหนึ่งเท่านั้น
ถ้าไม่จำเป็น ก็ไม่ต้องใช้
ถ้าเป็นบล็อกหรือไซต์เอกสารที่ส่วนใหญ่เป็นสแตติก ก็มีสถาปัตยกรรมที่ง่ายกว่า
คุณยังเขียน HTML แล้วโปรย JS ไม่กี่บรรทัดเท่าที่จำเป็นได้ และสำหรับธุรกิจขนาดเล็กส่วนใหญ่ WordPress หรือ Wix ก็ใช้ได้
แต่ถ้าคุณกำลังสร้างแอปที่ซับซ้อนกว่า React นั้นเหมือนความฝันจริง ๆ เมื่อเทียบกับวิธีที่ต้องไปกลับเซิร์ฟเวอร์เพื่อคำนวณ UI ใหม่กับทุกการโต้ตอบเล็ก ๆ แล้วส่งหน้า HTML ทั้งหน้ากลับมาทุกครั้ง
วิธีแบบนั้นทำให้เสียบริบท ตำแหน่งในหน้า ฟอร์มที่กรอกค้างไว้ครึ่งหนึ่ง ฯลฯ กระตุ้นให้ใช้ข้อมูลฟอร์มเป็นสถานะ และมักทำให้งานหายเมื่อเผลอกดปุ่มย้อนกลับ หรือเมื่อเซิร์ฟเวอร์ล่มบ่อย ๆ ก่อนจะมีการขยายสเกลบนคลาวด์ที่ทำได้ง่าย
ในความเห็นผม มันเป็นวิศวกรรมเกินเหตุก็ต่อเมื่อถูกนำไปใช้ผิดที่ผิดทาง
ในกรณีใช้งานที่เหมาะสม เครื่องมือเหล่านี้มีประโยชน์จริง ๆ และบางครั้งก็จำเป็น
ส่วนที่น่าเสียดายอาจเป็นการสอนและส่งเสริมให้ใช้มันมากเกินไป แม้ในสถานการณ์ที่ไม่จำเป็นหรือถึงขั้นเป็นโทษ
สุดท้ายแล้วต้องใช้เครื่องมือให้เหมาะกับงาน
ผมไม่ได้พยายามผลักดัน React เหนือ Vue, Svelte หรือ HTMX แค่จะบอกว่าความซับซ้อนฝั่งไคลเอนต์ก็มีประโยชน์เหมือนกัน
ผมรู้สึกว่า React กำลังพยายามไล่ตามทางเลือกที่ทันสมัยกว่า ง่ายกว่า เร็วกว่า และถูกกว่า
แต่แทนที่จะแก้ปัญหารากฐานอย่างการเรนเดอร์ซ้ำ การต้องทำ memoization บ่อย ๆ และ abstraction ที่รั่ว React กลับซับซ้อนขึ้น
ถ้าผลลัพธ์สุดท้ายยอดเยี่ยม ผมคงเข้าใจความพยายามนั้น แต่มันไม่ใช่
ในสภาพแวดล้อมจริง React ช้ากว่าที่ benchmark แสดง และ Next ก็หนักกว่านั้นอีก
ช่วงหลังผมเห็นเว็บไซต์ที่ทำด้วย Next แล้วช้ามากอยู่หลายเว็บ
ไม่เข้าใจจริง ๆ
ถ้าทีม React อยากปรับปรุง React ก็ควรแก้ที่ แกนหลัก
ตอนนี้มี dependency ของ ecosystem มากเกินไปจนแตะแล้วพัง
Target.com, Walmart.com, Microsoft Teams และอีกนับไม่ถ้วนต่างใช้ React
ยังมี ecosystem คอมโพเนนต์ขนาดมหึมาและบริษัทต่าง ๆ ที่สร้างอยู่บนมันด้วย
แนวคิดหลักมันพังอยู่ แต่การแก้หมายความว่าอาจทำให้ทุกอย่างที่เหลือพังได้
ถ้าจะทำให้ทุกอย่างพังอยู่แล้ว ใช้อย่างอื่นดีกว่า
ตอนนี้เพราะมวลของ dependency จึงได้แต่ถูกผูกติดกับ React แล้วกลิ้งต่อไป
React ตั้งต้นด้วยการเรนเดอร์ซ้ำแล้วค่อย opt out ขณะที่ Vue, Solid, Preact, Svelte ล้วนเป็นแบบ opt in เฉพาะจุดที่จำเป็น
นี่เป็นหนึ่งในเหตุผลหลักที่ทำให้ใช้อย่างถูกต้องได้ยากและเปราะบางต่อบั๊กบางประเภท
แม้ภายนอกจะดูเหมือน JavaScript ธรรมดา แต่คุณต้องคอยระวังว่าจะ opt out ต่อหรือไม่ และในเฟรมเวิร์กอื่น ๆ บั๊กแบบนั้นพบได้น้อยหรือแทบไม่มี
https://react.dev/blog/2023/03/22/react-labs-what-we-have-be...
ไม่เข้าใจว่าทำไมตอนนี้ถึงกลับมาใช้ React บนแบ็กเอนด์เพื่อเรนเดอร์ HTML
นี่จะให้ย้อนกลับไปเมื่อ 10 ปีก่อน หรือไง
ช่วงนี้ผมสลับไปมาระหว่าง Svelte กับเทมเพลตของ Django บ่อย ๆ และแค่ข้อเท็จจริงที่ว่า Svelte รู้จัก DOM ก็ทำให้ประสบการณ์ดีกว่ามากแล้ว
ผมไม่เคยเห็นอะไรแบบนี้ในระบบเทมเพลตที่ไม่ใช่ JS
เคยผ่านทั้ง PHP และ jQuery มาแล้วด้วย
แถมถ้าฟรอนต์เอนด์เป็น React อยู่แล้ว การรวมการสร้าง HTML ทั้งหมดไว้ในที่เดียวก็ง่ายกว่ามาก
แถมยังรองรับ static type ด้วย
ไม่ได้จะปกป้องว่า RSC หรือ React เป็นคำตอบที่ดีที่สุดตลอดกาล แต่ข้อโต้แย้งบางส่วนในนี้ยังไม่สุกงอม
ประโยชน์ของ React/RSC ไม่ได้เหมือนกับการที่เซิร์ฟเวอร์ส่งคืน HTML/CSS กับ JavaScript เล็กน้อยในเชิงเทคนิค
มันยังคงเป็นแอปเดียวกัน และเป็นวิธีจัดการ ขอบเขตระหว่างไคลเอนต์/เซิร์ฟเวอร์ ได้ฉลาดกว่าการเทียบกับ SSR/ไฮเดรชัน
ผมพร้อมอ่านข้อโต้แย้งที่รู้ลึกกว่านี้ว่า React ออกแบบตัวเองไปสู่ทางตันหรือไม่ และทางออกคืออะไร แต่การกลับไปใช้ PHP ไม่ใช่คำตอบ
React ก็ไม่ได้เรียนยากขนาดนั้น และมีเหตุผลที่แม้แต่คนที่ไม่ใช่นักพัฒนาก็เรียนพื้นฐานได้ภายในไม่กี่สัปดาห์ใน bootcamp
JSX เหนือกว่าระบบเทมเพลตของ Django, PHP, Rails อย่างเป็นรูปธรรม
ข้อโต้แย้งแบบโยน ๆ มาครึ่งหนึ่งดูเหมือนจะยังไม่เคยแม้แต่ benchmark โปรเจกต์ของตัวเองด้วยเครื่องมืออย่าง Lighthouse ด้วยซ้ำ