เส้นทางที่แสดงผลล่วงหน้าด้วยฟีเจอร์ Snap-Snap

หากไม่ได้ใช้การแสดงผลฝั่งเซิร์ฟเวอร์แต่ยังต้องการเร่งประสิทธิภาพของเว็บไซต์ React ลองใช้การแสดงผลล่วงหน้า

react-snap เป็นไลบรารีของบุคคลที่สามที่แสดงผลหน้าเว็บในเว็บไซต์เป็นไฟล์ HTML แบบคงที่ล่วงหน้า ซึ่งอาจช่วยปรับปรุงเวลาของ First Paint ในแอปพลิเคชัน

ต่อไปนี้เป็นการเปรียบเทียบแอปพลิเคชันเดียวกันที่มีและไม่มีการแสดงผลล่วงหน้าซึ่งโหลดในการเชื่อมต่อ 3G และอุปกรณ์เคลื่อนที่จำลอง

การเปรียบเทียบการโหลดแบบเทียบเคียง เวอร์ชันที่ใช้การแสดงผลล่วงหน้าจะโหลดเร็วขึ้น 4.2 วินาที

เหตุใดจึงมีประโยชน์

ปัญหาด้านประสิทธิภาพหลักของแอปพลิเคชันแบบหน้าเว็บเดียวขนาดใหญ่คือผู้ใช้ต้องรอให้กลุ่ม JavaScript ที่ประกอบกันเป็นเว็บไซต์ดาวน์โหลดเสร็จสิ้นก่อนจึงจะเห็นเนื้อหาจริง ยิ่งการรวมมีขนาดใหญ่เท่าใด ผู้ใช้ก็จะต้องรอนานขึ้นเท่านั้น

เพื่อแก้ไขปัญหานี้ นักพัฒนาซอฟต์แวร์จำนวนมากจึงใช้วิธีแสดงผลแอปพลิเคชันบนเซิร์ฟเวอร์ แทนที่จะเปิดเครื่องเฉพาะบนเบราว์เซอร์ เมื่อมีการเปลี่ยนไปหน้าเว็บ/เส้นทางแต่ละครั้ง ระบบจะสร้าง HTML ที่สมบูรณ์ในเซิร์ฟเวอร์และส่งไปยังเบราว์เซอร์ ซึ่งจะช่วยลดเวลาในการแสดงผลครั้งแรก แต่จะทำให้เวลาที่ได้รับข้อมูลไบต์แรกช้าลง

การแสดงผลล่วงหน้าเป็นเทคนิคแยกต่างหากที่มีความซับซ้อนน้อยกว่าการแสดงผลจากเซิร์ฟเวอร์ แต่ก็ช่วยปรับปรุงเวลา First Paint ในแอปพลิเคชันได้ด้วย ระบบจะใช้เบราว์เซอร์แบบไม่มีส่วนหัวหรือเบราว์เซอร์ที่ไม่มีอินเทอร์เฟซผู้ใช้เพื่อสร้างไฟล์ HTML แบบคงที่ของทุกเส้นทางในเวลาสร้าง จากนั้นจึงจะจัดส่งไฟล์เหล่านี้พร้อมกับกลุ่ม JavaScript ที่จําเป็นสําหรับแอปพลิเคชันได้

รีแอ็ก-สแนป

react-snap ใช้ Puppeteer เพื่อสร้างไฟล์ HTML ที่แสดงผลล่วงหน้าของเส้นทางต่างๆ ในแอปพลิเคชัน ในการเริ่มต้น ให้ติดตั้งเป็นข้อกําหนดในการพัฒนา ดังนี้

npm install --save-dev react-snap

จากนั้นเพิ่มสคริปต์ postbuild ใน package.json ดังนี้

"scripts": {
  //...
  "postbuild": "react-snap"
}

ซึ่งจะเรียกใช้คําสั่ง react-snap โดยอัตโนมัติทุกครั้งที่มีการสร้างบิวด์ใหม่ของแอปพลิเคชัน (npm build)

ขั้นตอนสุดท้ายที่ต้องทำคือเปลี่ยนวิธีบูตแอปพลิเคชัน เปลี่ยนไฟล์ src/index.js เป็นข้อมูลต่อไปนี้

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));
const rootElement = document.getElementById("root");

if (rootElement.hasChildNodes()) {
  ReactDOM.hydrate(<App />, rootElement);
} else {
  ReactDOM.render(<App />, rootElement);
}

แทนที่จะใช้ ReactDOM.render เพื่อแสดงผลเอลิเมนต์ React รูทใน DOM โดยตรง การดำเนินการนี้จะตรวจสอบว่ามีโหนดย่อยอยู่หรือไม่เพื่อพิจารณาว่าเนื้อหา HTML ได้รับการแสดงผลล่วงหน้า (หรือแสดงผลบนเซิร์ฟเวอร์) หรือไม่ หากเป็นเช่นนั้น ระบบจะใช้ ReactDOM.hydrate เพื่อแนบ Listener เหตุการณ์กับ HTML ที่สร้างขึ้นใหม่แทนการแนบรายการใหม่

ตอนนี้การสร้างแอปพลิเคชันจะสร้างไฟล์ HTML แบบคงที่เป็นเพย์โหลดสําหรับแต่ละเส้นทางที่ทำการ Crawl คุณสามารถดูลักษณะของเพย์โหลด HTML ได้โดยคลิก URL ของคําขอ HTML แล้วคลิกแท็บตัวอย่างในเครื่องมือสําหรับนักพัฒนาเว็บใน Chrome

การเปรียบเทียบก่อนและหลัง ภาพหลังแสดงว่าเนื้อหาได้รับการเรนเดอร์แล้ว

เนื้อหาที่ไม่มีการจัดรูปแบบปรากฏขึ้นเป็นระยะเวลาสั้นๆ

แม้ว่าตอนนี้ HTML แบบคงที่จะแสดงผลเกือบจะในทันที แต่ก็ยังไม่มีการจัดรูปแบบโดยค่าเริ่มต้น ซึ่งอาจทำให้เกิดปัญหาการแสดง "เนื้อหาที่ปรากฎขึ้นอย่างรวดเร็วโดยไม่มีการจัดรูปแบบ" (FOUC) ปัญหานี้อาจเห็นได้ชัดเป็นพิเศษหากคุณใช้ไลบรารี CSS-in-JS เพื่อสร้างตัวเลือก เนื่องจากจะต้องเรียกใช้แพ็กเกจ JavaScript ให้เสร็จสิ้นก่อนจึงจะใช้สไตล์ได้

เพื่อช่วยป้องกันปัญหานี้ คุณอาจแทรก CSS สําคัญหรือ CSS จํานวนขั้นต่ำที่จําเป็นสําหรับให้หน้าแรกแสดงผลลงใน <head> ของเอกสาร HTML โดยตรง react-snap ใช้ไลบรารีของบุคคลที่สามอีกรายการหนึ่งภายใต้minimalcss เพื่อดึง CSS ที่สําคัญสําหรับเส้นทางต่างๆ คุณเปิดใช้ได้โดยระบุข้อมูลต่อไปนี้ในไฟล์ package.json

"reactSnap": {
  "inlineCss": true
}

ตอนนี้การดูตัวอย่างการตอบกลับในเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome จะแสดงหน้าที่มีการจัดรูปแบบพร้อม CSS ที่สำคัญในบรรทัด

การเปรียบเทียบก่อนและหลัง ภาพหลังแสดงว่าเนื้อหาแสดงผลและมีการจัดรูปแบบเนื่องจาก CSS ที่สำคัญแบบแทรกในหน้า

บทสรุป

หากคุณไม่ได้กำหนดเส้นทางการแสดงผลฝั่งเซิร์ฟเวอร์ในแอปพลิเคชัน ให้ใช้ react-snap เพื่อแสดงผล HTML แบบคงที่ล่วงหน้าแก่ผู้ใช้

  1. ติดตั้งเป็นทรัพยากร Dependency ของการพัฒนาและเริ่มต้นด้วยการตั้งค่าเริ่มต้นเท่านั้น
  2. ใช้ตัวเลือก inlineCss แบบทดลองเพื่อแทรก CSS ที่สําคัญในหน้าหากตัวเลือกนี้ทํางานกับเว็บไซต์ของคุณ
  3. หากคุณใช้การแยกโค้ดที่ระดับคอมโพเนนต์ภายในเส้นทางใดก็ตาม โปรดระมัดระวังอย่าแสดงผลสถานะการโหลดล่วงหน้าต่อผู้ใช้ react-snap README จะอธิบายเกี่ยวกับเรื่องนี้โดยละเอียดยิ่งขึ้น