AngularJS: ความแตกต่างระหว่างวิธี $ observ และ $ watch


378

ฉันรู้ว่าทั้งสองWatchersและObserversถูกคำนวณทันทีที่มี$scopeการเปลี่ยนแปลงใน AngularJS แต่ไม่เข้าใจว่าอะไรคือความแตกต่างระหว่างสองอย่างนี้

ความเข้าใจเบื้องต้นของฉันคือการObserversคำนวณสำหรับการแสดงออกเชิงมุมซึ่งเป็นเงื่อนไขในด้าน HTML ที่Watchersดำเนินการเมื่อ$scope.$watch()มีการดำเนินการฟังก์ชั่น ฉันกำลังคิดอย่างถูกต้องหรือไม่


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

@smalone มีการเปลี่ยนแปลง ขอขอบคุณและขออภัย!
Abilash

👍ไม่ต้องกังวล ขอบคุณสำหรับการแก้ไข
smalone

คำตอบ:


608

$ observ ()เป็นวิธีการในวัตถุคุณสมบัติและเป็นเช่นนั้นมันสามารถใช้ในการสังเกต / ดูการเปลี่ยนแปลงค่าของแอตทริบิวต์ DOM มันใช้ / เรียกภายในคำสั่งเท่านั้น ใช้ $ observ เมื่อคุณต้องการสังเกต / ดูแอตทริบิวต์ DOM ที่มีการแก้ไข (เช่น {{}})
เช่นattr1="Name: {{name}}"จากนั้นในคำสั่ง:attrs.$observe('attr1', ...).
(หากคุณลองscope.$watch(attrs.attr1, ...)ใช้งานจะไม่ทำงานเพราะ {{}} s - คุณจะได้รับundefined) ใช้ $ watch สำหรับทุกอย่าง

$ watch ()ซับซ้อนกว่า มันสามารถสังเกต / ดู "การแสดงออก" ซึ่งการแสดงออกสามารถเป็นได้ทั้งฟังก์ชั่นหรือสตริง ถ้านิพจน์เป็นสตริงมันจะเป็น $ parse 'd (เช่นประเมินว่าเป็นนิพจน์เชิงมุม ) ในฟังก์ชัน (เป็นฟังก์ชันนี้ที่เรียกว่าทุกรอบการย่อย) การแสดงออกของสตริงไม่สามารถมี {{}} $ watch เป็นวิธีการในวัตถุขอบเขตดังนั้นจึงสามารถใช้ / เรียกได้ทุกที่ที่คุณสามารถเข้าถึงวัตถุขอบเขตได้

  • ตัวควบคุม - ตัวควบคุมใด ๆ - ตัวควบคุมที่สร้างขึ้นผ่าน ng-view, ng-controller หรือตัวควบคุมคำสั่ง
  • ฟังก์ชั่นการเชื่อมโยงในคำสั่งเนื่องจากมีการเข้าถึงขอบเขตเช่นกัน

เนื่องจากมีการประเมินสตริงเป็นนิพจน์เชิงมุมดังนั้น $ watch จึงมักถูกใช้เมื่อคุณต้องการสังเกต / ดูคุณสมบัติโมเดล / ขอบเขต เช่น, attr1="myModel.some_prop"จากนั้นในฟังก์ชั่นควบคุมหรือลิงค์: scope.$watch('myModel.some_prop', ...)หรือscope.$watch(attrs.attr1, ...)(หรือscope.$watch(attrs['attr1'], ...))
(ถ้าคุณลองattrs.$observe('attr1')คุณจะได้รับสตริงmyModel.some_propซึ่งอาจไม่ใช่สิ่งที่คุณต้องการ)

ตามที่กล่าวไว้ในความเห็นเกี่ยวกับคำตอบ @ PrimosK ของ $ ทุกสังเกตและ $ นาฬิกามีการตรวจสอบทุกแยกแยะวงจร

คำสั่งที่มีขอบเขตแยกซับซ้อนกว่า หากมีการใช้ไวยากรณ์ '@' คุณสามารถ $ สังเกตหรือ $ ดูแอตทริบิวต์ DOM ที่มีการแก้ไข (เช่น {{}}) (เหตุผลที่ทำงานกับ $ watch นั้นเป็นเพราะไวยากรณ์ '@' ทำการแก้ไขให้เราดังนั้น $ watch จะเห็นสตริงที่ไม่มี {{}}) เพื่อให้ง่ายต่อการจดจำว่าจะใช้เมื่อใดฉันขอแนะนำให้ใช้ $ สังเกตสำหรับกรณีนี้ด้วย

เพื่อช่วยในการทดสอบทั้งหมดนี้ฉันได้เขียนPlunkerที่กำหนดสองแนวทาง หนึ่ง ( d1) ไม่สร้างขอบเขตใหม่อีกอัน ( d2) สร้างขอบเขตแยก แต่ละคำสั่งมีคุณลักษณะหกประการ แต่ละแอตทริบิวต์มีทั้ง $ observ'd และ $ watch'ed

<div d1 attr1="{{prop1}}-test" attr2="prop2" attr3="33" attr4="'a_string'"
        attr5="a_string" attr6="{{1+aNumber}}"></div>

ดูบันทึกคอนโซลเพื่อดูความแตกต่างระหว่าง $ observ และ $ watch ในฟังก์ชันการลิงก์ จากนั้นคลิกลิงก์และดูว่า $ observes และ $ watches ใดถูกเรียกใช้โดยการเปลี่ยนแปลงคุณสมบัติที่ทำโดยตัวจัดการคลิก

โปรดสังเกตว่าเมื่อฟังก์ชันลิงก์ทำงานคุณลักษณะใด ๆ ที่มี {{}} ยังไม่ได้รับการประเมิน (ดังนั้นหากคุณลองตรวจสอบคุณสมบัติคุณจะได้รับundefined) วิธีเดียวที่จะเห็นค่าที่ถูกแก้ไขคือการใช้ $ observ (หรือ $ watch หากใช้ขอบเขต isolate ด้วย '@') ดังนั้นการรับค่าของแอ็ตทริบิวต์เหล่านี้เป็นการดำเนินการแบบอะซิงโครนัส (และนี่คือเหตุผลที่เราต้องการฟังก์ชัน $ observ และ $ watch)

บางครั้งคุณไม่ต้องการ $ observ หรือ $ watch เช่นถ้าแอตทริบิวต์ของคุณมีตัวเลขหรือบูลีน (ไม่ใช่สตริง) เพียงประเมินครั้งเดียว: แล้วในการพูดการฟังก์ชั่นการเชื่อมโยงของคุณ:attr1="22" var count = scope.$eval(attrs.attr1)หากเป็นเพียงสตริงคงที่ - attr1="my string"- ให้ใช้attrs.attr1ในคำสั่งของคุณ (ไม่จำเป็นต้องใช้ $ eval ())

ดูเพิ่มเติมที่Google กลุ่ม Vojta โพสต์เกี่ยวกับการแสดงออกของ $ watch


13
คำอธิบายที่ดี! +1
PrimosK

4
คำตอบที่ดี! คุณมีความคิดว่าทำไมng-src/ng-hrefใช้attr.$observeแทนscope.$watchแล้ว?
okm

4
+1 สำหรับ AngularJS Pope! ทุกครั้งที่ฉันค้นหาข้อมูลบางอย่างเกี่ยวกับปัญหาเชิงมุมล่าสุดของฉันฉันต้องอ่านคำตอบที่ได้รับการยอมรับ @MarkRajcok
GFoley83

1
ขอบคุณสำหรับการโพสต์ที่ดี ขอบเขต $ eval (รายการ) มีประโยชน์จริงๆ หากรายการเป็นสตริง json มันจะแปลงเป็นวัตถุ json
bnguyen82

5
@tamakisquare พวกมันสามารถใช้แทนกันได้เมื่อใช้@ไวยากรณ์ ฉันเชื่อว่าไม่มีความแตกต่างด้านประสิทธิภาพ (แต่ฉันไม่ได้ดูซอร์สโค้ดจริง)
Mark Rajcok

25

ถ้าผมเข้าใจคำถามของคุณได้คุณจะถามว่าอะไรคือความแตกต่างถ้าคุณลงทะเบียนโทรกลับฟังด้วยหรือถ้าคุณทำมันด้วย$watch$observe

โทรกลับ registerd ด้วย$watchถูก$digestดำเนินการเมื่อมีการดำเนินการ

การโทรกลับที่ลงทะเบียนด้วย$observeจะถูกเรียกเมื่อการเปลี่ยนแปลงค่าของแอตทริบิวต์ที่มีการแก้ไข (เช่นattr="{{notJetInterpolated}}")


ภายในคำสั่งคุณสามารถใช้ทั้งสองอย่างในลักษณะที่คล้ายกันมาก:

    attrs.$observe('attrYouWatch', function() {
         // body
    });

หรือ

    scope.$watch(attrs['attrYouWatch'], function() {
         // body
    });

3
ที่จริงตั้งแต่การเปลี่ยนแปลงทุกครั้งที่ได้รับการสะท้อนให้เห็นใน$digestเฟสมันปลอดภัยที่จะคิดว่าการเรียกกลับจะถูกเรียกใน$observe $digestและการ$watchโทรกลับจะถูกเรียกด้วย$digestแต่เมื่อใดก็ตามที่มีการเปลี่ยนแปลงค่า ฉันคิดว่าพวกเขาทำงานเดียวกันแน่นอน: "ดูการแสดงออกโทรกลับการเปลี่ยนแปลงค่า" ความแตกต่างของคำหลักอาจเป็นเพียงน้ำตาลเชิงซ้อนเพื่อไม่ให้นักพัฒนาสับสน
อายุKontacı

1
@ fastreload ฉันเห็นด้วยอย่างยิ่งกับความคิดเห็นของคุณ .. เขียนอย่างดี!
PrimosK

@ fastreload ... ขอบคุณสำหรับคำอธิบายที่ยอดเยี่ยม หากฉันเข้าใจถูกต้องผู้สังเกตการณ์จะใช้สำหรับการแสดงออกเชิงมุม ฉันถูกไหม?
Abilash

@PrimosK: เพิ่มคุณสำหรับความคิดเห็นก่อนหน้าของฉัน
Abilash

2
@Abilash ผู้สังเกตการณ์มีไว้สำหรับดูแอตทริบิวต์ dom ไม่ใช่เพียงการแสดงออก ดังนั้นหากคุณเปลี่ยนค่าแอตทริบิวต์ด้วยตัวเองมันจะแสดงให้เห็นในรอบย่อยถัดไป
อายุKontacı

1

ฉันคิดว่านี่ค่อนข้างชัดเจน:

  • $ สังเกตใช้ในการเชื่อมโยงการทำงานของคำสั่ง
  • $ watch ใช้ในขอบเขตเพื่อดูการเปลี่ยนแปลงค่าใด ๆ

โปรดทราบ : ทั้งฟังก์ชั่นมีสองข้อโต้แย้ง

$observe/$watch(value : string, callback : function);
  • value : มักจะเป็นการอ้างอิงสตริงไปยังองค์ประกอบที่เฝ้าดู (ชื่อของตัวแปรขอบเขตหรือชื่อของแอตทริบิวต์ของคำสั่งที่จะดู)
  • โทรกลับ : ฟังก์ชั่นที่จะดำเนินการของแบบฟอร์มfunction (oldValue, newValue)

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


2
มันค่อนข้างชัดเจนเกี่ยวกับประเพณีของมัน แต่ทำไมเป็นคำถาม มาร์กสรุปได้อย่างสวยงาม
Abilash

3
ฉันคิดว่า params อาจถูกสับเปลี่ยน - ดูเหมือนว่าจะผ่าน newValue จากนั้น oldValue เป็น attrs. $ observ () . .
blaster

0

ทำไม $ สังเกตถึงต่างจาก $ watch

watchExpression ได้รับการประเมินและเปรียบเทียบกับค่าก่อนหน้าแต่ละรอบการสรุป () หากมีการเปลี่ยนแปลงในค่า watchExpression ฟังก์ชันนาฬิกาจะถูกเรียก

$ สังเกตนั้นมีความเฉพาะกับการเฝ้าดูค่า interpolated หากค่าแอตทริบิวต์ของคำสั่งถูกแก้ไขเช่นdir-attr="{{ scopeVar }}"ฟังก์ชันสังเกตจะถูกเรียกเฉพาะเมื่อมีการตั้งค่าการแก้ไข (ดังนั้นเมื่อ $ digest ได้กำหนดให้มีการอัปเดตแล้ว) โดยทั่วไปมีผู้เฝ้าดูสำหรับการแก้ไขอยู่แล้วและฟังก์ชัน $ observing piggybacks ก็เป็นเช่นนั้น

ดู $ observ & $ set ในcompile.js

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.