หลีกเลี่ยงการใช้เลย์เอาต์ที่มีขนาดใหญ่และซับซ้อน รวมถึงการกดเลย์เอาต์จำนวนมาก

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

เลย์เอาต์เป็นที่ที่เบราว์เซอร์ค้นหาข้อมูลทางเรขาคณิตสำหรับองค์ประกอบต่างๆ ได้แก่ ขนาดและตำแหน่งในหน้าเว็บ องค์ประกอบแต่ละรายการจะมีข้อมูลการปรับขนาดที่ชัดเจนหรือโดยนัยโดยอิงตาม CSS ที่ใช้ เนื้อหาขององค์ประกอบ หรือองค์ประกอบระดับบนสุด กระบวนการนี้เรียกว่า "เลย์เอาต์" ใน Chrome (และเบราว์เซอร์ที่ได้รับ เช่น Edge) และ Safari ใน Firefox จะเรียกว่า Reflow แต่กระบวนการจะมีประสิทธิภาพเหมือนกัน

เช่นเดียวกับการคำนวณรูปแบบ สิ่งที่กังวลเกี่ยวกับต้นทุนสำหรับเลย์เอาต์มีดังนี้

  1. จำนวนองค์ประกอบที่ต้องมีเลย์เอาต์ซึ่งเป็นผลมาจากขนาด DOM ของหน้าเว็บ
  2. ความซับซ้อนของเลย์เอาต์เหล่านั้น

สรุป

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

ผลกระทบของเลย์เอาต์ต่อเวลาในการตอบสนองของการโต้ตอบ

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

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

หากต้องการรักษา INP ของเว็บไซต์ให้น้อยที่สุดเท่าที่จะเป็นไปได้ คุณควรหลีกเลี่ยงเลย์เอาต์เมื่อทำได้ หากหลีกเลี่ยงเลย์เอาต์ไม่ได้เลย สิ่งสำคัญคือต้องจำกัดการทำงานของเลย์เอาต์เพื่อให้เบราว์เซอร์นำเสนอเฟรมถัดไปได้อย่างรวดเร็ว

หลีกเลี่ยงเลย์เอาต์หากเป็นไปได้

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

.box {
  width: 20px;
  height: 20px;
}

/**
  * Changing width and height
  * triggers layout.
  */

.box--expanded {
  width: 200px;
  height: 350px;
}

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

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

เครื่องมือสำหรับนักพัฒนาเว็บแสดงระยะเวลานานในเลย์เอาต์

เมื่อเจาะลึกเข้าไปในการติดตามในตัวอย่างด้านบน เราจะเห็นว่าใช้เวลามากกว่า 28 มิลลิวินาทีภายในเลย์เอาต์สำหรับแต่ละเฟรม ซึ่งเวลาที่เรามีเวลา 16 มิลลิวินาทีในการรับเฟรมบนหน้าจอในภาพเคลื่อนไหวนั้นถือว่าสูงเกินไป นอกจากนี้ คุณยังเห็นว่าเครื่องมือสำหรับนักพัฒนาเว็บจะแสดงขนาดโครงสร้างต้นไม้ (ในกรณีนี้คือ 1,618 องค์ประกอบ) และจำนวนโหนดที่ต้องการเลย์เอาต์ (ในกรณีนี้คือ 5 โหนด)

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

หลีกเลี่ยงการจัดวางแบบซิงโครนัสแบบบังคับ

การจัดส่งเฟรมไปยังหน้าจอมีคำสั่งซื้อนี้:

ใช้ Flexbox เป็นเลย์เอาต์

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

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

// Schedule our function to run at the start of the frame:
requestAnimationFrame(logBoxHeight);

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

อาจเกิดปัญหาได้หากคุณเปลี่ยนรูปแบบของช่องก่อนที่จะขอความสูง ดังนี้

function logBoxHeight () {
  box.classList.add('super-big');

  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

ในการตอบคำถามเกี่ยวกับความสูง เบราว์เซอร์ต้องก่อนใช้การเปลี่ยนแปลงรูปแบบ (เนื่องจากมีการเพิ่มคลาส super-big) และแล้วเรียกใช้การออกแบบ เมื่อนั้นแล้ว ก็จะสามารถแสดงความสูงที่ถูกต้องได้ ซึ่งเป็นงานที่ไม่จำเป็นและอาจมีค่าใช้จ่ายสูง

ด้วยเหตุนี้ คุณจึงควรจัดกลุ่มรูปแบบที่อ่านแล้วทำก่อน (ซึ่งเบราว์เซอร์สามารถใช้ค่าเลย์เอาต์ของเฟรมก่อนหน้าได้) แล้วจึงเขียนดังนี้

ทำงานอย่างถูกต้อง ฟังก์ชันข้างต้นจะเป็น:

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);

  box.classList.add('super-big');
}

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

หลีกเลี่ยงการดันเลย์เอาต์

มีวิธีที่จะทำให้เลย์เอาต์แบบซิงโครนัสที่บังคับแย่ลงได้ โดยทำเลย์เอาต์เหล่านี้จำนวนมากติดต่อกันอย่างรวดเร็ว ให้ดูโค้ดนี้

function resizeAllParagraphsToMatchBlockWidth () {
  // Puts the browser into a read-write-read-write cycle.
  for (let i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = `${box.offsetWidth}px`;
  }
}

โค้ดนี้จะวนซ้ำที่กลุ่มย่อหน้าและตั้งค่าความกว้างของแต่ละย่อหน้าให้ตรงกับความกว้างขององค์ประกอบที่เรียกว่า "box" ดูไม่อันตรายมาพอสมควร แต่ปัญหาคือ การวนซ้ำแต่ละครั้งจะอ่านค่ารูปแบบ (box.offsetWidth) จากนั้นใช้การอัปเดตความกว้างของย่อหน้า (paragraphs[i].style.width) ทันที เมื่อวนซ้ำครั้งถัดไป เบราว์เซอร์ต้องคำนึงถึงข้อเท็จจริงที่ว่ารูปแบบเปลี่ยนแปลงไปนับตั้งแต่ที่ขอ offsetWidth ครั้งล่าสุด (ในการทำซ้ำก่อนหน้านี้) และจึงต้องใช้การเปลี่ยนแปลงรูปแบบและเรียกใช้เลย์เอาต์ ซึ่งจะเกิดขึ้นในทุกๆ ครั้ง

วิธีแก้ไขตัวอย่างนี้คือให้อ่านแล้วเขียนค่าอีกครั้ง ดังนี้

// Read.
const width = box.offsetWidth;

function resizeAllParagraphsToMatchBlockWidth () {
  for (let i = 0; i < paragraphs.length; i++) {
    // Now write.
    paragraphs[i].style.width = `${width}px`;
  }
}

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

รูปภาพหลักจาก Unsplash โดย Hal Gatewood