เจาะลึกการแสดงภาพ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 กำหนดสัญญาระหว่างองค์ประกอบทั้งหมดในระบบการออกแบบอย่างชัดเจนแล้ว เราพบว่าเราสามารถนำการเปลี่ยนแปลงมาใช้ได้อย่างมั่นใจมากขึ้น เรายังได้ประโยชน์อย่างมากจากโครงการ Web Platform Tests (WPT) ที่ยอดเยี่ยม ที่ช่วยให้หลายๆ ฝ่ายสามารถมีส่วนร่วมในชุดทดสอบเว็บร่วมกัน

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

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

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

ซึ่งพบได้บ่อยมากสำหรับโหมด 2-pass (เดินโครงสร้างต้นไม้ 2 ครั้งเพื่อกำหนดสถานะการออกแบบสุดท้าย) โหมดเค้าโครงที่อธิบายไว้ด้านล่าง ก่อนหน้านี้โค้ดของเรามีลักษณะดังนี้

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

โดยทั่วไปแล้ว การแก้ไขสำหรับข้อบกพร่องประเภทนี้มีดังนี้

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

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

ปัจจุบัน (ตามที่อธิบายไว้ข้างต้น) เรามีออบเจ็กต์ข้อจำกัดระดับบนสุดที่เปลี่ยนแปลงไม่ได้ซึ่งอธิบายอินพุตทั้งหมดจากเลย์เอาต์หลักไปยังย่อย เราจะจัดเก็บข้อมูลนี้ไว้กับข้อมูลโค้ดที่แก้ไขไม่ได้ ด้วยเหตุนี้ เราจึงมีศูนย์กลางที่เปรียบเทียบอินพุต 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 และตารางกริด

กล่อง 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