此錯誤與錯誤 NG0500 類似,但表示 Angular 的水合過程預期在 DOM 的某個特定位置有更多同層節點,卻沒有找到。與直接操作 DOM 相關的成因,請見錯誤 NG0500 頁面。
常見成因:伺服器與用戶端之間不同的條件內容
除了直接操作 DOM 之外,當 @if 或 @for 內的內容在伺服器上的渲染結果與用戶端水合時不同,也常發生此錯誤。如果伺服器把某個條件渲染為 true(或渲染出項目較多的清單),但水合執行時用戶端看到的是 false(或較短的清單),用戶端預期的同層節點數量就會少於伺服器實際送出的數量,水合便會在遍歷同層節點時失敗。
常見的發生方式是:值來自 input(),但沒有直接使用該輸入(或透過 computed() 使用),而是在 ngOnInit() 或建構函式中一次性複製到普通的 signal() 裡:
@Component({
selector: 'app-example',
template: `
@if (visibleItems().length) {
<ul>
@for (item of visibleItems(); track item) {
<li>{{ item }}</li>
}
</ul>
}
`,
})
export class Example {
items = input<string[]>();
// This only runs once. If `items()` is not ready yet at the exact
// moment `ngOnInit` runs, this signal can end up holding a different
// value on the server than it does on the client. That means a
// different number of DOM nodes gets rendered on each side, which
// causes a hydration mismatch.
visibleItems = signal<string[]>([]);
ngOnInit() {
this.visibleItems.set(this.items() ?? []);
}
}
修正方式是推導該值,而不是一次性複製它。這樣每次渲染(包括第一次)都會與輸入一致:
export class Example {
items = input<string[]>();
visibleItems = computed(() => this.items() ?? []);
}
如果你還需要讓使用者在本機覆寫該值,例如用「顯示更多」按鈕顯示額外項目,請改用 linkedSignal(),而不是在生命週期勾點中設定的普通 signal()。
除錯方式
與直接操作 DOM 相關成因的除錯提示,請見錯誤 NG0500。
單憑此錯誤無法得知是哪個元件造成的。Angular DevTools 瀏覽器擴充功能 可以協助你:它能標示出水合不相符實際發生的元件。找到後,請檢查它如何計算範本中 @if 或 @for 內使用的任何值。確認該值是直接來自 input()(透過範本繫結或 computed()),還是像上面那樣在生命週期勾點中被複製到訊號裡。