การตรวจจับการเปลี่ยนแปลงเชิงมุมและกลยุทธ์ OnPush

เผยแพร่แล้ว: 2022-03-11

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

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

การตรวจจับการเปลี่ยนแปลงเชิงมุมและกลยุทธ์ OnPush

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

เปลี่ยนการตรวจจับในเชิงมุม

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

โมเดลใน Angular สามารถเปลี่ยนแปลงได้เนื่องจากสถานการณ์ต่อไปนี้:

  • เหตุการณ์ DOM (คลิก วางเมาส์เหนือ ฯลฯ)

  • คำขอ AJAX

  • ตัวจับเวลา (setTimer(), setInterval())

เปลี่ยนเครื่องตรวจจับ

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

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

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

เปลี่ยนลำดับชั้นของตัวตรวจจับ

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

หาก Angular ได้รับรายงานจากตัวตรวจจับการเปลี่ยนแปลง จะสั่งให้ส่วนประกอบที่เกี่ยวข้องแสดงผลซ้ำและอัปเดต DOM ตามนั้น

เปลี่ยนกลยุทธ์การตรวจจับ

มูลค่าเทียบกับประเภทอ้างอิง

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

ในการเริ่มต้น มาทบทวนประเภทค่าและประเภทอ้างอิงและการจัดประเภทกัน

ประเภทค่า

  • บูลีน

  • โมฆะ

  • ไม่ได้กำหนด

  • ตัวเลข

  • สตริง

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

หน่วยความจำกอง

ประเภทอ้างอิง

  • อาร์เรย์

  • วัตถุ

  • ฟังก์ชั่น

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

หน่วยความจำสแต็คและหน่วยความจำฮีป

ความแตกต่างที่สำคัญระหว่างประเภทค่าและประเภทอ้างอิงคือ ในการอ่านค่าของประเภทค่า เราแค่ต้องสืบค้นหน่วยความจำสแต็ก แต่เพื่อที่จะอ่านค่าของประเภทอ้างอิง เราต้องมาก่อน สืบค้นหน่วยความจำสแต็กเพื่อรับการอ้างอิง จากนั้นใช้การอ้างอิงนั้นในลำดับที่สองเพื่อค้นหาหน่วยความจำฮีปเพื่อค้นหาค่าของประเภทการอ้างอิง

กลยุทธ์เริ่มต้น

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

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

กลยุทธ์ OnPush

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

กลยุทธ์ OnPush โดยทั่วไปจะถามคำถามสองข้อแทนที่จะเป็นคำถามเดียว การอ้างอิงของประเภทการอ้างอิงมีการเปลี่ยนแปลงหรือไม่? ถ้าใช่ แสดงว่าค่าในหน่วยความจำฮีพเปลี่ยนไปหรือไม่

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

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

การปฏิบัติต่อวัตถุที่เปลี่ยนแปลงได้:

 static mutable() { var before = {foo: "bar"}; var current = before; current.foo = "hello"; console.log(before === current); // => true }

การปฏิบัติต่อวัตถุที่ไม่เปลี่ยนรูป:

 static mutable() { var before = {foo: "bar"}; var current = before; current = {foo "hello"}; console.log(before === current); // => false }

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

ดังนั้นคุณจะใช้กลยุทธ์ OnPush สำหรับส่วนประกอบอย่างไร สิ่งที่คุณต้องทำคือเพิ่มพารามิเตอร์ changeDetection ในคำอธิบายประกอบ @Component

 import {ChangeDetectionStrategy, Component} from '@angular/core'; @Component({ // ... changeDetection: ChangeDetectionStrategy.OnPush }) export class OnPushComponent { // ... }

ไม่เปลี่ยนรูป.js

เป็นความคิดที่ดีที่จะบังคับใช้ความไม่เปลี่ยนรูปหากตัดสินใจใช้กลยุทธ์ OnPush บนองค์ประกอบเชิงมุม นั่นคือที่มาของ Immutable.js

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

ในการเพิ่ม Immutable.js ให้กับโปรเจ็กต์ของคุณ โปรดตรวจสอบให้แน่ใจว่าได้เข้าไปในเทอร์มินัลของคุณและรัน:

 $ npm install immutable --save

ตรวจสอบให้แน่ใจว่าได้นำเข้าโครงสร้างข้อมูลที่คุณใช้จาก Immutable.js ในส่วนประกอบที่คุณใช้อยู่

 import {Map, List} from 'immutable';

นี่คือวิธีการใช้แผนที่ Immutable.js:

 var foobar = {foo: "bar"}; var immutableFoobar = Map(foobar); console.log(immutableFooter.get("foo")); // => bar

และสามารถใช้อาร์เรย์ได้:

 var helloWorld = ["Hello", "World!"]; var immutableHelloWorld = List(helloWorld); console.log(immutableHelloWorld.first()); // => Hello console.log(immutableHelloWorld.last()); // => World! helloWorld.push("Hello Mars!"); console.log(immutableHelloWorld.last()); // => Hello Mars!

ข้อเสียของการใช้ Immutable.js

มีข้อเสียเปรียบหลักสองสามประการของการใช้ Immutable.js

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

สรุป

คุณอาจจะถามว่าทำไมกลยุทธ์ OnPush ไม่ใช่กลยุทธ์เริ่มต้นสำหรับ Angular ฉันคิดว่าคงเป็นเพราะ Angular ไม่ต้องการบังคับให้นักพัฒนา JavaScript ทำงานกับวัตถุที่ไม่เปลี่ยนรูป แต่นั่นไม่ได้หมายความว่าคุณไม่ได้รับอนุญาตให้ใช้

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