Skip to content

Commit a83b378

Browse files
authored
fix: translate defer guide (#966)
1 parent 04e1e8e commit a83b378

File tree

3 files changed

+373
-74
lines changed

3 files changed

+373
-74
lines changed

adev-ja/src/app/sub-navigation-data.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
489489
contentPath: 'guide/performance/overview',
490490
},
491491
{
492-
label: '遅延ビュー',
492+
label: '遅延可能なビュー',
493493
path: 'guide/defer',
494494
contentPath: 'guide/defer',
495495
},

adev-ja/src/content/guide/defer.en.md

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
# Deferrable Views
2+
3+
## Overview
4+
5+
Deferrable views can be used in component template to defer the loading of select dependencies within that template. Those dependencies include components, directives, and pipes, and any associated CSS. To use this feature, you can declaratively wrap a section of your template in a `@defer` block which specifies the loading conditions.
6+
7+
Deferrable views support a series of [triggers](guide/defer#triggers), [prefetching](guide/defer#prefetching), and several sub blocks used for [placeholder](guide/defer#placeholder), [loading](guide/defer#loading), and [error](guide/defer#error) state management. You can also create custom conditions with [`when`](guide/defer#when) and [`prefetch when`](guide/defer#prefetching).
8+
9+
```angular-html
10+
@defer {
11+
<large-component />
12+
}
13+
```
14+
15+
## Why use Deferrable Views?
16+
17+
Deferrable views, also known as `@defer` blocks, are a powerful tool that can be used to reduce the initial bundle size of your application or defer heavy components that may not ever be loaded until a later time. This should result in a faster initial load and an improvement in your Core Web Vitals (CWV) results. Deferring some of your components until later should specifically improve Largest Contentful Paint (LCP) and Time to First Byte (TTFB).
18+
19+
Note: It is highly recommended that any defer loaded component that might result in layout shift once the dependencies have loaded be below the fold or otherwise not yet visible to the user.
20+
21+
## Which dependencies are defer-loadable?
22+
23+
In order for dependencies within a `@defer` block to be deferred, they need to meet two conditions:
24+
25+
1. They must be standalone. Non-standalone dependencies cannot be deferred and will still be eagerly loaded, even inside of `@defer` blocks.
26+
27+
2. They must not be directly referenced from the same file, outside of `@defer` blocks; this includes ViewChild queries.
28+
29+
Transitive dependencies of the components, directives, and pipes used in the defer block can be standalone or NgModule based and will still be deferred.
30+
31+
## Blocks
32+
33+
`@defer` blocks have several sub blocks to allow you to gracefully handle different stages in the deferred loading process.
34+
35+
### `@defer`
36+
37+
The content of the main `@defer` block is the section of content that is lazily loaded. It will not be rendered initially, and all of the content will appear once the specified [trigger](guide/defer#triggers) or `when` condition is met and the dependencies have been fetched. By default, a `@defer` block is triggered when the browser state becomes [idle](guide/defer#on-idle).
38+
39+
### `@placeholder`
40+
41+
By default, defer blocks do not render any content before they are triggered. The `@placeholder` is an optional block that declares content to show before the defer block is triggered. This placeholder content is replaced with the main content once the loading is complete. You can use any content in the placeholder section including plain HTML, components, directives, and pipes; however keep in mind the dependencies of the placeholder block are eagerly loaded.
42+
43+
Note: For the best user experience, you should always specify a `@placeholder` block.
44+
45+
The `@placeholder` block accepts an optional parameter to specify the `minimum` amount of time that this placeholder should be shown. This `minimum` parameter is specified in time increments of milliseconds (ms) or seconds (s). This parameter exists to prevent fast flickering of placeholder content in the case that the deferred dependencies are fetched quickly. The `minimum` timer for the `@placeholder` block begins after the initial render of this `@placeholder` block completes.
46+
47+
```angular-html
48+
@defer {
49+
<large-component />
50+
} @placeholder (minimum 500ms) {
51+
<p>Placeholder content</p>
52+
}
53+
```
54+
55+
Note: Certain triggers may require the presence of either a `@placeholder` or a [template reference variable](guide/templates/reference-variables) to function. See the [Triggers](guide/defer#triggers) section for more details.
56+
57+
### `@loading`
58+
59+
The `@loading` block is an optional block that allows you to declare content that will be shown during the loading of any deferred dependencies. Its dependences are eagerly loaded (similar to `@placeholder`).
60+
61+
For example, you could show a loading spinner. Once loading has been triggered, the `@loading` block replaces the `@placeholder` block.
62+
63+
The `@loading` block accepts two optional parameters to specify the `minimum` amount of time that this placeholder should be shown and amount of time to wait `after` loading begins before showing the loading template. `minimum` and `after` parameters are specified in time increments of milliseconds (ms) or seconds (s). Just like `@placeholder`, these parameters exist to prevent fast flickering of content in the case that the deferred dependencies are fetched quickly. Both the `minimum` and `after` timers for the `@loading` block begins immediately after the loading has been triggered.
64+
65+
```angular-html
66+
@defer {
67+
<large-component />
68+
} @loading (after 100ms; minimum 1s) {
69+
<img alt="loading..." src="loading.gif" />
70+
}
71+
```
72+
73+
### `@error`
74+
75+
The `@error` block allows you to declare content that will be shown if deferred loading fails. Similar to `@placeholder` and `@loading`, the dependencies of the `@error` block are eagerly loaded. The `@error` block is optional.
76+
77+
```angular-html
78+
@defer {
79+
<calendar-cmp />
80+
} @error {
81+
<p>Failed to load the calendar</p>
82+
}
83+
```
84+
85+
## Triggers
86+
87+
When a `@defer` block is triggered, it replaces placeholder content with lazily loaded content. There are two options for configuring when this swap is triggered: `on` and `when`.
88+
89+
<a id="on"></a>
90+
`on` specifies a trigger condition using a trigger from the list of available triggers below. An example would be on interaction or on viewport.
91+
92+
Multiple event triggers can be defined at once. For example: `on interaction; on timer(5s)` means that the defer block will be triggered if the user interacts with the placeholder, or after 5 seconds.
93+
94+
Note: Multiple `on` triggers are always OR conditions. Similarly, `on` mixed with `when` conditions are also OR conditions.
95+
96+
```angular-html
97+
@defer (on viewport; on timer(5s)) {
98+
<calendar-cmp />
99+
} @placeholder {
100+
<img src="placeholder.png" />
101+
}
102+
```
103+
104+
<a id="when"></a>
105+
`when` specifies a condition as an expression that returns a boolean. When this expression becomes truthy, the placeholder is swapped with the lazily loaded content (which may be an asynchronous operation if the dependencies need to be fetched).
106+
107+
Note: if the `when` condition switches back to `false`, the defer block is not reverted back to the placeholder. The swap is a one-time operation. If the content within the block should be conditionally rendered, an `if` condition can be used within the block itself.
108+
109+
```angular-html
110+
@defer (when cond) {
111+
<calendar-cmp />
112+
}
113+
```
114+
115+
You could also use both `when` and `on` together in one statement, and the swap will be triggered if either condition is met.
116+
117+
```angular-html
118+
@defer (on viewport; when cond) {
119+
<calendar-cmp />
120+
} @placeholder {
121+
<img src="placeholder.png" />
122+
}
123+
```
124+
125+
### on idle
126+
127+
`idle` will trigger the deferred loading once the browser has reached an idle state (detected using the `requestIdleCallback` API under the hood). This is the default behavior with a defer block.
128+
129+
### on viewport
130+
131+
`viewport` would trigger the deferred block when the specified content enters the viewport using the [`IntersectionObserver` API](https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API). This could be the placeholder content or an element reference.
132+
133+
By default, the placeholder will act as the element watched for entering viewport as long as it is a single root element node.
134+
135+
```angular-html
136+
@defer (on viewport) {
137+
<calendar-cmp />
138+
} @placeholder {
139+
<div>Calendar placeholder</div>
140+
}
141+
```
142+
143+
Alternatively, you can specify a [template reference variable](guide/templates/reference-variables) in the same template as the `@defer` block as the element that is watched to enter the viewport. This variable is passed in as a parameter on the viewport trigger.
144+
145+
```angular-html
146+
<div #greeting>Hello!</div>
147+
148+
@defer (on viewport(greeting)) {
149+
<greetings-cmp />
150+
}
151+
```
152+
153+
### on interaction
154+
155+
`interaction` will trigger the deferred block when the user interacts with the specified element through `click` or `keydown` events.
156+
157+
By default, the placeholder will act as the interaction element as long as it is a single root element node.
158+
159+
```angular-html
160+
@defer (on interaction) {
161+
<calendar-cmp />
162+
} @placeholder {
163+
<div>Calendar placeholder</div>
164+
}
165+
```
166+
167+
Alternatively, you can specify a [template reference variable](guide/templates/reference-variables) as the element that triggers interaction. This variable is passed in as a parameter on the interaction trigger.
168+
169+
```angular-html
170+
<button type="button" #greeting>Hello!</button>
171+
172+
@defer (on interaction(greeting)) {
173+
<calendar-cmp />
174+
} @placeholder {
175+
<div>Calendar placeholder</div>
176+
}
177+
```
178+
179+
### on hover
180+
181+
`hover` triggers deferred loading when the mouse has hovered over the trigger area. Events used for this are `mouseenter` and `focusin`.
182+
183+
By default, the placeholder will act as the hover element as long as it is a single root element node.
184+
185+
```angular-html
186+
@defer (on hover) {
187+
<calendar-cmp />
188+
} @placeholder {
189+
<div>Calendar placeholder</div>
190+
}
191+
```
192+
193+
Alternatively, you can specify a [template reference variable](guide/templates/reference-variables) as the hover element. This variable is passed in as a parameter on the hover trigger.
194+
195+
```angular-html
196+
<div #greeting>Hello!</div>
197+
198+
@defer (on hover(greeting)) {
199+
<calendar-cmp />
200+
} @placeholder {
201+
<div>Calendar placeholder</div>
202+
}
203+
```
204+
205+
### on immediate
206+
207+
`immediate` triggers the deferred load immediately, meaning once the client has finished rendering, the defer chunk would then start fetching right away.
208+
209+
```angular-html
210+
@defer (on immediate) {
211+
<calendar-cmp />
212+
} @placeholder {
213+
<div>Calendar placeholder</div>
214+
}
215+
```
216+
217+
### on timer
218+
219+
`timer(x)` would trigger after a specified duration. The duration is required and can be specified in `ms` or `s`.
220+
221+
```angular-html
222+
@defer (on timer(500ms)) {
223+
<calendar-cmp />
224+
}
225+
```
226+
227+
## Prefetching
228+
229+
`@defer` allows to specify conditions when prefetching of the dependencies should be triggered. You can use a special `prefetch` keyword. `prefetch` syntax works similarly to the main defer conditions, and accepts `when` and/or `on` to declare the trigger.
230+
231+
In this case, `when` and `on` associated with defer controls when to render, and `prefetch when` and `prefetch on` controls when to fetch the resources. This enables more advanced behaviors, such as letting you start to prefetch resources before a user has actually seen or interacted with a defer block, but might interact with it soon, making the resources available faster.
232+
233+
In the example below, the prefetching starts when a browser becomes idle and the contents of the block is rendered on interaction.
234+
235+
```angular-html
236+
@defer (on interaction; prefetch on idle) {
237+
<calendar-cmp />
238+
} @placeholder {
239+
<img src="placeholder.png" />
240+
}
241+
```
242+
243+
## Testing
244+
245+
Angular provides TestBed APIs to simplify the process of testing `@defer` blocks and triggering different states during testing. By default, `@defer` blocks in tests will play through like a defer block would behave in a real application. If you want to manually step through states, you can switch the defer block behavior to `Manual` in the TestBed configuration.
246+
247+
```typescript
248+
it('should render a defer block in different states', async () => {
249+
// configures the defer block behavior to start in "paused" state for manual control.
250+
TestBed.configureTestingModule({deferBlockBehavior: DeferBlockBehavior.Manual});
251+
252+
@Component({
253+
// ...
254+
template: `
255+
@defer {
256+
<large-component />
257+
} @placeholder {
258+
Placeholder
259+
} @loading {
260+
Loading...
261+
}
262+
`
263+
})
264+
class ComponentA {}
265+
266+
// Create component fixture.
267+
const componentFixture = TestBed.createComponent(ComponentA);
268+
269+
// Retrieve the list of all defer block fixtures and get the first block.
270+
const deferBlockFixture = (await componentFixture.getDeferBlocks())[0];
271+
272+
// Renders placeholder state by default.
273+
expect(componentFixture.nativeElement.innerHTML).toContain('Placeholder');
274+
275+
// Render loading state and verify rendered output.
276+
await deferBlockFixture.render(DeferBlockState.Loading);
277+
expect(componentFixture.nativeElement.innerHTML).toContain('Loading');
278+
279+
// Render final state and verify the output.
280+
await deferBlockFixture.render(DeferBlockState.Complete);
281+
expect(componentFixture.nativeElement.innerHTML).toContain('large works!');
282+
});
283+
```
284+
285+
## Behavior with Server-side rendering (SSR) and Static site generation (SSG)
286+
287+
When rendering an application on the server (either using SSR or SSG), defer blocks always render their `@placeholder` (or nothing if a placeholder is not specified). Triggers are ignored on the server.
288+
289+
## Behavior with `NgModule`
290+
291+
`@defer` blocks can be used in both standalone and NgModule-based components, directives and pipes. You can use standalone and NgModule-based dependencies inside of a `@defer` block, however **only standalone components, directives, and pipes can be deferred**. The NgModule-based dependencies would be included into the eagerly loaded bundle.
292+
293+
## Nested `@defer` blocks and avoiding cascading loads
294+
295+
There are cases where nesting multiple `@defer` blocks may cause cascading requests. An example of this would be when a `@defer` block with an immediate trigger has a nested `@defer` block with another immediate trigger. When you have nested `@defer` blocks, make sure that an inner one has a different set of conditions, so that they don't trigger at the same time, causing cascading requests.
296+
297+
## Avoiding Layout Shifts
298+
299+
It is a recommended best practice to not defer components that will be visible in the user's viewport on initial load. This will negatively affect Core Web Vitals by causing an increase in cumulative layout shift (CLS). If you choose to defer components in this area, it's best to avoid `immediate`, `timer`, `viewport`, and custom `when` conditions that would cause the content to be loaded during the initial render of the page.

0 commit comments

Comments
 (0)