การแก้ไขความไม่เสถียรของเลย์เอาต์

คำแนะนำแบบทีละขั้นในการใช้ WebPageTest เพื่อระบุและแก้ไขปัญหาความไม่เสถียรของเลย์เอาต์

ในโพสต์ก่อนหน้านี้ผมได้เขียนเกี่ยวกับการวัด Cumulative Layout Shift (CLS) ใน WebPageTest CLS คือการรวมการเปลี่ยนแปลงเลย์เอาต์ทั้งหมด โพสต์นี้เลยคิดว่าคงน่าสนใจหากได้เจาะลึกรายละเอียดและตรวจสอบการเปลี่ยนแปลงเลย์เอาต์แต่ละรายการในหน้าเว็บ เพื่อพยายามทำความเข้าใจสิ่งที่อาจเป็นสาเหตุของความไม่เสถียรและพยายามแก้ไขปัญหาจริงๆ

การวัดการเปลี่ยนแปลงเลย์เอาต์

เมื่อใช้ Layout Instability API เราจะรับรายการเหตุการณ์ Layout Shift ทั้งหมดในหน้าเว็บได้

new Promise(resolve => {
  new PerformanceObserver(list => {
    resolve(list.getEntries().filter(entry => !entry.hadRecentInput));
  }).observe({type: "layout-shift", buffered: true});
}).then(console.log);

ซึ่งจะสร้างอาร์เรย์ของการเปลี่ยนเลย์เอาต์ที่ไม่ได้เกิดเหตุการณ์การป้อนข้อมูลขึ้นก่อน:

[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 210.78500000294298,
    "duration": 0,
    "value": 0.0001045969445437389,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

ในตัวอย่างนี้มีการเปลี่ยนแปลงเล็กน้อยมากเพียงครั้งเดียวที่ 0.01% ที่ 210 มิลลิวินาที

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

การวัดการเปลี่ยนแปลงของเลย์เอาต์ใน WebPageTest

การวัดการเปลี่ยนแปลงเลย์เอาต์แต่ละรายการจะต้องใช้เมตริกที่กําหนดเอง ซึ่งคล้ายกับการวัด CLS ใน WebPageTest อย่างไรก็ตาม กระบวนการนี้ง่ายขึ้นเพราะ Chrome 77 มีความเสถียรแล้ว Layout Instability API จะเปิดใช้งานโดยค่าเริ่มต้น ดังนั้นคุณควรเรียกใช้ข้อมูลโค้ด JS ดังกล่าวบนเว็บไซต์ใดๆ ภายใน Chrome 77 และรับผลลัพธ์ได้ทันที ใน WebPageTest คุณจะใช้เบราว์เซอร์ Chrome เริ่มต้นได้และไม่ต้องกังวลเรื่องแฟล็กบรรทัดคำสั่งหรือการใช้ Canary

มาแก้ไขสคริปต์ดังกล่าวเพื่อสร้างเมตริกที่กำหนดเองสำหรับ WebPageTest กัน

[LayoutShifts]
return new Promise(resolve => {
  new PerformanceObserver(list => {
    resolve(JSON.stringify(list.getEntries().filter(entry => !entry.hadRecentInput)));
  }).observe({type: "layout-shift", buffered: true});
});

คำสัญญาในสคริปต์นี้จะแปลค่าเป็น JSON ของอาร์เรย์แทนที่จะเป็นอาร์เรย์ เนื่องจากเมตริกที่กําหนดเองจะสร้างได้เฉพาะข้อมูลประเภทพื้นฐาน เช่น สตริงหรือตัวเลข

เว็บไซต์ที่ฉันจะทำการทดสอบคือ ismyhostfastyet.com ซึ่งเป็นเว็บไซต์ที่ฉันสร้างขึ้นเพื่อเปรียบเทียบประสิทธิภาพในการโหลดในชีวิตจริงของโฮสต์เว็บ

การระบุสาเหตุของความไม่เสถียรของเลย์เอาต์

ในผลลัพธ์ เราจะเห็นว่าเมตริกที่กำหนดเองของ LayoutShift มีค่าต่อไปนี้

[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 3087.2349999990547,
    "duration": 0,
    "value": 0.3422101449275362,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

โดยสรุปแล้ว เลย์เอาต์ที่มีการเปลี่ยนแปลงเลย์เอาต์เดียวอยู่ที่ 34.2% เกิดขึ้นที่ 3, 087 มิลลิวินาที เราจะใช้มุมมองแถบฟิล์มของ WebPageTest เพื่อช่วยระบุตัวผู้ต้องหา

เซลล์ 2 เซลล์ในแถบฟิล์ม ซึ่งแสดงภาพหน้าจอก่อนและหลังการเปลี่ยนเลย์เอาต์
เซลล์ 2 เซลล์ในแถบฟิล์มที่แสดงภาพหน้าจอก่อนและหลังการเปลี่ยนเลย์เอาต์

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

ส่วนหัวของเว็บฟอนต์โผล่ออกมาจากที่อื่น
ส่วนหัวแบบอักษรของเว็บที่ปรากฏโดยไม่มีที่ใดเลย

แต่ยังไม่หมดเพียงเท่านี้ เมื่อหน้าเว็บเสร็จสมบูรณ์ที่ประมาณ 4.3 วินาที เราจะเห็นว่า <h1> ของหน้าเว็บ "โฮสต์ของฉันเร็วไหม" ปรากฏขึ้น ที่เป็นเช่นนี้เพราะเว็บไซต์ใช้แบบอักษรของเว็บและยังไม่ได้ดำเนินการเพิ่มประสิทธิภาพการแสดงผล เลย์เอาต์ไม่ได้มีการเปลี่ยนแปลงเมื่อเกิดเหตุการณ์นี้ขึ้น แต่ยังคงทำให้ผู้ใช้ได้รับประสบการณ์ที่ไม่ดีจากการต้องรอนานๆ จึงจะอ่านชื่อได้

การแก้ไขความไม่เสถียรของเลย์เอาต์

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

ต่อไปนี้เป็นโค้ดสำหรับสร้างข้อมูลตัวยึดตำแหน่ง

function getRandomFiller(maxLength) {
  var filler = '█';
  var len = Math.ceil(Math.random() * maxLength);
  return new Array(len).fill(filler).join('');
}

function getRandomDistribution() {
  var fast = Math.random();
  var avg = (1 - fast) * Math.random();
  var slow = 1 - (fast + avg);
  return [fast, avg, slow];
}

// Temporary placeholder data.
window.data = [];
for (var i = 0; i < 36; i++) {
  var [fast, avg, slow] = getRandomDistribution();
  window.data.push({
    platform: getRandomFiller(10),
    client: getRandomFiller(5),
    n: getRandomFiller(1),
    fast,
    avg,
    slow
  });
}
updateResultsTable(sortResults(window.data, 'fast'));

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

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

นี่คือลักษณะของตัวยึดตำแหน่งขณะที่ระบบกำลังโหลดข้อมูล JSON

ตารางข้อมูลจะแสดงผลพร้อมข้อมูลตัวยึดตำแหน่ง
ตารางข้อมูลจะแสดงผลพร้อมข้อมูลตัวยึดตำแหน่ง

การแก้ไขปัญหาแบบอักษรของเว็บจะง่ายขึ้นมาก เนื่องจากเว็บไซต์ใช้ Google Fonts เราจึงจำเป็นต้องส่งไปในพร็อพเพอร์ตี้ display=swap ในคำขอ CSS ไม่มีแล้ว Fonts API จะเพิ่มรูปแบบ font-display: swap ในการประกาศแบบอักษร ซึ่งจะช่วยให้เบราว์เซอร์แสดงผลข้อความในแบบอักษรสำรองได้ทันที ต่อไปนี้คือมาร์กอัปที่เกี่ยวข้องพร้อมการแก้ไข

<link href="https://fonts.googleapis.com/css?family=Chivo:900&display=swap" rel="stylesheet">

การยืนยันการเพิ่มประสิทธิภาพ

หลังจากเรียกใช้หน้าเว็บอีกครั้งผ่าน WebPageTest แล้ว เราจะสร้างการเปรียบเทียบก่อนและหลังเพื่อแสดงภาพความแตกต่างและวัดระดับใหม่ของความไม่เสถียรของเลย์เอาต์ได้

แถบฟิล์ม WebPageTest ที่แสดงให้เห็นทั้ง 2 เว็บไซต์ที่กำลังโหลดอยู่ข้างๆ กันโดยมีและไม่มีการปรับปรุงเลย์เอาต์
แถบฟิล์ม WebPageTest ที่แสดงให้เห็นทั้ง 2 เว็บไซต์ที่กำลังโหลดอยู่ข้างๆ กันโดยมีและไม่มีการปรับปรุงเลย์เอาต์
[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 3070.9349999997357,
    "duration": 0,
    "value": 0.000050272187989256116,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

ตามเมตริกที่กำหนดเอง ยังคงมีการเปลี่ยนแปลงเลย์เอาต์เกิดขึ้นที่ 3, 071 มิลลิวินาที (ใกล้เคียงกับก่อนหน้า) แต่ความรุนแรงของการเปลี่ยนแปลงจะน้อยกว่ามาก นั่นคือ 0.005% ผมอยู่กับสิ่งนี้ได้

นอกจากนี้ แถบฟิล์มยังเห็นได้ชัดเจนว่าแบบอักษร <h1> จะเปลี่ยนไปใช้แบบอักษรของระบบทันที ซึ่งทำให้ผู้ใช้อ่านได้เร็วขึ้น

บทสรุป

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

(อีกเรื่องหนึ่ง) การวัดความไม่เสถียรของเลย์เอาต์ที่พบจากผู้ใช้จริง

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

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

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