เจาะลึกการแสดงภาพNG: LayoutNG

Ian Kilpatrick
Ian Kilpatrick
Koji Ishi
Koji Ishi

ผมชื่อ Ian Kilpatrick เป็นหัวหน้าทีมวิศวกรของทีมเลย์เอาต์ Blink ร่วมกับ Koji Ishii ก่อนที่จะมาทำงานในทีม Blink ฉันเป็นวิศวกรฝั่งหน้าเว็บ (ก่อนที่ Google จะมีบทบาท "วิศวกรฝั่งหน้าเว็บ") เพื่อสร้างฟีเจอร์ต่างๆ ใน Google เอกสาร, ไดรฟ์ และ Gmail หลังจากทำงานในตำแหน่งดังกล่าวประมาณ 5 ปี ฉันได้เสี่ยงที่จะเปลี่ยนไปทำงานกับทีม Blink เพื่อเรียนรู้ C++ ในการทำงานอย่างมีประสิทธิภาพ และพยายามเพิ่มประสิทธิภาพโค้ดเบสของ Blink ที่ยุ่งยากอย่างมาก ทุกวันนี้ เรายังเข้าใจภาษานี้เพียงส่วนน้อยเท่านั้น ขอขอบคุณที่สละเวลาให้เราในระยะเวลานี้ ฉันรู้สึกสบายใจเมื่อทราบว่ามี "วิศวกรฝั่งหน้าเว็บที่กลับมาทำงานอีกครั้ง" จำนวนมากได้เปลี่ยนไปเป็น "วิศวกรเบราว์เซอร์" มาก่อนฉัน

ประสบการณ์ที่ผ่านมาเป็นแนวทางให้ผมในขณะที่อยู่ในทีม Blink ในฐานะวิศวกรฝั่งหน้าเว็บ ฉันพบปัญหาความไม่สอดคล้องของเบราว์เซอร์ ปัญหาด้านประสิทธิภาพ ข้อบกพร่องในการเรนเดอร์ และฟีเจอร์ที่ขาดหายไปอยู่เสมอ LayoutNG เป็นโอกาสที่ฉันได้ช่วยแก้ไขปัญหาเหล่านี้อย่างเป็นระบบภายในระบบเลย์เอาต์ของ Blink และเป็นผลงานรวมของความพยายามของวิศวกรหลายคนตลอดหลายปีที่ผ่านมา

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

ภาพรวมระดับ 30,000 ฟุตของสถาปัตยกรรมเครื่องมือจัดวาง

ก่อนหน้านี้ ต้นไม้เลย์เอาต์ของ Blink เป็นสิ่งที่เราเรียกว่า "ต้นไม้ที่เปลี่ยนแปลงได้"

แสดงแผนภูมิตามคำอธิบายในข้อความต่อไปนี้

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

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

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

การแสดงผลเลย์เอาต์บนโหนดในต้นไม้นี้ใช้ "สไตล์และ DOM" เป็นหลัก และข้อจำกัดหลักจากระบบเลย์เอาต์หลัก (ตารางกริด บล็อก หรือ Flex) เรียกใช้อัลกอริทึมข้อจำกัดเลย์เอาต์ และแสดงผลลัพธ์

โมเดลแนวคิดที่อธิบายไว้ก่อนหน้านี้

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

ต้นไม้ของข้อมูลโค้ด

เราได้อธิบายต้นไม้ Fragment แบบคงที่ก่อนหน้านี้ โดยอธิบายถึงวิธีออกแบบให้นำต้นไม้ส่วนใหญ่ก่อนหน้ามาใช้ซ้ำสำหรับเลย์เอาต์ที่เพิ่มขึ้น

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

นอกจากนี้ เรายังเขียนอัลกอริทึมเลย์เอาต์แบบแทรกในบรรทัด (ข้อความ) ใหม่ให้ตรงกับสถาปัตยกรรมแบบคงที่ใหม่ด้วย ไม่เพียงแต่จะสร้างการแสดงรายการแบบแบนที่ไม่เปลี่ยนแปลงสำหรับเลย์เอาต์ในบรรทัดเท่านั้น แต่ยังมีการแคชระดับย่อหน้าเพื่อให้จัดเรียงใหม่ได้เร็วขึ้น การกำหนดรูปร่างต่อย่อหน้าเพื่อใช้ฟีเจอร์แบบอักษรในองค์ประกอบและคำต่างๆ อัลกอริทึมแบบ 2 ทิศทาง Unicode ใหม่ที่ใช้ ICU การแก้ไขความถูกต้องจำนวนมาก และอื่นๆ

ประเภทข้อบกพร่องของเลย์เอาต์

โดยรวมแล้วข้อบกพร่องของเลย์เอาต์จะแบ่งออกเป็น 4 หมวดหมู่ที่แตกต่างกัน แต่ละหมวดหมู่มีสาเหตุที่ต่างกัน

ความถูกต้อง

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

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

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

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

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

ใช้งานไม่ได้

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

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

if (/* some very complicated statement */) {
  child->ForceLayout();
}

การแก้ไขข้อบกพร่องประเภทนี้มักจะมีลักษณะดังนี้

if (/* some very complicated statement */ ||
    /* another very complicated statement */) {
  child->ForceLayout();
}

โดยทั่วไป การแก้ไขปัญหาประเภทนี้จะทำให้ประสิทธิภาพลดลงอย่างมาก (ดูการลบล้างมากเกินไปด้านล่าง) และแก้ไขได้ยากมาก

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

การแก้ไขโค้ดการเปรียบเทียบนี้มักจะทำได้ง่าย และทดสอบหน่วยได้ง่ายๆ เนื่องจากการสร้างออบเจ็กต์อิสระเหล่านี้นั้นง่าย

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

โค้ดการเปรียบเทียบสำหรับตัวอย่างข้างต้นคือ

if (width.IsPercent()) {
  if (old_constraints.WidthPercentageSize() 
    != new_constraints.WidthPercentageSize())
   return kNeedsLayout;
}
if (height.IsPercent()) {
  if (old_constraints.HeightPercentageSize() 
    != new_constraints.HeightPercentageSize())
   return kNeedsLayout;
}

ฮิสเทรีซิส

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

ในตัวอย่างด้านล่าง เราเพียงแค่สลับคุณสมบัติ CSS ระหว่าง 2 ค่า แต่วิธีนี้จะทำให้สี่เหลี่ยมผืนผ้า "ขยายขนาดอย่างไม่จำกัด"

วิดีโอและเดโมแสดงข้อบกพร่องฮิสเทรีซิสใน Chrome 92 และต่ำกว่า ปัญหานี้ได้รับการแก้ไขแล้วใน Chrome 93

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

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

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

การลบล้างมากเกินไปและประสิทธิภาพ

ซึ่งตรงข้ามกับข้อบกพร่องประเภท "การลบล้างไม่เพียงพอ" บ่อยครั้งที่การแก้ไขข้อบกพร่องการลบล้างไม่สมบูรณ์จะทําให้ประสิทธิภาพลดลงอย่างมาก

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

เลย์เอาต์แบบ 2 ผ่านและประสิทธิภาพที่ลดลง

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

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

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

กล่อง 2 ชุด ชุดแรกแสดงขนาดที่แท้จริงของกล่องในพาสการวัด ชุดที่ 2 แสดงเลย์เอาต์ที่มีความสูงเท่ากันทั้งหมด

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

เลย์เอาต์แบบ 1, 2 และ 3 ผ่านที่อธิบายไว้ในคำบรรยาย
ในรูปภาพด้านบน เรามีองค์ประกอบ <div> 3 รายการ เลย์เอาต์แบบผ่านครั้งเดียวอย่างง่าย (เช่น เลย์เอาต์บล็อก) จะเข้าชมโหนดเลย์เอาต์ 3 โหนด (ความซับซ้อน O(n)) อย่างไรก็ตาม สำหรับเลย์เอาต์แบบ 2 ผ่าน (เช่น Flex หรือตารางกริด) การดำเนินการนี้อาจส่งผลให้เกิดความซับซ้อนในการเข้าชม O(2n) สำหรับตัวอย่างนี้
กราฟแสดงเวลาในการจัดวางที่เพิ่มขึ้นแบบทวีคูณ
รูปภาพและการสาธิตนี้แสดงเลย์เอาต์แบบทวีคูณที่มีเลย์เอาต์ตารางกริด ปัญหานี้ได้รับการแก้ไขใน Chrome 93 ซึ่งเป็นผลมาจากการย้ายตารางกริดไปยังสถาปัตยกรรมใหม่

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

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

เราพบว่าเมื่อนักพัฒนาซอฟต์แวร์พบปัญหาด้านประสิทธิภาพเกี่ยวกับเลย์เอาต์โดยเฉพาะ ปัญหานั้นมักเกิดจากข้อบกพร่องเกี่ยวกับเวลาในการแสดงผลแบบทวีคูณ ไม่ใช่ปริมาณข้อมูลที่ส่งผ่านขั้นต้นของระยะการแสดงผลของไปป์ไลน์ หากการเปลี่ยนแปลงเล็กๆ น้อยๆ (องค์ประกอบหนึ่งๆ เปลี่ยนแปลงพร็อพเพอร์ตี้ CSS รายการเดียว) ส่งผลให้เลย์เอาต์ใช้เวลา 50-100 มิลลิวินาที แสดงว่านี่อาจเป็นข้อบกพร่องของเลย์เอาต์แบบทวีคูณ

ข้อมูลสรุป

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

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

รูปภาพ 1 รูป (คุณรู้อยู่แล้วว่ารูปภาพไหน) โดย Una Kravets