เจาะลึก RenderingNG: การกระจายตัวของบล็อก LayoutNG

Morten Stenshorne
Morten Stenshorne

การแยกบล็อกคือการแยกกล่องระดับบล็อก CSS (เช่น ส่วนหรือย่อหน้า) ออกเป็นหลายส่วนเมื่อไม่พอดีทั้งหมดภายในคอนเทนเนอร์เศษข้อมูล 1 รายการ ซึ่งเรียกว่า Fragmentainer องค์ประกอบการแยกส่วนไม่ใช่องค์ประกอบ แต่แสดงคอลัมน์ในเลย์เอาต์หลายคอลัมน์ หรือหน้าในสื่อแบบแบ่งหน้า

เนื้อหาต้องอยู่ในบริบทการแยกส่วนจึงจะเกิดการแยกส่วน บริบทการกระจาย Fragment มักสร้างขึ้นจากคอนเทนเนอร์ที่มีหลายคอลัมน์ (เนื้อหาแบ่งออกเป็นหลายคอลัมน์) หรือเมื่อพิมพ์ (เนื้อหาจะแบ่งออกเป็นหน้าต่างๆ) อาจต้องแยกย่อหน้ายาวที่มีหลายบรรทัดออกเป็นหลายส่วน เพื่อให้ระบบวางบรรทัดแรกไว้ในส่วนที่ 1 และวางบรรทัดที่เหลือไว้ในส่วนที่ตามมา

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

การแยกบล็อกนั้นคล้ายกับการแยกประเภทอื่นๆ ที่รู้จักกันดีอย่างการแยกบรรทัด หรือที่เรียกว่า "การแบ่งบรรทัด" องค์ประกอบในบรรทัดที่มีมากกว่า 1 คำ (โหนดข้อความ องค์ประกอบ <a> ฯลฯ) และอนุญาตให้มีการแบ่งบรรทัดอาจแบ่งออกเป็นหลายส่วน ส่วนย่อยแต่ละรายการจะวางอยู่ในช่องบรรทัดที่แตกต่างกัน กล่องบรรทัดคือการแยกย่อหน้าย่อยในบรรทัด ซึ่งเทียบเท่ากับ fragmentainer สำหรับคอลัมน์และหน้าเว็บ

การแยกส่วนบล็อก LayoutNG

LayoutNGBlockFragmentation เป็นการเขียนเครื่องมือแยกส่วนสำหรับ LayoutNG ขึ้นใหม่ ซึ่งเปิดตัวครั้งแรกใน Chrome 102 ในแง่ของโครงสร้างข้อมูล รูปแบบนี้แทนที่โครงสร้างข้อมูลก่อน NG หลายรายการด้วยข้อมูลโค้ด NG ที่แสดงในต้นไม้ข้อมูลโค้ดโดยตรง

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

ตัวอย่างการจัดแนวส่วนหัว
รูปที่ 1 ตัวอย่างที่ 1 แสดงส่วนหัวที่ด้านล่างของหน้า ส่วนที่สองแสดงส่วนหัวที่ด้านบนของหน้าถัดไปซึ่งมีเนื้อหาที่เกี่ยวข้อง

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

การแยกบล็อกใน LayoutNG เสร็จสมบูรณ์แล้ว

การกระจาย Fragment หลัก (บล็อกคอนเทนเนอร์ รวมถึงเลย์เอาต์แบบเส้น จำนวนลอยตัว และการจัดตำแหน่งที่ไม่เคลื่อนไหว) ที่จัดส่งใน Chrome 102 Flex และ Fragment ตารางกริดจัดส่งใน Chrome 103 ส่วนตาราง Fragment จัดส่งใน Chrome 106 สุดท้ายคือการพิมพ์ที่มาพร้อมกับ Chrome 108 การแยกบล็อกเป็นชิ้นๆ เป็นฟีเจอร์สุดท้ายที่อาศัยเครื่องมือรุ่นเดิมในการจัดวาง

ตั้งแต่ Chrome 108 เป็นต้นไป จะไม่มีการใช้งานเครื่องมือเดิมเพื่อทำเลย์เอาต์อีกต่อไป

นอกจากนี้ โครงสร้างข้อมูล LayoutNG ยังรองรับการวาดภาพและการทดสอบ Hit แต่เรายังใช้โครงสร้างข้อมูลเดิมบางอย่างสําหรับ JavaScript API ที่อ่านข้อมูลเลย์เอาต์ เช่น offsetLeft และ offsetTop

การวางแผนทุกอย่างด้วย NG จะช่วยให้สามารถใช้และจัดส่งฟีเจอร์ใหม่ๆ ที่มีเพียงการใช้งาน LayoutNG เท่านั้น (และไม่มีตัวเลือกจากเครื่องมือแบบเดิม) เช่น การค้นหาคอนเทนเนอร์ CSS, การกำหนดตำแหน่ง Anchor, MathML และเลย์เอาต์ที่กำหนดเอง (Houdini) สำหรับคำค้นหาคอนเทนเนอร์ เราได้เปิดตัวไปล่วงหน้าเล็กน้อยพร้อมคำเตือนให้นักพัฒนาแอปทราบว่ายังไม่รองรับการพิมพ์

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

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

การโต้ตอบของเครื่องมือรุ่นเดิม

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

การตรวจหาและการจัดการเครื่องยนต์สำรองเดิม

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

ทางเดินชมต้นไม้ก่อนทาสี

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

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

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

ปัญหาเกี่ยวกับเครื่องมือ Fragment เดิม

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

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

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

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

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

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

ต่อไปนี้คือตัวอย่างที่มี text-shadow

เครื่องมือเดิมจัดการเรื่องนี้ได้ไม่ดีนัก:

เงาข้อความที่ถูกตัดทิ้งไว้ในคอลัมน์ที่ 2

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

ซึ่งควรมีลักษณะดังนี้

ข้อความ 2 คอลัมน์ที่แสดงเงาอย่างถูกต้อง

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

กล่องแบ่งออกเป็น 2 คอลัมน์อย่างไม่ถูกต้อง

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

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

แทนที่จะปล่อยให้ข้อมูลแสดงเกินคอลัมน์แรก (เช่นเดียวกับการแยกส่วนบล็อก LayoutNG)

ALT_TEXT_HERE

เครื่องมือรุ่นเดิมรองรับช่วงพักแบบบังคับ เช่น <div style="break-before:page;"> จะแทรกตัวแบ่งหน้าก่อน DIV แต่รองรับการค้นหาการแบ่งแบบไม่บังคับที่เหมาะสมเพียงบางส่วนเท่านั้น รองรับ break-inside:avoid และบรรทัดแรกและบรรทัดสุดท้าย แต่ไม่รองรับการหลีกเลี่ยงการแบ่งบรรทัดระหว่างบล็อก เช่น หากขอผ่าน break-before:avoid ลองดูตัวอย่างนี้

ข้อความแบ่งออกเป็น 2 คอลัมน์

ตรงนี้ องค์ประกอบ #multicol มีที่ว่าง 5 บรรทัดในแต่ละคอลัมน์ (เพราะสูง 100 พิกเซล และความสูงของบรรทัดเท่ากับ 20 พิกเซล) ดังนั้น #firstchild ทั้งหมดจึงอาจพอดีในคอลัมน์แรก แต่รายการพี่น้อง #secondchild มี break-before:avoid ซึ่งหมายความว่าเนื้อหาไม่ต้องการให้มีช่วงพักระหว่างรายการ เนื่องจากค่าของ widows คือ 2 เราจึงต้องส่ง #firstchild 2 บรรทัดไปยังคอลัมน์ที่ 2 เพื่อดำเนินการตามคำขอหลีกเลี่ยงช่วงพักทั้งหมด Chromium เป็นเครื่องมือของเบราว์เซอร์แรกที่รองรับการผสมผสานฟีเจอร์นี้อย่างเต็มรูปแบบ

วิธีการทำงานของการแยก NG

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

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

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

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

ระบบจะแทรกช่วงพักเมื่อเราไม่มีพื้นที่ Fragmenter (ช่วงพักที่ไม่ได้บังคับใช้) หรือเมื่อมีการขอช่วงพักแบบบังคับ

ข้อกำหนดมีกฎสำหรับการแบ่งบรรทัดที่เหมาะสมที่สุด และการวางการแบ่งบรรทัดตรงที่พื้นที่ไม่เพียงพอนั้นไม่ใช่วิธีที่เหมาะสมเสมอไป เช่น มีพร็อพเพอร์ตี้ CSS ต่างๆ เช่น break-before ที่ส่งผลต่อการเลือกตำแหน่งการแบ่งบรรทัด

ระหว่างจัดเลย์เอาต์ เราจำเป็นต้องติดตามเบรกพอยท์ที่อาจจะดีเพื่อให้สามารถใช้ส่วนข้อกำหนดของช่วงพักโฆษณาที่ไม่ได้บังคับใช้ได้อย่างถูกต้อง ระเบียนนี้หมายความว่าเราสามารถย้อนกลับไปใช้จุดหยุดพักที่ดีที่สุดที่พบล่าสุดได้ หากพื้นที่โฆษณาหมดลง ณ จุดที่เราละเมิดคําขอหลีกเลี่ยงช่วงพัก (เช่น break-before:avoid หรือ orphans:7) จุดหยุดพักที่เป็นไปได้แต่ละจุดจะได้รับคะแนนตั้งแต่ "ใช้เฉพาะในกรณีที่ไม่มีทางเลือกอื่น" ไปจนถึง "จุดพักที่เหมาะที่สุด" โดยมีค่าบางอย่างอยู่ตรงกลาง หากตำแหน่งหยุดพักได้คะแนนเป็น "ยอดเยี่ยม" หมายความว่าจะไม่ละเมิดกฎการหยุดพักหากเราหยุดพักที่จุดนั้น (และหากได้คะแนนนี้ตรงจุดที่เรามีพื้นที่ไม่เพียงพอแล้ว ก็ไม่จำเป็นต้องมองหาจุดอื่นที่ดีกว่า) หากคะแนนเป็น "ทางเลือกสุดท้าย" แสดงว่าจุดหยุดพักนั้นไม่ถูกต้อง แต่เราอาจยังหยุดพักที่จุดนั้นหากไม่พบจุดที่ดีกว่า เพื่อหลีกเลี่ยงการประมวลผลรายการย่อยที่มากเกินไป

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

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

ในกรณีนี้ เรามีจำนวนช่องว่างไม่เพียงพอก่อน #second แต่มี "break-before:avoid" ซึ่งได้คะแนนตำแหน่งการแบ่งบรรทัดเป็น "ละเมิดการหลีกเลี่ยงการแบ่งบรรทัด" เมื่อถึงจุดนั้น เราจะมีเชน NGEarlyBreak ของ "inside #outer > inside #middle > inside #inner > before "line 3"' พร้อม "perfect" เราจึงควรหยุดพักที่นั่น ดังนั้นเราต้องกลับไปและเรียกใช้เลย์เอาต์อีกครั้งตั้งแต่ต้นของ #outer (และครั้งนี้ให้ส่ง NGEarlyBreak ที่พบ) เพื่อให้เราหยุดก่อน "บรรทัดที่ 3" ใน #inner ได้ (เราแบ่งบรรทัดก่อน "บรรทัด 3" เพื่อให้บรรทัดที่เหลือ 4 บรรทัดไปอยู่ในตัวแบ่งกลุ่มถัดไป และเพื่อรักษา widows:4)

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

ด้วยเหตุนี้ เราจึงจำเป็นต้องละเมิดคำขอหลีกเลี่ยงช่วงพักบางรายการในบางครั้ง หากจะช่วยหลีกเลี่ยงการล้นของ Fragmentainer ได้ เช่น

ในกรณีนี้ พื้นที่ไม่เพียงพอก่อน #second แต่มี "break-before:avoid" ซึ่งจะแปลเป็น "ละเมิดการเลี่ยงช่วงพัก" เหมือนตัวอย่างสุดท้าย นอกจากนี้ เรายังมี NGEarlyBreak ที่มี "การละเมิดบรรทัดแรกและบรรทัดสุดท้าย" (ภายใน #first > ก่อน "บรรทัด 2") ซึ่งยังคงไม่สมบูรณ์แบบ แต่ดีกว่า "การละเมิดการหลีกเลี่ยงการแบ่งบรรทัด" ดังนั้นเราจะหยุดพักก่อน "บรรทัดที่ 2" ซึ่งละเมิดนโยบายคำขอของเด็กกำพร้า / แม่ม่าย ข้อกำหนดจะกล่าวถึงเรื่องนี้ใน 4.4 การแบ่งที่ไม่บังคับ ซึ่งจะกำหนดว่าระบบจะละเว้นกฎการแบ่งบรรทัดใดก่อน หากเรามีจุดหยุดพักไม่เพียงพอที่จะหลีกเลี่ยงการแยกออกเป็นหลายส่วน

บทสรุป

เป้าหมายด้านฟังก์ชันการทำงานของโปรเจ็กต์การแยกส่วนบล็อก LayoutNG คือเพื่อให้การติดตั้งใช้งานที่รองรับสถาปัตยกรรม LayoutNG ของทุกอย่างที่เครื่องมือเดิมรองรับ และดำเนินการอื่นๆ น้อยที่สุดเท่าที่จะเป็นไปได้ นอกเหนือจากการแก้ไขข้อบกพร่อง ข้อยกเว้นหลักคือการรองรับการหลีกเลี่ยงช่วงพักที่ดีกว่า (เช่น break-before:avoid) เนื่องจากนี่เป็นส่วนหลักของเครื่องมือ Fragment จึงควรอยู่ในแพลตฟอร์มนี้ตั้งแต่เริ่มต้น เนื่องจากการเพิ่มในภายหลังจะหมายถึงการเขียนซ้ำอีก

ตอนนี้การแยกส่วนบล็อก LayoutNG เสร็จแล้ว เราจึงเริ่มเพิ่มฟังก์ชันการทำงานใหม่ๆ ได้ เช่น การรองรับขนาดหน้าเว็บแบบผสมเมื่อพิมพ์ @pageกล่องระยะขอบเมื่อพิมพ์ box-decoration-break:clone และอื่นๆ และเช่นเดียวกับ LayoutNG โดยทั่วไป เราคาดหวังว่าอัตราข้อบกพร่องและภาระในการบำรุงรักษาระบบใหม่จะลดลงอย่างมากเมื่อเวลาผ่านไป

ขอขอบคุณ

  • Una Kravets สำหรับ "ภาพหน้าจอที่วาดด้วยมือ" ที่ยอดเยี่ยม
  • Chris Harrelson สำหรับการตรวจทาน ความคิดเห็น และคำแนะนำ
  • Philip Jägenstedt สำหรับความคิดเห็นและคำแนะนำ
  • Rachel Andrew สำหรับการดูแลแก้ไขและรูปภาพตัวอย่างแบบหลายคอลัมน์รูปแรก