Renderer2
Renderer2 的作用
Renderer2
是 Angular 提供的抽象層,用於安全地操作 DOM。它的主要目的是:
平台無關性:讓程式碼能在不同平台執行(瀏覽器、伺服器端、Web Worker)
安全性:防止 XSS 攻擊,自動清理危險內容
一致性:提供統一的 API 來操作 DOM
測試友善:更容易進行單元測試
為什麼使用 Renderer2 而不是直接操作 DOM?
// ❌ 不好的做法:直接操作 DOM
document.getElementById('myElement').innerHTML = userInput;
// ✅ 好的做法:使用 Renderer2
this.renderer.setProperty(element, 'innerHTML', userInput); // 會自動清理
常用方法分類
1. 元素創建與移除
// 創建元素
createElement(name: string, namespace?: string): any
const div = this.renderer.createElement('div');
const svg = this.renderer.createElement('circle', 'http://www.w3.org/2000/svg');
// 創建文字節點
createText(value: string): any
const textNode = this.renderer.createText('Hello World');
// 創建註解
createComment(value: string): any
const comment = this.renderer.createComment('This is a comment');
// 移除子元素
removeChild(parent: any, oldChild: any, isHostElement?: boolean): void
this.renderer.removeChild(parentElement, childElement);
// 銷毀節點
destroyNode(node: any): void
this.renderer.destroyNode(element);
2. DOM 結構操作
// 添加子元素
appendChild(parent: any, newChild: any): void
this.renderer.appendChild(this.el.nativeElement, newDiv);
// 插入元素
insertBefore(parent: any, newChild: any, refChild: any, isMove?: boolean): void
this.renderer.insertBefore(parent, newElement, referenceElement);
// 選擇器查詢(盡量少用)
selectRootElement(selectorOrNode: string | any, preserveContent?: boolean): any
3. 屬性操作
// 設定屬性
setAttribute(el: any, name: string, value: string, namespace?: string): void
this.renderer.setAttribute(element, 'id', 'myId');
this.renderer.setAttribute(element, 'aria-label', 'Close button');
// 移除屬性
removeAttribute(el: any, name: string, namespace?: string): void
this.renderer.removeAttribute(element, 'disabled');
// 設定特性(property)
setProperty(el: any, name: string, value: any): void
this.renderer.setProperty(element, 'innerHTML', '<span>Safe content</span>');
this.renderer.setProperty(element, 'value', inputValue);
4. 樣式操作
// 設定樣式
setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2): void
this.renderer.setStyle(element, 'color', 'red');
this.renderer.setStyle(element, 'width', '100px');
this.renderer.setStyle(element, 'display', 'none');
// 移除樣式
removeStyle(el: any, style: string, flags?: RendererStyleFlags2): void
this.renderer.removeStyle(element, 'color');
// 添加/移除 CSS 類別
addClass(el: any, name: string): void
removeClass(el: any, name: string): void
this.renderer.addClass(element, 'active');
this.renderer.removeClass(element, 'hidden');
5. 事件處理
// 監聽事件
listen(target: 'window' | 'document' | 'body' | any, eventName: string, callback: (event: any) => boolean | void): () => void
// 監聽元素事件
const unlisten = this.renderer.listen(element, 'click', (event) => {
console.log('Clicked!', event);
});
// 監聽全域事件
const unlistenWindow = this.renderer.listen('window', 'resize', (event) => {
console.log('Window resized');
});
// 移除事件監聽器
unlisten(); // 呼叫返回的函數來移除監聽
6. 其他實用方法
// 設定值(通常用於表單元素)
setValue(node: any, value: string): void
this.renderer.setValue(inputElement, 'new value');
// 獲取父節點
parentNode(node: any): any
const parent = this.renderer.parentNode(element);
// 獲取下一個兄弟節點
nextSibling(node: any): any
const sibling = this.renderer.nextSibling(element);
在你的 Directive 中的具體應用
export class {
private renderer = inject(Renderer2);
private el = inject(ElementRef);
private createIframe(): void {
// 創建元素
const iframe = this.renderer.createElement('iframe');
// 設定屬性
this.renderer.setAttribute(iframe, 'src', this.iframeSrc());
this.renderer.setAttribute(iframe, 'frameborder', '0');
// 設定樣式
this.renderer.setStyle(iframe, 'width', '100%');
this.renderer.setStyle(iframe, 'height', '500px');
this.renderer.setStyle(iframe, 'border', 'none');
// 監聽事件
const unlisten = this.renderer.listen(iframe, 'load', () => {
console.log('Iframe loaded');
unlisten(); // 移除監聽器
});
// 添加到 DOM
this.renderer.appendChild(this.el.nativeElement, iframe);
}
}
最佳實踐
1. 記得清理資源
ngOnDestroy(): void {
// 移除事件監聽器
if (this.unlistenFn) {
this.unlistenFn();
}
// 清理 DOM 節點
if (this.createdElement) {
this.renderer.removeChild(this.el.nativeElement, this.createdElement);
}
}
2. 避免直接 DOM 操作
// ❌ 避免這樣做
this.el.nativeElement.innerHTML = '<div>content</div>';
this.el.nativeElement.style.color = 'red';
// ✅ 使用 Renderer2
this.renderer.setProperty(this.el.nativeElement, 'innerHTML', '<div>content</div>');
this.renderer.setStyle(this.el.nativeElement, 'color', 'red');
3. 處理平台差異
import { PLATFORM_ID, inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
constructor() {
const platformId = inject(PLATFORM_ID);
if (isPlatformBrowser(platformId)) {
// 只在瀏覽器環境執行
this.setupBrowserSpecificFeatures();
}
}
效能考量
批量操作:盡量一次設定多個屬性,避免頻繁的 DOM 操作
事件移除:記得移除事件監聽器避免記憶體洩漏
元素回收:移除不需要的 DOM 元素
Renderer2 是 Angular 中 DOM 操作的標準方式,使用它能確保你的程式碼更安全、更可測試,且能在不同平台正常運作。
Last updated