การตรวจจับการเปลี่ยนแปลงเชิงมุมและกลยุทธ์ OnPush
เผยแพร่แล้ว: 2022-03-11คุณได้เริ่มใช้ Angular กับโปรเจ็กต์โปรดทั้งหมดของคุณแล้ว คุณรู้ว่า Angular นำเสนออะไร และคุณจะใช้ประโยชน์จากมันเพื่อสร้างเว็บแอปที่น่าทึ่งได้อย่างไร แต่มีบางสิ่งเกี่ยวกับ Angular และการรู้ว่าสิ่งเหล่านี้สามารถช่วยให้คุณใช้ Angular สำหรับโครงการของคุณได้ดีขึ้น
การไหลของข้อมูลเป็นศูนย์กลางของเกือบทุกอย่างในเชิงมุม การตรวจจับการเปลี่ยนแปลงเป็นสิ่งที่ควรค่าแก่การรู้ เนื่องจากจะช่วยให้คุณติดตามจุดบกพร่องได้ง่ายขึ้นมากและให้โอกาสในการเพิ่มประสิทธิภาพแอปของคุณเมื่อทำงานกับชุดข้อมูลที่ซับซ้อน
ในบทความนี้ คุณจะได้เรียนรู้วิธีที่ 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 ทำให้การเปลี่ยนไปใช้กลยุทธ์การตรวจจับการเปลี่ยนแปลงอื่นทำได้ง่ายเพียงใด