ความซับซ้อนของตัวเลื่อนได้ไม่รู้จบ

TL;DR: ใช้องค์ประกอบ DOM อีกครั้งและนำองค์ประกอบที่ห่างจาก วิวพอร์ต ใช้ตัวยึดตำแหน่งเพื่อพิจารณาข้อมูลที่ล่าช้า ต่อไปนี้คือ demo และโค้ดสำหรับค่าอนันต์ แถบเลื่อน

เครื่องมือเลื่อนได้ไม่รู้จบปรากฏขึ้นทั่วอินเทอร์เน็ต รายชื่อศิลปินของ Google Music คือ 1. ไทม์ไลน์ของ Facebook มีลักษณะหนึ่ง และฟีดสดของ Twitter ก็เหมือนกัน คุณ เลื่อนลงมาก่อนที่จะถึงด้านล่าง เนื้อหาใหม่ก็จะปรากฏขึ้นมาอย่างน่ามหัศจรรย์ ดูเหมือนจะออกมาจากที่อื่นด้วย ถือเป็นประสบการณ์ที่ราบรื่นสำหรับผู้ใช้และ ดูการอุทธรณ์

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

Theที่เหมาะสมTM

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

เราจะใช้ 3 เทคนิคเพื่อบรรลุเป้าหมายของเรา ได้แก่ การรีไซเคิล DOM, การตีหลุมฝังศพ และการเลื่อนแท็ก Anchor

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

ภาพหน้าจอของแอป Chat

การรีไซเคิล DOM

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

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

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

รันเวย์ Sentinel วิวพอร์ต

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

Tombstone

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

เช่น
หลุมฝังศพ หินมาก ว้าว

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

การยึดกับการเลื่อน

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

เลื่อนแผนภาพการยึดตำแหน่ง

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

เลย์เอาต์

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

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

การปรับแต่งขอบเลือด

เมื่อเร็วๆ นี้ Chrome ได้เพิ่มการรองรับ CSS Containment ซึ่งเป็นฟีเจอร์ ที่ทำให้นักพัฒนาซอฟต์แวร์สามารถบอกเบราว์เซอร์ว่าองค์ประกอบใดเป็นขอบเขตสำหรับ การออกแบบและระบายสี เนื่องจากเรากำลังทำเลย์เอาต์ตรงนี้เอง จึงเป็นโอกาสที่ดีที่สุด ในการกักเก็บ เมื่อใดก็ตามที่เราเพิ่มองค์ประกอบลงในรันเวย์ เราทราบ รายการอื่นๆ ไม่จำเป็นต้องได้รับผลกระทบจากการส่งต่อ ดังนั้นแต่ละรายการควร รับ contain: layout และเราไม่อยากให้การเปลี่ยนแปลงอื่นๆ บนเว็บไซต์ ดังนั้นรันเวย์ก็ควรได้รับสไตล์นี้เช่นกัน

อีกสิ่งหนึ่งที่เราคำนึงถึงคือการใช้ IntersectionObservers เป็นกลไกในการตรวจหาเมื่อ ผู้ใช้เลื่อนได้ไกลพอที่จะเริ่มรีไซเคิลองค์ประกอบและโหลดองค์ประกอบใหม่ๆ แต่ IntersectionObservers ถูกระบุว่าใช้เวลาในการตอบสนองสูง (ราวกับว่า ที่ใช้ requestIdleCallback) เราจึงอาจรู้สึกว่าตอบสนองต่อ IntersectionObservers มากกว่าที่ไม่มี แม้แต่การติดตั้งในปัจจุบันของเราโดยใช้ scroll ได้รับผลกระทบจากปัญหานี้ เนื่องจากระบบจะส่งเหตุการณ์การเลื่อนใน "พยายามอย่างเต็มที่" ในที่สุดแล้ว Houdini’s Compositor Worklet จะเป็นโซลูชันความแม่นยำสูงสำหรับปัญหานี้

ก็ยังไม่สมบูรณ์แบบ

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

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

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