การแยกโค้ดด้วย React.lazy และ Suspense

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

วิธี React.lazy ช่วยให้การแยกโค้ดแอปพลิเคชัน React ที่ระดับคอมโพเนนต์โดยใช้การนำเข้าแบบไดนามิกเป็นเรื่องง่าย

import React, { lazy } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));

const DetailsComponent = () => (
  <div>
    <AvatarComponent />
  </div>
)

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

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

ฟังก์ชัน React.lazy มีวิธีในตัวในการแยกคอมโพเนนต์ในแอปพลิเคชันออกเป็น JavaScript หลายๆ ชิ้นโดยไม่ต้องทำอะไรมาก จากนั้นคุณจะจัดการสถานะการโหลดได้เมื่อใช้ร่วมกับคอมโพเนนต์ Suspense

สืบสวน

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

อย่างไรก็ตาม จะมีความล่าช้าเล็กน้อยที่ผู้ใช้ต้องพบเมื่อ มีการดึงข้อมูลคอมโพเนนต์ที่แยกโค้ดผ่านเครือข่าย ดังนั้นจึงควร แสดงสถานะการโหลดที่มีประโยชน์ การใช้ React.lazy กับคอมโพเนนต์ Suspense จะช่วยแก้ปัญหานี้ได้

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));

const renderLoader = () => <p>Loading</p>;

const DetailsComponent = () => (
  <Suspense fallback={renderLoader()}>
    <AvatarComponent />
  </Suspense>
)

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

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

ตัวอย่างการทำงานของฟีเจอร์นี้

  • หากต้องการดูตัวอย่างเว็บไซต์ ให้กดดูแอป แล้วกด เต็มหน้าจอ เต็มหน้าจอ
  • กด `Control+Shift+J` (หรือ `Command+Option+J` ใน Mac) เพื่อเปิด DevTools
  • คลิกแท็บเครือข่าย
  • คลิกเมนูแบบเลื่อนลงการควบคุมอัตรา ซึ่งตั้งค่าเป็นไม่มีการควบคุมอัตราโดยค่าเริ่มต้น เลือก 3G เร็ว
  • คลิกปุ่มคลิกฉันในแอป

ตอนนี้สัญญาณบอกสถานะการโหลดจะแสดงนานขึ้น สังเกตว่าโค้ดทั้งหมดที่ประกอบกันเป็น AvatarComponent จะได้รับการดึงข้อมูลเป็นก้อนแยกต่างหาก

แผงเครือข่ายของเครื่องมือสำหรับนักพัฒนาเว็บแสดงไฟล์ chunk.js ที่กำลังดาวน์โหลด

การระงับคอมโพเนนต์หลายรายการ

อีกฟีเจอร์หนึ่งของ Suspense คือการอนุญาตให้คุณระงับการโหลดคอมโพเนนต์หลายรายการ แม้ว่าคอมโพเนนต์ทั้งหมดจะโหลดแบบ Lazy Loading ก็ตาม

เช่น

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));
const InfoComponent = lazy(() => import('./InfoComponent'));
const MoreInfoComponent = lazy(() => import('./MoreInfoComponent'));

const renderLoader = () => <p>Loading</p>;

const DetailsComponent = () => (
  <Suspense fallback={renderLoader()}>
    <AvatarComponent />
    <InfoComponent />
    <MoreInfoComponent />
  </Suspense>
)

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

คุณดูได้ด้วยการฝังต่อไปนี้

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

จัดการการโหลดที่ไม่สำเร็จ

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

React มีรูปแบบมาตรฐานสำหรับการจัดการข้อผิดพลาดในการโหลดประเภทนี้อย่างราบรื่น นั่นคือการใช้ขอบเขตข้อผิดพลาด ตามที่อธิบายไว้ในเอกสารประกอบ คอมโพเนนต์ React ใดก็ได้สามารถทำหน้าที่เป็นขอบเขตข้อผิดพลาดได้หากใช้เมธอดวงจรของคอมโพเนนต์อย่างใดอย่างหนึ่ง (หรือทั้ง 2 อย่าง) static getDerivedStateFromError() หรือ componentDidCatch()

หากต้องการตรวจหาและจัดการข้อผิดพลาดในการโหลดแบบ Lazy Loading คุณสามารถห่อSuspense คอมโพเนนต์ด้วยคอมโพเนนต์ระดับบนสุดที่ทำหน้าที่เป็นขอบเขตข้อผิดพลาด ภายในเมธอด render() ของ "ขอบเขตข้อผิดพลาด" คุณสามารถแสดงผลองค์ประกอบย่อยตามเดิมได้หากไม่มีข้อผิดพลาด หรือแสดงข้อความแสดงข้อผิดพลาดที่กำหนดเองหากเกิดข้อผิดพลาด

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));
const InfoComponent = lazy(() => import('./InfoComponent'));
const MoreInfoComponent = lazy(() => import('./MoreInfoComponent'));

const renderLoader = () => <p>Loading</p>;

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {hasError: false};
  }

  static getDerivedStateFromError(error) {
    return {hasError: true};
  }

  render() {
    if (this.state.hasError) {
      return <p>Loading failed! Please reload.</p>;
    }

    return this.props.children;
  }
}

const DetailsComponent = () => (
  <ErrorBoundary>
    <Suspense fallback={renderLoader()}>
      <AvatarComponent />
      <InfoComponent />
      <MoreInfoComponent />
    </Suspense>
  </ErrorBoundary>
)

บทสรุป

หากไม่แน่ใจว่าจะเริ่มใช้การแยกโค้ดกับแอปพลิเคชัน React ที่ใด ให้ทำตามขั้นตอนต่อไปนี้

  1. เริ่มที่ระดับเส้นทาง เส้นทางเป็นวิธีที่ง่ายที่สุดในการระบุจุดของ แอปพลิเคชันที่แยกได้ เอกสารประกอบของ React เอกสารประกอบของ React แสดงวิธีใช้ Suspense ร่วมกับ react-router
  2. ระบุคอมโพเนนต์ขนาดใหญ่ในหน้าเว็บของเว็บไซต์ที่แสดงผลเฉพาะเมื่อมีการโต้ตอบของผู้ใช้บางอย่าง (เช่น การคลิกปุ่ม) การแยกคอมโพเนนต์เหล่านี้จะช่วยลดเพย์โหลด JavaScript
  3. พิจารณาแยกส่วนอื่นๆ ที่อยู่นอกหน้าจอและไม่สำคัญต่อ ผู้ใช้