Angular - * ngIf เทียบกับการเรียกฟังก์ชั่นธรรมดาในเทมเพลต


14

ขออภัยถ้านี่ได้รับคำตอบแล้วที่นี่ แต่ฉันไม่พบสิ่งที่ตรงกันสำหรับสถานการณ์เฉพาะของเราดังนั้นที่นี่จะไป!

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

สมมติว่าเรามีบล็อกเทมเพลตที่ล้อมรอบด้วย ngIf ซึ่งจะตรวจสอบพารามิเตอร์หลายรายการเช่นที่นี่:

<ng-template *ngIf="user && user.name && isAuthorized">
 ...
</ng-template>

จะมีความแตกต่างอย่างมีนัยสำคัญในประสิทธิภาพเมื่อเทียบกับสิ่งนี้:

แม่แบบ:

<ng-template *ngIf="userCheck()">
 ...
</ng-template>

typescript:

userCheck(): boolean {
  return this.user && this.user.name && this.isAuthorized;
}

ดังนั้นเพื่อสรุปคำถามตัวเลือกสุดท้ายจะมีต้นทุนด้านประสิทธิภาพที่สำคัญหรือไม่

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


7
ไม่มันจะไม่ และมันก็สะอาดขึ้นเช่นกันเนื่องจากทำให้เทมเพลตสามารถอ่านได้ง่ายขึ้นสภาพนั้นทดสอบได้ง่ายขึ้นและนำมาใช้ซ้ำได้มากขึ้นและคุณมีเครื่องมือมากขึ้นในการกำจัด (ภาษา TypeScript ทั้งหมด) เพื่อให้สามารถอ่านได้และมีประสิทธิภาพมากที่สุด ฉันจะเลือกชื่อที่ชัดเจนกว่า "userCheck"
JB Nizet

ขอบคุณมากสำหรับการป้อนข้อมูลของคุณ :)
Jesper

คำตอบ:


8

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

ฉันเพิ่มกรณีอื่นด้วยuserCheck()ผลการแคช

*ngIf="isUserChecked"

...
// .ts
isUserChecked = this.userCheck()

เตรียมการสาธิตที่นี่: https://stackblitz.com/edit/angular-9qgsm9

น่าแปลกที่ดูเหมือนว่าไม่มีความแตกต่างระหว่าง

*ngIf="user && user.name && isAuthorized"

และ

*ngIf="userCheck()"

...
// .ts
userCheck(): boolean {
  return this.user && this.user.name && this.isAuthorized;
}

และ

*ngIf="isUserChecked"

...
// .ts
isUserChecked = this.userCheck()

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


10

นี่เป็นคำตอบที่ค่อนข้างน่าเชื่อถือ

การใช้งานฟังก์ชั่นเช่นนี้เป็นที่ยอมรับอย่างสมบูรณ์ มันจะทำให้แม่แบบมีความชัดเจนมากขึ้นและไม่ทำให้เกิดค่าใช้จ่ายที่สำคัญ อย่างที่ JB เคยกล่าวไว้ก่อนหน้านี้มันจะเป็นฐานที่ดีกว่าสำหรับการทดสอบหน่วยเช่นกัน

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

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

(โดยใช้ ฯลฯ เพราะผมไม่ทราบเหตุผลอื่น ๆ อีกต่อไป)


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

userIsAuthorized$ = combineLatest([
  this.user$,
  this.isAuthorized$
]).pipe(
  map(([ user, authorized ]) => !!user && !!user.name && authorized),
  shareReplay({ refCount: true, bufferSize: 1 })
);

จากนั้นคุณสามารถใช้เทมเพลตแบบนี้:

<ng-template *ngIf="userIsAuthorized$ | async">
 ...
</ng-template>

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

export class UserComponent implements ngOnChanges {
  userIsAuthorized: boolean = false;

  @Input()
  user?: any;

  @Input()
  isAuthorized?: boolean;

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.user || changes.isAuthorized) {
      this.userIsAuthorized = this.userCheck();
    }
  }

  userCheck(): boolean {
    return this.user && this.user.name && this.isAuthorized || false;
  }
}

ซึ่งคุณสามารถใช้ในเทมเพลตของคุณเช่นนี้:

<ng-template *ngIf="userIsAuthorized">
 ...
</ng-template>

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

1
@Jesper หากองค์ประกอบดำเนินการตามคำขอ get คุณมีObservableกระแสข้อมูลอยู่แล้วซึ่งจะทำให้เป็นตัวเลือกที่สมบูรณ์แบบสำหรับตัวเลือกที่ 2 ที่ฉันแสดง ไม่ว่าจะด้วยวิธีใดดีใจที่ฉันให้ข้อมูลเชิงลึกแก่คุณ
Poul Kruijt

6

ไม่แนะนำด้วยเหตุผลหลายประการ

ในการตรวจสอบว่าจำเป็นต้องแสดงผล userCheck () อีกครั้งหรือไม่ Angular จำเป็นต้องเรียกใช้งานนิพจน์ userCheck () เพื่อตรวจสอบว่ามีการเปลี่ยนแปลงค่าส่งคืนหรือไม่

เนื่องจาก Angular ไม่สามารถทำนายได้ว่าค่าการส่งคืนของ userCheck () มีการเปลี่ยนแปลงหรือไม่จึงจำเป็นต้องเรียกใช้ฟังก์ชันทุกครั้งที่ตรวจจับการเปลี่ยนแปลงการเรียกใช้

ดังนั้นหากการตรวจจับการเปลี่ยนแปลงทำงาน 300 ครั้งฟังก์ชันจะเรียกว่า 300 ครั้งแม้ว่าค่าที่ส่งคืนจะไม่เปลี่ยนแปลง

คำอธิบายเพิ่มเติมและปัญหาเพิ่มเติมhttps://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496

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

ตัวอย่างที่มีสิ่งที่สังเกตได้

user$;
isAuth$
userCheck$;

userCheck$ = user$.pipe(
switchMap((user) => {
    return forkJoin([of(user), isAuth$]);
 }
)
.map(([user, isAuthenticated])=>{
   if(user && user.name && isAuthenticated){
     return true;
   } else {
     return false;
   }
})
);

จากนั้นคุณสามารถใช้งานได้กับท่อ async ในรหัสของคุณ


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

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

ใช่ว่าจริงมันขอโทษจะไม่แก้ไขสำหรับการปฏิบัติที่ไม่ดี
แอนโธนีวิลลิสmuñoz

@ anthonywillismuñozคุณจะรับมือกับสถานการณ์เช่นนี้ได้อย่างไร? เพียงอยู่กับเงื่อนไขที่หลากหลายและอ่านยากใน * ngIf
Jesper

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

0

ฉันคิดว่า JavaScript ถูกสร้างขึ้นโดยมีเป้าหมายเพื่อให้ผู้พัฒนาไม่ได้สังเกตเห็นความแตกต่างระหว่างการแสดงผลและการเรียกใช้ฟังก์ชันที่เกี่ยวข้องกับประสิทธิภาพ

ใน C ++ มีคำสำคัญinlineเพื่อทำเครื่องหมายฟังก์ชั่น ตัวอย่างเช่น:

inline bool userCheck()
{
    return isAuthorized;
}

สิ่งนี้ทำเพื่อกำจัดการเรียกใช้ฟังก์ชัน ดังนั้นคอมไพเลอร์จะแทนที่การเรียกทั้งหมดuserCheckด้วยเนื้อความของฟังก์ชัน เหตุผลในการสร้างนวัตกรรมinline? การเพิ่มประสิทธิภาพ

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

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