ฝั่งไคลเอ็นต์เทียบกับฝั่งเซิร์ฟเวอร์เทียบกับการแสดงผลล่วงหน้าสำหรับเว็บแอป

เผยแพร่แล้ว: 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 → เปลี่ยนเส้นทางไปยังไฟล์ root index.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 และเปรียบเทียบประสิทธิภาพ

ที่เกี่ยวข้อง: ภาพรวมของเครื่องกำเนิดไซต์คงที่ยอดนิยม