หากไม่ได้ใช้การแสดงผลฝั่งเซิร์ฟเวอร์แต่ยังต้องการเร่งประสิทธิภาพของเว็บไซต์ React ลองใช้การแสดงผลล่วงหน้า
react-snap
เป็นไลบรารีของบุคคลที่สามที่แสดงผลหน้าเว็บในเว็บไซต์เป็นไฟล์ HTML แบบคงที่ล่วงหน้า ซึ่งอาจช่วยปรับปรุงเวลาของ First Paint ในแอปพลิเคชัน
ต่อไปนี้เป็นการเปรียบเทียบแอปพลิเคชันเดียวกันที่มีและไม่มีการแสดงผลล่วงหน้าซึ่งโหลดในการเชื่อมต่อ 3G และอุปกรณ์เคลื่อนที่จำลอง
เหตุใดจึงมีประโยชน์
ปัญหาด้านประสิทธิภาพหลักของแอปพลิเคชันแบบหน้าเว็บเดียวขนาดใหญ่คือผู้ใช้ต้องรอให้กลุ่ม 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 ที่สำคัญในบรรทัด
บทสรุป
หากคุณไม่ได้กำหนดเส้นทางการแสดงผลฝั่งเซิร์ฟเวอร์ในแอปพลิเคชัน ให้ใช้ react-snap
เพื่อแสดงผล HTML แบบคงที่ล่วงหน้าแก่ผู้ใช้
- ติดตั้งเป็นทรัพยากร Dependency ของการพัฒนาและเริ่มต้นด้วยการตั้งค่าเริ่มต้นเท่านั้น
- ใช้ตัวเลือก
inlineCss
แบบทดลองเพื่อแทรก CSS ที่สําคัญในหน้าหากตัวเลือกนี้ทํางานกับเว็บไซต์ของคุณ - หากคุณใช้การแยกโค้ดที่ระดับคอมโพเนนต์ภายในเส้นทางใดก็ตาม โปรดระมัดระวังอย่าแสดงผลสถานะการโหลดล่วงหน้าต่อผู้ใช้
react-snap
README จะอธิบายเกี่ยวกับเรื่องนี้โดยละเอียดยิ่งขึ้น