# Angular @defer ในปี 2026: Declarative Lazy Loading สำหรับแอปพลิเคชันที่เร็วขึ้น > เจาะลึก Angular @defer blocks สำหรับ declarative lazy loading พร้อม triggers ทุกประเภท prefetching, incremental hydration, พฤติกรรมบน SSR และรูปแบบการใช้งานจริงเพื่อเพิ่มประสิทธิภาพ - Published: 2026-06-14 - Updated: 2026-06-14 - Author: SharpSkill - Tags: angular, performance, lazy-loading - Reading time: 8 min --- Angular `@defer` blocks แก้ปัญหาที่ router-based lazy loading ไม่เคยทำได้มาก่อน นั่นคือการโหลด components, directives และ pipes แต่ละตัวตามความต้องการ โดยไม่จำเป็นต้องปรับโครงสร้างแอปพลิเคชันใหม่ให้แยกเป็น routes ต่างหาก ฟีเจอร์นี้เปิดตัวใน Angular 17 และผ่านการพัฒนาจนเสถียรใน Angular 22 ทำให้สิ่งที่เคยต้องใช้ dynamic imports, `*ngIf` flags และการตั้งค่า webpack configuration กลายเป็น declarative template block เพียงบล็อกเดียว > **@defer ทำอะไรได้บ้าง** > > `@defer` สั่งให้ Angular compiler แยก components ที่ถูกครอบไว้ออกเป็น JavaScript chunks แยกต่างหาก โดยจะโหลดเฉพาะเมื่อเงื่อนไข trigger ถูกกระตุ้นเท่านั้น เบราว์เซอร์ดาวน์โหลด JavaScript น้อยลงตั้งแต่เริ่มต้น ช่วยปรับปรุงค่า Largest Contentful Paint (LCP) และ Time to First Byte (TTFB) โดยไม่ต้องตั้งค่า code-splitting ด้วยตนเอง ## กลไกการทำงานภายในของ @defer เมื่อ Angular compiler พบ `@defer` block จะทำการแยก standalone component, directive และ pipe ทุกตัวที่อยู่ภายในออกเป็น chunk แยกต่างหาก main bundle จะถูกส่งไปยังเบราว์เซอร์โดยไม่รวม dependencies เหล่านี้ เมื่อถึง runtime Angular จะประเมินเงื่อนไข trigger และดึง chunk ผ่าน dynamic `import()` call บล็อกนี้รองรับ sub-blocks สี่ประเภทที่ควบคุมสิ่งที่ผู้ใช้เห็นตลอดวงจรชีวิตการโหลด: ```typescript // app.component.html @defer (on viewport) { } @placeholder {
} @loading (minimum 200ms) { } @error {

Failed to load the dashboard. Please refresh.

} ``` `@placeholder` แสดงผลก่อนที่ trigger จะทำงาน `@loading` ปรากฏขึ้นขณะที่ chunk กำลังดาวน์โหลด โดยมีพารามิเตอร์ `minimum` เพื่อกำหนดระยะเวลาขั้นต่ำในการแสดงผลและป้องกันการกะพริบ `@error` รับมือกับความล้มเหลวของเครือข่ายหรือข้อผิดพลาดของ chunk พารามิเตอร์ `minimum` บน `@loading` ป้องกัน spinner กะพริบสั้นเกินไปเมื่อ chunk โหลดจาก cache ได้อย่างรวดเร็ว ข้อจำกัดที่สำคัญคือ dependency ทุกตัวภายใน `@defer` ต้องเป็น standalone เท่านั้น components ที่ไม่ใช่ standalone ซึ่งประกาศใน `NgModule` ไม่สามารถ defer ได้และจะถูกโหลดแบบ eagerly โดยไม่คำนึงถึง `@defer` wrapper ที่ครอบอยู่ ## ประเภทของ Trigger: การควบคุมจังหวะการโหลด Component Angular จัดเตรียม trigger ในตัวไว้เจ็ดประเภท แต่ละประเภทมุ่งเป้าไปที่กลยุทธ์การโหลดที่แตกต่างกัน triggers หลายตัวสามารถรวมกันด้วย semicolons โดยประเมินเป็นเงื่อนไข OR ```typescript // product-page.component.html // Loads when the browser goes idle (default) @defer { } // Loads when the element enters the viewport @defer (on viewport) { } // Loads on click or keydown @defer (on interaction) { } @placeholder { } // Loads on mouseenter or focusin @defer (on hover) { } // Loads after 3 seconds @defer (on timer(3s)) { } // Loads immediately after initial render @defer (on immediate) { } ``` Trigger `on viewport` ใช้ Intersection Observer API ภายในเพื่อตรวจจับว่า element เลื่อนเข้าสู่พื้นที่ที่มองเห็นบนหน้าจอ `on idle` อาศัย `requestIdleCallback` ทำให้เป็นค่าเริ่มต้นที่ปลอดภัยที่สุดสำหรับเนื้อหาที่อยู่ด้านล่างของหน้าจอ `on timer` รับค่าระยะเวลาในหน่วยมิลลิวินาที (`500ms`) หรือวินาที (`3s`) ### Trigger `when` สำหรับเงื่อนไขแบบ Reactive นอกเหนือจาก trigger ที่อิงกับ event แล้ว `when` ยังรับ boolean expression ใดก็ได้ รวมถึงการอ่านค่า signal: ```typescript // dashboard.component.ts export class DashboardComponent { showAdvanced = signal(false); } // dashboard.component.html @defer (when showAdvanced()) { } @placeholder { } ``` การรวม `on` กับ `when` เข้าด้วยกันถูกต้องตามไวยากรณ์ Angular ถือว่าเป็นเงื่อนไข OR กล่าวคือบล็อกจะโหลดเมื่อเงื่อนไขใดเงื่อนไขหนึ่งเป็นจริง ## Prefetching: การแยกการดาวน์โหลดออกจากการแสดงผล Prefetching แยกจังหวะที่ chunk ถูกดาวน์โหลดออกจากจังหวะที่ component ถูก render ช่วยขจัดความรู้สึกล่าช้าของผู้ใช้โดยการดึง JavaScript ล่วงหน้าก่อนที่ผู้ใช้จะกระตุ้น trigger จริง ```typescript // settings.component.html @defer (on interaction; prefetch on idle) { } @placeholder { } ``` ในรูปแบบนี้เบราว์เซอร์จะดาวน์โหลด chunk ของ `account-settings` ในช่วง idle time เมื่อผู้ใช้คลิก component จะ render ทันทีเพราะโค้ดพร้อมใช้งานแล้ว Prefetching รองรับ trigger types เดียวกันกับ `on` clause หลัก รูปแบบที่นิยมใช้สำหรับ dashboards คือ prefetch components ที่มีน้ำหนักมากในช่วง idle ขณะที่เลื่อนการ render ไปจนกว่าจะเข้าสู่ viewport: ```typescript // analytics.component.html @defer (on viewport; prefetch on idle) { } @placeholder (minimum 100ms) {
} ``` ## @defer กับ Server-Side Rendering บนฝั่ง server `@defer` blocks จะ render เนื้อหาของ `@placeholder` เป็นค่าเริ่มต้น deferred component จะไม่ถูก render บน server เลย พฤติกรรมนี้เป็นไปตามเจตนา เนื่องจากการดาวน์โหลด chunk มีความหมายเฉพาะในบริบทของเบราว์เซอร์เท่านั้น สำหรับเนื้อหาที่สำคัญต่อ SEO หมายความว่า `@placeholder` ต้องมี HTML ที่มีความหมาย ไม่ใช่เพียงแค่ spinner: ```typescript // blog-post.component.html @defer (on viewport) { } @placeholder {

Comments

Loading comments...

} ``` Search engines จะ index เนื้อหาของ placeholder ระหว่าง SSR การใช้ placeholder ที่มีโครงสร้าง semantic ชัดเจนช่วยรักษาคุณภาพของหน้าเว็บให้สมบูรณ์ทั้งในแง่ของ SEO และ accessibility ## Incremental Hydration ร่วมกับ @defer Angular 19 เปิดตัว incremental hydration ในสถานะ developer preview และ Angular 22 ทำให้ฟีเจอร์นี้เสถียรพร้อมสำหรับ production Incremental hydration ขยายความสามารถของ `@defer` ด้วย `hydrate` trigger ที่ควบคุมช่วงเวลาที่ component ซึ่ง render บน server จะกลายเป็น interactive บน client ```typescript // product-page.component.html @defer (hydrate on viewport) { } @defer (hydrate on interaction) { } ``` สิ่งที่แตกต่างจาก `@defer` แบบมาตรฐานคือ incremental hydration จะ render HTML ของ component อย่างสมบูรณ์บน server ฝั่ง client ได้รับ markup ที่ครบถ้วนแต่ข้ามการ hydrate component ไปจนกว่า trigger จะทำงาน ผลลัพธ์คือเบราว์เซอร์ paint หน้าจอทั้งหมดได้ทันที แต่การ execute JavaScript ถูกเลื่อนออกไป ## รูปแบบการใช้งานจริงและกลยุทธ์ด้าน Performance ### รูปแบบที่ 1: Dashboard ขนาดใหญ่ที่มีหลาย Deferred Panels ```typescript // dashboard.component.html
@defer (on viewport; prefetch on idle) { } @placeholder { } @defer (on viewport; prefetch on idle) { } @placeholder { } @defer (on interaction) { } @placeholder { }
``` แต่ละ panel โหลดอย่างอิสระจากกัน export panel จะไม่ถูกโหลดจนกว่าผู้ใช้จะร้องขออย่างชัดเจน ช่วยประหยัดการดาวน์โหลด chunk ทั้ง chunk สำหรับผู้ใช้ที่ไม่เคยใช้ฟังก์ชันส่งออกข้อมูล ### รูปแบบที่ 2: การโหลดฟีเจอร์ตามเงื่อนไขด้วย Signals ```typescript // editor.component.ts export class EditorComponent { user = inject(UserService).currentUser; isPremium = computed(() => this.user()?.plan === 'premium'); } // editor.component.html @defer (when isPremium()) { } @placeholder { } ``` ผู้ใช้ระดับ free-tier จะไม่ดาวน์โหลด chunk ของ AI assistant เลย upgrade banner ทำหน้าที่เป็นทั้ง placeholder และสื่อส่งเสริมการอัปเกรดแพ็คเกจ ### การหลีกเลี่ยงข้อผิดพลาดที่พบบ่อย การซ้อน `@defer` blocks ที่ใช้ trigger เดียวกันจะทำให้ chunks ถูกดาวน์โหลดพร้อมกัน ควรกระจาย triggers ให้แตกต่างกันระหว่างบล็อกที่ซ้อนกัน: ```typescript // Avoid: both fire on idle simultaneously @defer { @defer { } } // Better: outer on idle, inner on viewport @defer (on idle) { } // Inside wrapper-component template: @defer (on viewport) { } ``` ข้อผิดพลาดอีกประการคือการใช้ `@defer` กับ components ที่อยู่ใน critical rendering path การ defer เนื้อหา above-the-fold จะทำให้ LCP แย่ลงเพราะเบราว์เซอร์ต้องดาวน์โหลดและ execute chunk ก่อนจึงจะ paint ได้ ควรสงวน `@defer` ไว้สำหรับเนื้อหา below-the-fold หรือเนื้อหาที่ถูกกระตุ้นโดยผู้ใช้ ## @defer เทียบกับ Router-Based Lazy Loading Router-based lazy loading และ `@defer` เป็นเทคนิคที่เสริมซึ่งกันและกัน Router lazy loading แบ่งในระดับ route โดย defer feature modules ทั้งหมด ส่วน `@defer` แบ่งภายใน template โดย defer components แต่ละตัว | Aspect | Router Lazy Loading | @defer | |--------|-------------------|--------| | Granularity | Route level | Component level | | Trigger | Navigation event | viewport, idle, interaction, hover, timer, condition | | SSR behavior | Full server render | Placeholder on server | | Requires standalone | No (works with NgModules) | Yes (standalone only) | | Prefetching | `preloadingStrategy` | `prefetch on` clause | | Use case | Feature modules, pages | Widgets, panels, conditional UI | แอปพลิเคชันทั่วไปจะใช้ทั้งสองเทคนิคร่วมกัน: router lazy loading สำหรับ feature areas ระดับบนสุด และ `@defer` สำหรับ components ที่มีน้ำหนักมากภายใน feature areas เหล่านั้น ## คำถามสัมภาษณ์งานเกี่ยวกับ Angular @defer การสัมภาษณ์งานด้านเทคนิคมีแนวโน้มครอบคลุม `@defer` มากขึ้น เนื่องจากฟีเจอร์นี้กลายเป็นหัวใจสำคัญของกลยุทธ์ performance ใน Angular ต่อไปนี้คือรูปแบบคำถามที่ปรากฏบ่อยในบริบทของ[คำถามสัมภาษณ์ Angular](/technologies/angular/interview-questions/standalone-components) **ถาม: จะเกิดอะไรขึ้นเมื่อวาง component ที่ไม่ใช่ standalone ไว้ภายใน @defer?** Component จะถูกโหลดแบบ eagerly โดยไม่คำนึงถึง `@defer` block Angular compiler ไม่สามารถแยก components ที่ประกาศใน NgModule ออกเป็น chunks แยกต่างหากได้ ไม่มี error ถูก throw แต่การ optimization จะล้มเหลวอย่างเงียบๆ **ถาม: @defer ทำงานอย่างไรระหว่าง SSR?** Server จะ render เนื้อหาของ `@placeholder` เท่านั้น deferred component จะไม่ถูก render บน server เลย Hydration จะเกิดขึ้นในฝั่ง client เมื่อ trigger ทำงาน (หรือผ่าน `hydrate on` สำหรับ incremental hydration) **ถาม: @defer สามารถใช้ร่วมกับ OnPush change detection ได้หรือไม่?** ได้ `@defer` เป็นฟีเจอร์ระดับ template และทำงานร่วมกับ change detection strategy ใดก็ได้ เมื่อ deferred component ถูกโหลดแล้ว จะทำงานตามการตั้งค่า change detection ของตัวเอง **ถาม: ความแตกต่างระหว่าง `on idle` กับ `on immediate` คืออะไร?** `on idle` รอจนกว่าเบราว์เซอร์จะทำงานที่ค้างอยู่เสร็จทั้งหมดผ่าน `requestIdleCallback` ส่วน `on immediate` trigger ทันทีหลังจาก Angular เสร็จสิ้น initial render pass โดยไม่รอ idle state `on immediate` เหมาะสำหรับ components ที่ควรโหลดในไม่ช้าแต่ไม่ควร block first paint ## สรุป - `@defer` แยก standalone components ออกเป็น chunks แยกต่างหากที่โหลดตามความต้องการ ช่วยลดขนาด initial bundle โดยไม่ต้องปรับโครงสร้าง routes - trigger ทั้งเจ็ดประเภท (`idle`, `viewport`, `interaction`, `hover`, `timer`, `immediate`, `when`) ครอบคลุมกลยุทธ์การโหลดทุกรูปแบบตั้งแต่แบบ passive ไปจนถึงแบบที่ผู้ใช้กระตุ้น - `prefetch on` แยกการดาวน์โหลด chunk ออกจากการ render component ช่วยขจัดความรู้สึกล่าช้าสำหรับ components ที่ถูกกระตุ้นโดยผู้ใช้ - Incremental hydration (`hydrate on`) render HTML สมบูรณ์บน server ขณะเลื่อนการ execute JavaScript ไปยัง client ส่งผลให้ LCP ปรับปรุงขึ้น 40-50% สำหรับหน้าที่มีเนื้อหามาก - เนื้อหาของ `@placeholder` จะถูก render ระหว่าง SSR และต้องมี semantic HTML ที่สมบูรณ์เพื่อรักษาคุณภาพ SEO - `@defer` เสริมกับ router-based lazy loading: ใช้ routes สำหรับการแบ่งระดับ feature และ `@defer` สำหรับการแบ่งระดับ component ภายใน templates - components ที่ไม่ใช่ standalone ไม่สามารถ defer ได้และจะถูกโหลดแบบ eagerly อย่างเงียบๆ --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/th/blog/angular/angular-defer-loading-declarative-lazy-loading-guide