本文轉載自:
過去幾年對 Angular 來說是變革的時期,我們推出了許多重大進展,如使用 Signals 的響應式編程和 Zoneless 應用的強大功能。
我們希望這些功能能幫助 Angular 社區構建下一代網絡應用程序,使其快速上市并具有強大的性能。
我們才剛剛開始!Angular v20 是我們最新發布的版本,我們花費了無數的時間打磨一些正在開發中的功能,為您提供堅如磐石的開發體驗。
亮點包括:
穩定 API,如
effect
、linkedSignal
、toSignal
、增量水合、路由級渲染模式配置以及將 zoneless 提升至開發者預覽版通過 Angular DevTools 改進調試功能,并與 Chrome 合作,在 Chrome DevTools 中直接進行自定義 Angular 報告
通過編碼風格指南更新、類型檢查和 HostBindings 的語言服務支持、模板中支持 untagged 模板字面量表達式、默認模板熱模塊替換等,提升開發者體驗。
GenAI 開發方面的進步,包括
llms.txt
和 angular.dev 指南和視頻,用于構建生成式 AI 應用。啟動對 Angular 官方吉祥物的評論請求。
過去三年,我們重新思考了 Angular 的反應性模型,使其更加健壯和面向未來。在 Angular v16 中,我們發布了 Angular Signals 的開發者預覽版,自那以后,它們在谷歌內外得到了廣泛采用。
YouTube 在舞臺上分享了如何利用 Angular Signals 和 Wiz 將Living Room的輸入延遲改善了 35%。與此同時,TC39 啟動了一項調查,通過基于 Angular Signals 的參考實現,將 Signals 引入 JavaScript 語言。
在收集了 RFC 反饋并迭代實現后,我們將 signal
、computed
、input
和 view queries API 推升至穩定版本。今天,我們宣布 effect
、linkedSignal
和 toSignal
也成為穩定版本。
新的實驗性 API
為了解決使用 Angular 管理異步狀態的問題,在 v19 中我們開發了 resource API。自那以后,我們引入了資源流,并創建了一個名為 httpResource
的新 API,它允許你使用基于 Signal 的響應式 API 發起 HTTP 請求。這兩個 API 都作為 v20 的一部分提供實驗性支持。
resource API
允許你在 Signal 變化時發起異步操作,并將該操作的結果作為 Signal 暴露出去:
const userId: Signal
= getUserId(); const userResource = resource({ params: () => ({id: userId()}), loader: ({request, abortSignal}): Promise => { // 當給定的 `AbortSignal` 指示請求已中止時, // fetch 會取消任何未完成的 HTTP 請求。 return fetch(`users/${request.id}`, {signal: abortSignal}); }, });
上面的代碼將在 userId
signal 變化時獲取具有特定標識符的用戶。
現在假設我們從 WebSocket 獲取數據。為此,我們可以使用流式資源:
@Component({ template: `{{ dataStream.value() }}` }) export class App { // WebSocket 初始化邏輯將在此處執行…… // ... // 流媒體資源的初始化 dataStream = resource({ stream: () => { return new Promise string []>>> ((resolve) => { const resourceResult = signal<{ value: string[] }>({ value: [], }); this.socket.onmessage = event => { resourceResult.update(current => ({ value: [...current.value, event.data] }); }; resolve(resourceResult); }); }, }); }
在這個最小示例中,我們聲明了一個新的流式 resource,它返回一個 Signal 的 Promise。該 signal 的值類型為 ResourceStreamItem
,這意味著如果我們想返回錯誤,signal 可以持有 { value: string[] }
或 {error: … }
的值。
我們通過 resourceResult
Signal 發出從 WebSocket 接收到的值。
基于這一模式,我們還發布了實驗性功能 httpResource
:
@Component({ template: `{{ userResource.value() | json }}` }) class UserProfile { userId = signal(1); userResource = httpResource (() => `https://example.com/v1/users/${this.userId()}` }); }
上述代碼片段會在 userId
變化時,向我們指定的 URL 發送 HTTP GET 請求。 httpResource
返回 HttpResourceRef
,它具有一個類型為 signal 的 value
屬性,我們可以在模板中直接訪問。 userResource
還包含其他值,例如 isLoading
、headers
等。
在底層,httpResource
使用 HttpClient
,因此你可以在 HttpClient
provider 中指定攔截器:
bootstrapApplication(AppComponent, {providers: [ provideHttpClient( withInterceptors([loggingInterceptor, cachingInterceptor]), ) ]});
將 Zoneless 提升至開發者預覽版在過去六個月里,我們在 zoneless 的方面取得了很大進展,特別是在服務器端渲染和錯誤處理方面。
許多開發人員甚至在不知不覺中使用 Zone.js 來捕捉應用程序中的錯誤。Zone.js 還能讓框架知道我們何時準備好將服務器端渲染的應用程序刷新到客戶端。
在 Zoneless 的世界里,我們必須為這些問題找到可靠的解決方案。
在 v20 中,我們現在在 Node.js 的 SSR 期間為 unhandledRejection
和 uncaughtException
提供了默認處理器,以防止在出現錯誤時 Node 服務器崩潰。
在客戶端,你可以在你的 providers 中包含 provideBrowserGlobalErrorListeners
。你可以通過更新你的 providers 來開始使用 zoneless:
bootstrapApplication(AppComponent, {providers: [ provideZonelessChangeDetection(), provideBrowserGlobalErrorListeners() ]});
此外,請確保從你的 angular.json
中移除 zone.js
依賴。了解更多關于 zoneless 的優勢以及如何遷移你的項目,請參閱我們的文檔。
如果你正在創建一個新的 Angular 項目,可以使用 CLI 從一開始就將其設置為 zoneless:
在服務器上鞏固 Angular
在 v20 版本中,我們還專注于優化我們的旗艦服務器端渲染功能 —— 增量水合和路由級渲染模式配置。今天,我們很高興地將兩者都提升至穩定版本!
提醒一下,增量水合通過在特定觸發條件下下載并水合頁面的一部分,從而讓你的應用運行更快。
這樣,您的用戶無需下載特定頁面的所有 JavaScript,而是可以逐步下載他們需要的部分。
要開始使用增量水合,請通過指定 withIncrementalHydration
來配置水合:
import { provideClientHydration, withIncrementalHydration } from '@angular/platform-browser'; // ... provideClientHydration(withIncrementalHydration());
現在,在您的組件模板中,您可以使用可延遲視圖:
@defer (hydrate on viewport) {
}
這樣,Angular 將下載購物車組件及其傳遞依賴項,并在該部分 UI 進入視口時才進行水合。
此外,現在您可以使用路由級別的渲染模式配置作為穩定的 API!如果您的應用中不同路由有不同的渲染需求,您可以在服務器路由配置中進行設置:
export const routeConfig: ServerRoute = [ { path: '/login', mode: RenderMode.Server }, { path: '/dashboard', mode: RenderMode.Client }, { path: '/product/:id', mode: RenderMode.Prerender, async getPrerenderParams() { const dataService = inject(ProductService); const ids = await dataService.getIds(); // ["1", "2", "3"] // 在路由路徑中,使用 `id` 代替 `:id`。 return ids.map(id => ({ id })); } } ];
在上述代碼片段中,我們配置了在服務器上渲染登錄頁面,在客戶端渲染儀表板頁面,并預渲染產品頁面。
請注意,產品頁面需要一個 id
參數。為了解析每個產品的標識符,我們可以使用異步函數 getPrerenderParams
。它返回一個對象,其中的鍵映射到路由參數。對于 /product/:id
頁面,我們返回一個具有 id
屬性的對象。
您可以將服務器端渲染的應用程序托管在大多數云服務提供商上。我們與 Firebase App Hosting 緊密合作,提供無縫的部署體驗,支持混合渲染(SSR、SSG 和 CSR),并提供 Google Cloud 的安全性和可擴展性。
優化開發者體驗
我們在開發 v20 的過程中投入了大量時間,致力于工程卓越性 —— 完善現有 API 以提升開發者體驗。我們全面進行了這項工作 —— 框架、路由器、表單、HTTP 等。讓我在這里分享更多我們在此處所做的工作!
Chrome DevTools 中的性能洞察
為了進一步增強開發者體驗,并深入了解應用程序性能,我們與 Chrome DevTools 團隊合作,將 Angular 特定的分析數據直接集成到性能面板中。
此前,開發者經常需要在框架特定的分析器和瀏覽器 DevTools 之間切換,這使得關聯信息并定位瓶頸變得具有挑戰性,尤其是在處理壓縮的生產代碼時。
這項新集成旨在通過在與其他瀏覽器性能指標相同的時序軸上顯示 Angular 運行時數據(如組件渲染、變更檢測周期和事件監聽器執行)來解決這個問題。
這項直接集成從 Angular v20 開始提供,利用了性能面板擴展性 API,特別是使用 console.timeStamp
API 以其低開銷,確保分析不會對應用程序性能產生負面影響。
開發者現在可以更深入地了解 Angular 的內部運作,通過彩色編碼的條目來區分開發者編寫的 TypeScript 代碼和 Angular 編譯器生成的代碼。要啟用此功能,只需在應用程序中或 DevTools 控制臺中運行全局工具 ng.enableProfiling()
。這一進步提供了更直觀和全面的性能分析體驗,使開發者能夠構建性能更優的 Angular 應用程序。
在上面的截圖中,你可以看到這項功能的效果。注意在性能時序軸的底部有一個專門用于 Angular 的軌道。
通過彩色條形圖,你可以預覽組件實例化、運行變更檢測等。Angular DevTools 和 Chrome 性能時間線中的 Angular 跟蹤都使用相同的鉤子,不同之處在于 Chrome 的性能時間線可以將你的應用生命周期置于框架外部的其他 JavaScript 調用上下文中。
此外,Chrome 性能時間線中的 Angular 跟蹤顯示了一些目前在 Angular DevTools 中不存在的數據,例如組件和提供者的實例化。
框架的添加和改進
要動態創建一個 Angular 組件,你可以使用 createComponent
函數。在 v20 中,我們引入了新功能,允許你將指令應用于動態創建的組件并指定綁定:
import {createComponent, signal, inputBinding, outputBinding} from'@angular/core'; const canClose = signal(false); const title = signal('My dialog title'); // 創建 MyDialog createComponent(MyDialog, { bindings: [ // 綁定一個 signal 到 `canClose` 輸入 inputBinding('canClose', canClose), // 專門監聽 MyDialog 上的 `onClose` 事件。 outputBinding ( 'onClose', result =>console.log(result)), // 在 title 屬性上創建雙向綁定 twoWayBinding('title', title), ], directives: [ // 將 `FocusTrap` 指令應用于 `MyDialog`,無需任何綁定。 FocusTrap, // 將 `HasColor` 指令應用于 `MyDialog` 并將 `red` 值綁定到其 `color` 輸入。 // 每次檢測到變化時都會調用 `inputBinding` 的回調。 { type: HasColor, bindings: [inputBinding('color', () =>'red')] } ] });
我們在上面創建了一個對話框組件,并指定:
canClose
輸入綁定,將canClose
signal 作為值傳遞將輸出
onClose
設置為回調函數,該函數記錄發出的結果title
屬性與title
signal 之間的雙向綁定
此外,我們向組件中添加了 FocusTrap
和 HasColor
指令。請注意,我們還可以為應用于 MyDialog
的 HasColor
指令指定輸入綁定。
擴展模板表達式語法
我們一直在彌合 Angular 模板表達式與完整 JavaScript 語法之間的差距,以實現更高的表達能力和更好的開發者體驗。今天,我們引入了對冪運算符 **
和 in
運算符的支持:
{{ n ** 2 }} {{ name in person }}
在 v20 中,我們還允許您直接在表達式中使用 untagged 的模板字面量:
div>
擴展診斷為了防止常見錯誤,我們引入了靜態檢查,用于檢測無效的空值合并、檢測結構化指令的缺失導入,并在您未調用傳遞給 @for
的 track
函數時發出警告:
@Component({ template: ` @for (user of users; track trackFn) {
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.