ฝั่งไคลเอ็นต์เทียบกับฝั่งเซิร์ฟเวอร์เทียบกับการแสดงผลล่วงหน้าสำหรับเว็บแอป
เผยแพร่แล้ว: 2022-03-11มีบางอย่างเกิดขึ้นภายในชุมชนส่วนหน้าเมื่อเร็ว ๆ นี้ การเรนเดอร์ฝั่งเซิร์ฟเวอร์มีแรงฉุดมากขึ้นเรื่อยๆ ด้วย React และฟีเจอร์ไฮเดรชั่นฝั่งเซิร์ฟเวอร์ในตัว แต่ไม่ใช่ทางออกเดียวที่จะมอบประสบการณ์ที่รวดเร็วให้กับผู้ใช้ด้วยคะแนน time-to-first-byte (TTFB) ที่เร็วมาก: การเรนเดอร์ล่วงหน้าก็เป็นกลยุทธ์ที่ดีเช่นกัน อะไรคือความแตกต่างระหว่างโซลูชันเหล่านี้กับแอปพลิเคชันที่แสดงโดยไคลเอ็นต์ทั้งหมด
แอปพลิเคชันที่แสดงโดยไคลเอ็นต์
เนื่องจากมีเฟรมเวิร์กอย่าง Angular, Ember.js และ Backbone นักพัฒนาฟรอนต์เอนด์จึงมักจะแสดงทุกอย่างในฝั่งไคลเอ็นต์ ขอบคุณ Google และความสามารถในการ "อ่าน" JavaScript มันใช้งานได้ดีและเป็นมิตรกับ SEO
ด้วยโซลูชันการแสดงผลฝั่งไคลเอ็นต์ คุณจะเปลี่ยนเส้นทางคำขอไปยังไฟล์ HTML ไฟล์เดียว และเซิร์ฟเวอร์จะส่งโดยไม่มีเนื้อหาใดๆ (หรือหน้าจอโหลด) จนกว่าคุณจะดึง JavaScript ทั้งหมด และปล่อยให้เบราว์เซอร์คอมไพล์ทุกอย่างก่อนแสดงเนื้อหา
ภายใต้การเชื่อมต่ออินเทอร์เน็ตที่ดีและเชื่อถือได้ มันค่อนข้างเร็วและทำงานได้ดี แต่มันจะดีกว่านี้มาก และไม่ต้องยากเลยที่จะทำให้เป็นแบบนั้น นั่นคือสิ่งที่เราจะเห็นในส่วนต่อไปนี้
การแสดงผลฝั่งเซิร์ฟเวอร์ (SSR)
โซลูชัน SSR เป็นสิ่งที่เราเคยทำเมื่อหลายปีก่อน แต่มักจะลืมไปว่าโซลูชันการแสดงผลฝั่งไคลเอ็นต์
ด้วยโซลูชันการเรนเดอร์ฝั่งเซิร์ฟเวอร์แบบ เก่า คุณได้สร้างหน้าเว็บ—เช่น PHP— เซิร์ฟเวอร์รวบรวมทุกอย่าง รวมข้อมูล และส่งหน้า HTML ที่มีข้อมูลครบถ้วนไปยังไคลเอนต์ มันรวดเร็วและมีประสิทธิภาพ
แต่... ทุกครั้งที่คุณนำทางไปยังเส้นทางอื่น เซิร์ฟเวอร์ต้องทำงานใหม่ทั้งหมด: รับไฟล์ PHP คอมไพล์และส่ง HTML โดยที่ CSS และ JS ทั้งหมดจะชะลอการโหลดหน้าเป็นสองสามร้อย ms หรือ แม้กระทั่งวินาทีทั้งหมด
จะเกิดอะไรขึ้นถ้าคุณสามารถโหลดหน้าแรกด้วยโซลูชัน SSR แล้วใช้เฟรมเวิร์กเพื่อกำหนดเส้นทางแบบไดนามิกด้วย AJAX โดยดึงเฉพาะข้อมูลที่จำเป็น
นี่คือเหตุผลที่ SSR ได้รับแรงฉุดลากมากขึ้นเรื่อยๆ ภายในชุมชน เนื่องจาก React ทำให้ปัญหานี้เป็นที่นิยมด้วยโซลูชันที่ใช้งานง่าย: วิธี RenderToString
เว็บแอปพลิเคชันรูปแบบใหม่นี้เรียกว่า แอปสากล หรือแอปไอ โซมอร์ฟิ ค ยังคงมีการโต้เถียงกันอยู่บ้างเกี่ยวกับความหมายที่แท้จริงของคำเหล่านี้และความสัมพันธ์ระหว่างคำเหล่านี้ แต่หลายคนใช้แทนกันได้
อย่างไรก็ตาม ข้อดีของโซลูชันนี้คือสามารถพัฒนาแอปฝั่งเซิร์ฟเวอร์และฝั่งไคลเอ็นต์ด้วยโค้ดเดียวกัน และมอบประสบการณ์ที่รวดเร็วมากให้กับผู้ใช้ด้วยข้อมูลที่กำหนดเอง ข้อเสียคือคุณต้องเปิดเซิร์ฟเวอร์
SSR ใช้เพื่อดึงข้อมูลและเติมข้อมูลล่วงหน้าในหน้าเว็บด้วยเนื้อหาที่กำหนดเอง โดยใช้ประโยชน์จากการเชื่อมต่ออินเทอร์เน็ตที่เชื่อถือได้ของเซิร์ฟเวอร์ กล่าวคือ การเชื่อมต่ออินเทอร์เน็ตของเซิร์ฟเวอร์เองนั้นดีกว่าการเชื่อมต่อของผู้ใช้ที่มี lie-fi) ดังนั้นจึงสามารถดึงข้อมูลล่วงหน้าและรวมข้อมูลเข้าด้วยกันก่อนที่จะส่งข้อมูลให้ผู้ใช้
ด้วยข้อมูลที่เติมไว้ล่วงหน้า การใช้แอป SSR ยังสามารถแก้ไขปัญหาที่แอปที่แสดงผลโดยไคลเอ็นต์มีเกี่ยวกับการแชร์ทางสังคมและระบบ OpenGraph ตัวอย่างเช่น หากคุณมีไฟล์ index.html
เพียงไฟล์เดียวที่จะส่งไปยังไคลเอนต์ ไฟล์เหล่านั้นก็จะมีข้อมูลเมตาเพียงประเภทเดียว ซึ่งมีแนวโน้มว่าจะเป็นข้อมูลเมตาของหน้าแรกของคุณมากที่สุด สิ่งนี้จะไม่ได้รับบริบทเมื่อคุณต้องการแบ่งปันเส้นทางอื่น ดังนั้นจะไม่มีเส้นทางใดของคุณปรากฏบนไซต์อื่นที่มีเนื้อหาผู้ใช้ที่เหมาะสม (คำอธิบายและภาพตัวอย่าง) ที่ผู้ใช้ต้องการแบ่งปันกับคนทั้งโลก
การแสดงผลล่วงหน้า
เซิร์ฟเวอร์บังคับสำหรับแอปสากลอาจเป็นอุปสรรคสำหรับบางคน และอาจเกินความสามารถสำหรับแอปพลิเคชันขนาดเล็ก นี่คือเหตุผลที่การเรนเดอร์ล่วงหน้าอาจเป็นทางเลือกที่ดี
ฉันค้นพบวิธีแก้ปัญหานี้ด้วย Preact และ CLI ของตัวเองที่ให้คุณคอมไพล์เส้นทางที่เลือกไว้ล่วงหน้าทั้งหมด เพื่อให้คุณสามารถจัดเก็บไฟล์ HTML ที่มีข้อมูลครบถ้วนไปยังเซิร์ฟเวอร์แบบส แตติก สิ่งนี้ช่วยให้คุณมอบประสบการณ์ที่รวดเร็วเป็นพิเศษแก่ผู้ใช้ด้วยฟังก์ชันไฮเดรชั่น Preact/React โดยไม่จำเป็นต้องใช้ Node.js
สิ่งที่จับได้คือ เนื่องจากนี่ไม่ใช่ SSR คุณจึงไม่มีข้อมูลเฉพาะผู้ใช้ที่จะแสดง ณ จุดนี้ เป็นเพียงไฟล์คงที่ (และค่อนข้างทั่วไป) ที่ส่งโดยตรงในคำขอแรกตามที่เป็นอยู่ ดังนั้น หากคุณมีข้อมูลเฉพาะผู้ใช้ นี่คือที่ที่คุณสามารถผสานรวมโครงกระดูกที่ออกแบบมาอย่างสวยงามเพื่อแสดงให้ผู้ใช้เห็นว่าข้อมูลของพวกเขากำลังจะมาถึง เพื่อหลีกเลี่ยงความหงุดหงิดในส่วนของพวกเขา:
มีอีกสิ่งที่จับได้: เพื่อให้เทคนิคนี้ทำงาน คุณยังคงต้องมีพรอกซีหรือบางอย่างเพื่อเปลี่ยนเส้นทางผู้ใช้ไปยังไฟล์ที่ถูกต้อง
ทำไม?
ด้วยแอปพลิเคชันหน้าเดียว คุณต้องเปลี่ยนเส้นทางคำขอทั้งหมดไปยังไฟล์รูท จากนั้นเฟรมเวิร์กจะเปลี่ยนเส้นทางผู้ใช้ด้วยระบบการกำหนดเส้นทางในตัว ดังนั้นการโหลดหน้าแรกจะเป็นไฟล์รูทเดียวกันเสมอ

เพื่อให้โซลูชันการแสดงผลล่วงหน้าทำงาน คุณต้องบอกพร็อกซีของคุณว่าเส้นทางบางเส้นทางต้องการไฟล์เฉพาะและไม่ใช่ไฟล์ root index.html
เสมอไป
ตัวอย่างเช่น สมมติว่าคุณมีสี่เส้นทาง ( /
, /about
, /jobs
และ blog
) และเส้นทางทั้งหมดมีรูปแบบที่แตกต่างกัน คุณต้องมีไฟล์ HTML ที่แตกต่างกันสี่ไฟล์เพื่อส่งโครงกระดูกไปยังผู้ใช้ จากนั้นจึงให้ React/Preact/อื่นๆ เติมน้ำด้วยข้อมูล ดังนั้น หากคุณเปลี่ยนเส้นทางเส้นทางเหล่านั้นทั้งหมดไปยังไฟล์ root index.html
หน้าดังกล่าวจะรู้สึกไม่พึงใจและมีข้อผิดพลาดในระหว่างการโหลด โดยที่ผู้ใช้จะเห็นโครงร่างของหน้าที่ไม่ถูกต้องจนกว่าจะโหลดเสร็จและเปลี่ยนเลย์เอาต์ ตัวอย่างเช่น ผู้ใช้อาจเห็นโครงกระดูกของหน้าแรกที่มีเพียงคอลัมน์เดียว เมื่อพวกเขาขอหน้าอื่นที่มีแกลเลอรีเหมือน Pinterest
วิธีแก้ไขคือบอกพร็อกซีของคุณว่าเส้นทางทั้งสี่นั้นต้องการไฟล์เฉพาะ:
-
https://my-website.com
→ เปลี่ยนเส้นทางไปยังไฟล์ rootindex.html
-
https://my-website.com/about
→ เปลี่ยนเส้นทางไปยังไฟล์/about/index.html
-
https://my-website.com/jobs
→ เปลี่ยนเส้นทางไปยังไฟล์/jobs/index.html
-
https://my-website.com/blog
→ เปลี่ยนเส้นทางไปยังไฟล์/blog/index.html
นี่คือเหตุผลที่โซลูชันนี้มีประโยชน์สำหรับแอปพลิเคชันขนาดเล็ก คุณจะเห็นได้ว่ามันจะเจ็บปวดเพียงใดถ้าคุณมีหน้าสองสามร้อยหน้า
พูดอย่างเคร่งครัด ไม่จำเป็นต้องทำเช่นนี้—คุณสามารถใช้ไฟล์สแตติกได้โดยตรง ตัวอย่างเช่น https://my-website.com/about/
จะทำงานโดยไม่มีการเปลี่ยนเส้นทาง เนื่องจากจะค้นหา index.html
ภายในไดเรกทอรีโดยอัตโนมัติ แต่คุณต้องการพร็อกซี่นี้หากคุณมี URL พารามิเตอร์ https://my-website.com/profile/guillaume
จะต้องเปลี่ยนเส้นทางคำขอไปที่ /profile/index.html
ด้วยเลย์เอาต์ของตัวเอง เพราะ profile/guillaume/index.html
ไม่มีอยู่และจะทำให้เกิดข้อผิดพลาด 404
กล่าวโดยสรุป มีสามมุมมองพื้นฐานที่เล่นกับกลยุทธ์การแสดงผลที่อธิบายข้างต้น: หน้าจอการโหลด โครงกระดูก และหน้าเต็มเมื่อแสดงผลในที่สุด
ขึ้นอยู่กับกลยุทธ์ บางครั้งเราใช้มุมมองทั้งสามนี้ และบางครั้งเราข้ามไปยังหน้าที่แสดงผลทั้งหมดโดยตรง ในกรณีการใช้งานเดียวเท่านั้นที่เราถูกบังคับให้ใช้วิธีอื่น:
วิธี | การลงจอด (เช่น / ) | คงที่ (เช่น /about ) | แก้ไขไดนามิก (เช่น /news ) | พารามิเตอร์ไดนามิก (เช่น /users/:user-id ) |
---|---|---|---|---|
แสดงผลโดยไคลเอ็นต์ | กำลังโหลด → เต็ม | กำลังโหลด → เต็ม | กำลังโหลด → โครงกระดูก → เต็ม | กำลังโหลด → โครงกระดูก → เต็ม |
แสดงผลล่วงหน้า | เต็ม | เต็ม | โครงกระดูก → อิ่ม | HTTP 404 (ไม่พบหน้า) |
แสดงผลล่วงหน้าด้วย Proxy | เต็ม | เต็ม | โครงกระดูก → อิ่ม | โครงกระดูก → อิ่ม |
SSR | เต็ม | เต็ม | เต็ม | เต็ม |
การแสดงผลเฉพาะไคลเอ็นต์มักไม่เพียงพอ
แอปพลิเคชันที่แสดงผลโดยไคลเอ็นต์เป็นสิ่งที่เราควรหลีกเลี่ยงในขณะนี้ เนื่องจากเราสามารถทำได้ดีกว่าสำหรับผู้ใช้ และในกรณีนี้ การทำได้ดีขึ้นก็ง่ายพอๆ กับโซลูชันการแสดงผลล่วงหน้า แน่นอนว่าเป็นการปรับปรุงการเรนเดอร์เฉพาะไคลเอ็นต์และใช้งานง่ายกว่าแอปพลิเคชันที่แสดงผลฝั่งเซิร์ฟเวอร์โดยสมบูรณ์
แอปพลิเคชัน SSR/สากลอาจมีประสิทธิภาพมาก หากคุณมีแอปพลิเคชันขนาดใหญ่ที่มีหน้าต่างๆ มากมาย ช่วยให้เนื้อหาของคุณมีสมาธิและมีความเกี่ยวข้องเมื่อพูดคุยกับโปรแกรมรวบรวมข้อมูลทางสังคม สิ่งนี้ก็เป็นจริงเช่นกันสำหรับโรบ็อตของเครื่องมือค้นหา ซึ่งตอนนี้นำประสิทธิภาพของเว็บไซต์ของคุณมาพิจารณาเมื่อจัดอันดับ
คอยติดตามบทช่วยสอนติดตามผล ซึ่งฉันจะอธิบายเกี่ยวกับการเปลี่ยนแปลงของ SPA เป็นเวอร์ชันแสดงผลล่วงหน้าและ SSR และเปรียบเทียบประสิทธิภาพ