TL;DR: ใช้องค์ประกอบ DOM ซ้ำและนำองค์ประกอบที่อยู่ห่างจากวิวพอร์ตออก ใช้ตัวยึดตําแหน่งเพื่อพิจารณาข้อมูลที่มีการเลื่อนเวลา ต่อไปนี้คือตัวอย่างและโค้ดสําหรับแถบเลื่อนแบบไม่สิ้นสุด
ฟีเจอร์การเลื่อนได้ไม่รู้จบปรากฏขึ้นทั่วอินเทอร์เน็ต รายการศิลปินของ Google Music รายการเดียว ไทม์ไลน์ของ Facebook รายการเดียว และฟีดการถ่ายทอดสดของ Twitter รายการเดียว คุณเลื่อนลงและก่อนที่คุณจะถึงด้านล่าง เนื้อหาใหม่ก็ปรากฏขึ้นราวกับว่ามาจากไหนก็ไม่รู้ ผู้ใช้จะได้รับประสบการณ์การใช้งานที่ราบรื่นและเห็นความน่าสนใจได้ง่ายๆ
อย่างไรก็ตาม ปัญหาทางเทคนิคที่อยู่เบื้องหลังแถบเลื่อนแบบอินฟินิตี้นั้นยากกว่าที่คิด ปัญหาที่คุณพบเมื่อต้องการทําในสิ่งที่ถูกต้อง™มีมากมายมหาศาล โดยเริ่มจากสิ่งเล็กๆ น้อยๆ เช่น ลิงก์ในท้ายหน้าแทบจะเข้าถึงไม่ได้เนื่องจากเนื้อหาดันส่วนท้ายหน้าออกไปเรื่อยๆ แต่ปัญหาจะยากขึ้น คุณจัดการเหตุการณ์การปรับขนาดอย่างไรเมื่อมีคนหมุนโทรศัพท์จากแนวตั้งเป็นแนวนอน หรือคุณป้องกันไม่ให้โทรศัพท์ทำงานช้าลงจนหยุดชะงักเมื่อรายการมีความยาวเกินไปได้อย่างไร
The right thing™
เราจึงคิดว่านี่เป็นเหตุผลเพียงพอที่จะสร้างการใช้งานอ้างอิงซึ่งแสดงวิธีจัดการปัญหาเหล่านี้ทั้งหมดด้วยวิธีที่นํากลับมาใช้ซ้ำได้ พร้อมกับรักษามาตรฐานประสิทธิภาพไว้
เราจะใช้เทคนิค 3 อย่างเพื่อให้บรรลุเป้าหมาย ได้แก่ การรีไซเคิล DOM, รายการที่ลบไปแล้ว และจุดยึดการเลื่อน
กรณีสาธิตของเราจะเป็นหน้าต่างแชทที่มีลักษณะคล้ายกับ Hangouts ซึ่งเราเลื่อนดูข้อความได้ สิ่งแรกที่เราต้องมีคือแหล่งที่มาของข้อความแชทที่ไม่มีวันหมด ในทางเทคนิคแล้ว แถบเลื่อนแบบเลื่อนได้ไม่จำกัดไม่มีทางเลื่อนได้ไม่จำกัดจริงๆ แต่ด้วยปริมาณข้อมูลที่พร้อมจะส่งไปยังแถบเลื่อนเหล่านี้ ก็อาจทำให้ดูเหมือนว่าเลื่อนได้ไม่จำกัด เพื่อความสะดวก เราจะเขียนโค้ดชุดข้อความแชทไว้ล่วงหน้า แล้วเลือกข้อความ ผู้เขียน และไฟล์แนบรูปภาพเป็นครั้งคราวแบบสุ่ม โดยเพิ่มการหน่วงเวลาเทียมเล็กน้อยเพื่อให้มีลักษณะการทำงานคล้ายกับเครือข่ายจริงมากขึ้น

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

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

หากปรับขนาดวิวพอร์ตและรันเวย์มีการเปลี่ยนแปลง เราจะคืนค่าสถานการณ์ที่ผู้ใช้เห็นภาพเหมือนกันได้ ชนะ ยกเว้นหน้าต่างที่มีการปรับขนาด ซึ่งหมายความว่าแต่ละรายการอาจมีการเปลี่ยนแปลงความสูง ดังนั้นเราจะรู้ได้อย่างไรว่าควรวางเนื้อหาที่ยึดไว้ไว้ที่ด้านล่างสุด ไม่ได้ หากต้องการทราบ เราต้องจัดวางองค์ประกอบทุกรายการเหนือรายการที่ยึดไว้และบวกความสูงทั้งหมดขององค์ประกอบเหล่านั้น ซึ่งอาจทำให้ระบบหยุดชั่วคราวเป็นเวลานานหลังจากปรับขนาด และเราไม่ต้องการให้เป็นเช่นนั้น แต่เราจะถือว่ารายการทั้งหมดด้านบนมีขนาดเท่ากันกับรายการที่ตายแล้ว และปรับตำแหน่งการเลื่อนของเราตามความเหมาะสม เมื่อเลื่อนองค์ประกอบไปยังรันเวย์ เราจะปรับตำแหน่งการเลื่อน ซึ่งจะช่วยเลื่อนเวลาการจัดวางไปจนกว่าจะถึงเวลาที่จําเป็นจริงๆ
เลย์เอาต์
เราข้ามรายละเอียดที่สำคัญไป นั่นคือเลย์เอาต์ การรีไซเคิลองค์ประกอบ DOM แต่ละครั้งตามปกติจะจัดวางเลย์เอาต์ใหม่ทั้งหมด ซึ่งจะทำให้เฟรมต่อวินาทีต่ำกว่าเป้าหมายที่ 60 เฟรมต่อวินาที เพื่อหลีกเลี่ยงปัญหานี้ เราจึงรับภาระการจัดวางไว้เองและใช้องค์ประกอบที่มีตำแหน่งแบบสัมบูรณ์กับการเปลี่ยนรูปแบบ วิธีนี้ช่วยให้เราจินตนาการได้ว่าองค์ประกอบทั้งหมดที่อยู่ไกลออกไปบนรันเวย์ยังคงใช้พื้นที่อยู่ แม้ว่าในความเป็นจริงแล้วจะมีเพียงพื้นที่ว่าง เนื่องจากเราเป็นผู้วางเลย์เอาต์เอง เราจึงแคชตําแหน่งที่แต่ละรายการปรากฏขึ้นได้ และสามารถโหลดองค์ประกอบที่ถูกต้องจากแคชได้ทันทีเมื่อผู้ใช้เลื่อนย้อนกลับ
ตามหลักการแล้ว รายการจะได้รับการวาดภาพอีกครั้งเพียงครั้งเดียวเมื่อแนบกับ DOM และจะไม่ได้รับผลกระทบจากการเพิ่มหรือนำรายการอื่นๆ ในรันเวย์ออก เป็นไปได้ แต่ใช้ได้กับเบราว์เซอร์สมัยใหม่เท่านั้น
การปรับแต่งสุดล้ำ
เมื่อเร็วๆ นี้ Chrome ได้เพิ่มการรองรับการจำกัดเขต CSS ซึ่งเป็นฟีเจอร์ที่ช่วยให้เรานักพัฒนาแอปบอกเบราว์เซอร์ได้ว่าองค์ประกอบใดเป็นขอบเขตของเลย์เอาต์และงานเพนต์ เนื่องจากเราจัดเลย์เอาต์เองที่นี่ จึงเหมาะอย่างยิ่งสำหรับการควบคุม เมื่อใดก็ตามที่เราเพิ่มองค์ประกอบลงในรันเวย์ เราทราบว่ารายการอื่นๆ ไม่จำเป็นต้องได้รับผลกระทบจากการวางเลย์เอาต์ใหม่ ดังนั้นแต่ละรายการควรได้รับ contain: layout
นอกจากนี้ เรายังไม่ต้องการให้ส่งผลต่อส่วนอื่นๆ ของเว็บไซต์ด้วย ดังนั้นรันเวย์เองก็ควรได้รับคำสั่งสไตล์นี้ด้วย
อีกสิ่งที่เราพิจารณาคือการใช้ IntersectionObservers
เป็นกลไกในการตรวจจับเมื่อผู้ใช้เลื่อนไปไกลพอที่เราจะเริ่มต้นรีไซเคิลองค์ประกอบและโหลดข้อมูลใหม่ อย่างไรก็ตาม IntersectionObservers มีการระบุไว้ว่ามีความล่าช้าสูง (เหมือนกับการใช้ requestIdleCallback
) เราจึงอาจรู้สึกว่า IntersectionObservers ตอบสนองช้ากว่าที่ไม่ได้ใช้ แม้แต่การใช้งานปัจจุบันของเราที่ใช้เหตุการณ์ scroll
ก็มีปัญหานี้เช่นกัน เนื่องจากระบบจะส่งเหตุการณ์การเลื่อนตาม "ความพยายามที่ดีที่สุด" ในที่สุด Worklet ของ Compositor ใน Houdini จะกลายเป็นโซลูชันที่มีประสิทธิภาพสูงในการแก้ปัญหานี้
แต่ก็ยังไม่ใช่วิธีที่สมบูรณ์แบบ
การใช้งานการรีไซเคิล DOM ในปัจจุบันของเรานั้นไม่เหมาะอย่างยิ่งเนื่องจากจะเพิ่มองค์ประกอบทั้งหมดที่ผ่านวิวพอร์ตแทนที่จะสนใจเฉพาะองค์ประกอบที่แสดงบนหน้าจอจริงๆ ซึ่งหมายความว่าเมื่อคุณเลื่อนเร็วมาก คุณทำให้ Chrome ทำงานหนักมากจนตามไม่ทัน คุณจะเห็นแต่พื้นหลัง คะแนนนี้ไม่ใช่คะแนนที่แย่ที่สุด แต่ก็เป็นสิ่งที่ต้องปรับปรุง
เราหวังว่าคุณจะเห็นว่าปัญหาง่ายๆ นั้นอาจเป็นเรื่องยากเพียงใดเมื่อคุณต้องการรวมประสบการณ์การใช้งานที่ยอดเยี่ยมเข้ากับมาตรฐานประสิทธิภาพสูง เมื่อ Progressive Web App กลายเป็นประสบการณ์การใช้งานหลักบนโทรศัพท์มือถือ เรื่องนี้จะยิ่งมีความสำคัญมากขึ้น และนักพัฒนาเว็บจะต้องลงทุนอย่างต่อเนื่องในการใช้รูปแบบที่คำนึงถึงข้อจำกัดด้านประสิทธิภาพ
โค้ดทั้งหมดอยู่ในที่เก็บข้อมูล เราพยายามอย่างเต็มที่เพื่อให้นํากลับมาใช้ใหม่ได้ แต่จะไม่ได้เผยแพร่เป็นไลบรารีจริงใน npm หรือเป็นรีโปแยกต่างหาก การใช้งานหลักคือด้านการศึกษา