Angular v12 來了!!

YEAH!!!

今天一早準備工作時,看到令人興奮的好消息!那就是 ─ Angular v12 已經正式 Release 了!!

官方文章在此:Angular v12 is now available

幫看到英文就倒退三百步的朋友總結一下幾個重點:

Ivy Everywhere

為了更靠近 Ivy Everywhere 這個目標, Angular 棄用了舊的 Compiler - View Engine ,不過這對於一般開發者來說沒什麼影響,主要是如果你是身為 Library 的作者且是使用 View Engine 來 Compile 的話,可以開始著手準備使用 Ivy 來 Compile 了。

為此,官方有提供實作細節

Protractor 的未來

關於 Angular 生態圈所標配的 E2E 測試框架 ─ Protractor 的未來,目前官方決定將從 v12 開始不再預設包含 Protractor ,而是與其他夥伴合作(目前官方已收到的夥伴回音有 CypressTestCafeWebdriverIO 。如果你們公司也有在做這塊且有興趣,可以跟官方團隊聯繫),並透過 Angular CLI 的 schematic/builder 與 Angular 緊密結合。

官方堅信,透過與其他夥伴的合作以及擁抱社群的力量,可以使 Angular 變得更加強大!對於細節有興趣的朋友可以閱讀官方的RFC(請求意見稿)

Nullish 操作符

從 v12 開始,開發者們可以在 Angular 的 Template 裡使用 Nullish 操作符 ─ ??(沒錯,就是很香很甜的語法糖)了!

透過使用 Nullish 操作符,可以讓你的 code 看起來更加 Clean (雖然每個人、每個團隊對於 Clean Code 的定義都不一樣,但官方是這麼認為,我也是)。

舉個例子:

1
{{ age !== null && age !== undefined ? age : calculateAge() }}

使用 Nullish 操作符後,你的 code 像這樣:

1
{{ age ?? calculateAge() }}

謎之聲:耶!令人舒爽的程式碼!

樣式相關的改進

關於樣式方面,從 v12 開始,只要在 angular.json 裡加上 inlineStyleLanguage: "scss" 的設定,就可以在 Component 的 styles 裡寫 SCSS 了,雖然關於這點我其實覺得還好,因為我個人不喜歡將 Template 或是樣式直接寫在 *.component.ts 裡。

不過令人興奮的是, Angular CDKAngular Material 現在內部已經改使用 Sass 新的 Module System 了!與此同時, Angular CDKAngular Material 所外露的 API 介面也已經改採用 @use 語法,官方文件 也已經同步更新(尷尬,破版了XDD)。

不過如果你的應用程式是建立在 Angular v12 以下,但有使用到新版的 Angular CDK 或是 Angular Material 的話,可以使用 ng update 的指令讓 Angular CLI 幫你自動更新所有使用於 Angular CDKAngular Material@import 聲明(Before and After),並記得將你的 NPM 套件從 node-sass 改為 sassnode-sass 已經沒有持續更新且維護了(如果你是使用 Angular v12 則不用做此行為,內建已經是 sass 了)。

編譯模式預設為 Production 模式

v12 之後, ng build 指令將會預設為 Production 模式,意即開發者們不用再加上 --prod 來強調要編譯 Production 模式的檔案,減少多餘的步驟並防止將開發模式的程式碼誤植到 Production 上的意外發生。

預設開啟嚴格模式

v12 之後,將預設開啟 TypeScript 的嚴格模式,嚴格模式會幫助開發者們儘早發現錯誤。

關於嚴格模式可以閱讀 Angular 官方文件Angular 官方部落格的文章

Angular Language Service 預設為 Ivy-based

v12 之後,將原先 Angular Language Service 可以選擇性使用 Ivy-based 改為預設使用。

官方還有提供了一部影片 是關於Angular Language Service with Ivy 的,有興趣的朋友也可以看看。

使用 Webpack 5

從 v11 開始, Angular 其實就有實驗性地在支援 Webpack 5 ;但從 v12 開始,就正式支援啦!!!(灑花)

TypeScript v4.2

v12 預設所採用的 TypeScript 是 v4.2 ,關於 v4.2 的細節可以閱讀官方的文章

棄用對 IE 11 的支援

從 v12 開始,官方將棄用對於 IE 11 的支援,並預計將在 v13 時正式將其移除。關於此決定可以觀看 官方的 RFC(請求意見稿)

其他更新

其實其他還有一些滿重要的更新與調整,不過上述幾點是我個人擷取出來,覺得對一般開發者來說比較重要且有感的更新,所以關於其他的更新,可以直接閱讀官方的文章,抑或是直接看官方的 CHANGELOG


以上,就是今天李歐想要分享給大家的資訊,感謝您的收看!

如果您喜歡我的文章,歡迎按讚、訂閱以及分享,我們下次見囉,拜拜~!(好 Youtuber 式的結尾XD)

Angular 之 Injector 探討

關於 Angular 的 Injector ,雖然我之前在寫「Angular 深入淺出三十天」的系列文時,就有在基礎結構說明(四)這篇文章裡介紹過,不過今天我在跟朋友們聊到相關的話題時,除了好好的做了個實驗,並把程式碼保留下來之外,也忽然起了個念頭,覺得是該把這個問題寫成文章,讓剛學 Angular 或是對 Angular 的了解沒那麼深的朋友們可以比較容易理解、比較不會用錯(就我個人的經驗來看,沒有注意到這個部分的朋友們還滿多的)。


問題描述

在 Angular 裡寫 Service 的時候,我們一般會用以下三種方式註冊它:

  1. 在該 Service 的 @Injectable() 裡加上 { providedIn: 'root' } 的 Metadata ( Angular 7+ 推薦寫法),抑或者在 AppModule 的 providers 裡註冊它( Angular 6- 的寫法)。
  2. 在相關的功能模組的 providers 裡註冊它。
  3. 在某個 Component 的 providers 裡註冊它。

其實在 Angular 9 之後的版本,除了 providedIn: 'root' 之外,還多了 'platform''any' 這兩個選擇,不過此篇文章不討論這個部分。

這三種註冊方式其中的第一種與第三種,大家比較不會有問題,比較會有問題的是第二種,很多人可能會覺得,我使用起來明明就是同一個實體呀?怎麼官方說是不同實體呢?

實驗開始

為了釐清這個問題,我做了個實驗,程式碼在這裡:https://stackblitz.com/edit/angular-ivy-5fdo5y?file=src/app/app.component.html

在這個實驗裡,我用了四個 Module ─ AboutModuleContactModuleHomeModuleWhateverModule 與兩個 Service ─ ASserviceBService 來模擬大部分會遇到的情況:

  • AService 使用 providedIn: 'root' 註冊。
  • HomeModuleContactModuleWhateverModule 都 import 到 AppModule 裡。
  • AboutModuleLazyLoading 的方式載入。
  • BService 則分別註冊在 ContactModuleHomeModuleAboutModuleproviders 裡與 WhateverComponentproviders 裡。

結果:

實驗結果

有看懂嗎? BServiceHomeComponentContactComponet 裡是用同一個實體,這是因為他們都有被 import 到 AppModule 裡,這時 AppModule(上層 Module)會接手 ContactModuleHomeModule(子層 Module)的 injector,所以他們雖然有分別註冊 BService ,但實際上還是共用了同一個實體。

這部份其實官網文件有寫到:

官網文件

網址在這:https://angular.io/guide/hierarchical-dependency-injection#moduleinjector

不過我這裡只有實驗一層的情況,如果 Module 一層一層的接下去,是不是也是一樣的狀況我就沒有特別做實驗了,大家可以自己玩看看。

至於 AboutComponentWhateverComponent 呢,很明顯都是使用不同的
BService 的實體,前者因為使用 LazyLoading 的關係,跟 AppModule 沒有直接關聯,後者則是因為是使用了第三種註冊方式的關係。

結論

總而言之,官方之所以提供了不同的註冊方式給我們,就是希望我們可以在不同的情況下使用不同的註冊方式來解決我們的問題,只要能夠熟知這其中的差異,就可以寫出好維護又高品質的系統囉!

我的 Nx 筆記 - tsconfig.json 的坑

我最近在研究 Nx 時遇到了一個坑,這個坑讓我卡了兩、三個小時,所以我將這件事情筆記下來,希望如果以後有人遇到類似的問題,這篇筆記多少可以幫得上忙。


問題描述

在 Angular 專案裡我們經常會看到各種 tsconfig.json ,而這些 tsconfig.json 又繼承來繼承去的,雖然一般我們不會特別去動他們的設定,但當我們因為某些緣故需要去調整的時候,就要特別小心「覆蓋」的問題。

而我遇到的狀況是,我在使用 JSZip 這個套件時,遇到了下圖中的 Error :

JSZip 的 Error

解決辦法是在 apps/app-name/tsconfig.app.jsoncompilerOptions 裡加上 path 的配置:

1
2
3
4
5
6
7
8
9
10
11
12
{
"//": "...",
"compilerOptions": {
"//": "...",
"paths": {
"jszip": [
"node_modules/jszip/dist/jszip.min.js"
]
}
}
"//": "..."
}

加上之後, JSZip 的問題解決了,但變成下圖中的 Error :

找不到 libs 裡的 Module

我百思不得其解,因為 ng serve 沒有問題,但 build 的時候就會說找不到,而我也試了很多方式都解不掉這個問題。

後來我想到,會不會是因為設定會被覆蓋的關係?!

因為原本 libs 裡的 import 路徑會在專案根目錄的 tsconfig.json 裡設定,而 tsconfig.json 們會像下圖這樣繼承配置:

tsconfig.json 的繼承方式示意圖

  1. apps/app-name/tsconfig.json 繼承 ./tsconfig.json 裡的配置 。
  2. apps/app-name/tsconfig.app.json 再繼承 apps/app-name/tsconfig.json 裡的配置。

因此,當我們一開始在 apps/app-name/tsconfig.app.json 加上因應 JSZippaths 配置時,就覆蓋掉原本在 ./tsconfig.json 裡的 paths 配置,造成找不到 libs 裡 export 的 module 的問題。

解決辦法

解決這個問題的辦法基本上有兩個:

  1. apps/app-name/tsconfig.app.jsonpaths 補上原本在 ./tsconfig.jsonpaths 配置。
  2. JSZip 的配置從 apps/app-name/tsconfig.app.json 移到 ./tsconfig.json 裡。

相對來說,第二個方式絕對會比第一個方式好得多。

結論

一般來說,其他的 tsconfig.json 都不太會需要去動,因此只要我們養成習慣,都只在修改在專案根目錄下的 tsconfig.json 的配置就不會有問題了。

參考連結

我的 Nx 筆記 - 如何在你的 Application 裡,用很漂亮的方式引入 Library 的 SCSS

Nx

最近在開始研究 Nx 這個非常好用的開發工具,當然也撞了不少牆…(鼻青臉腫中)。

今天主要要分享的是:

如何在你的 Application 裡,用很漂亮的方式引入 Library 裡的 SCSS

而這個方法是我在這個 issue 裡看到的,留言的人是 tcoz

在開始之前,請先準備 Nx 的專案,且裡面有一個 Application 名為 sample 、 一個 Library 名為 sample-lib ,然後在 sample-lib 裡準備一個 abc.scss

scss 的內容很簡單:

1
2
3
h1 {
color: red;
}

接著把 sample 跑起來,應該會看到以下畫面:

sample 的起始畫面

然後先在 Nx 的 angular.json 裡,找到你的專案的 build 配置,大概長這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"//": "...",
"projects": {
"sample": {
"//": "...",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/apps/sample",
"index": "apps/sample/src/index.html",
"main": "apps/sample/src/main.ts",
"polyfills": "apps/sample/src/polyfills.ts",
"tsConfig": "apps/sample/tsconfig.app.json",
"aot": false,
"assets": ["apps/sample/src/favicon.ico", "apps/sample/src/assets"],
"styles": ["apps/sample/src/styles.scss"],
"scripts": []
},
},
"//": "...",
}
}
},
"//": "...",
}

然後加上 stylePreprocessorOptionsextractCss 的配置,像這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"//": "...",
"projects": {
"sample": {
"//": "...",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"//": "...",

"stylePreprocessorOptions": {
"includePaths": ["libs/sample-lib/src/lib/scss"]
},
"extractCss": true,

"//": "...",
},
},
"//": "...",
}
}
},
"//": "...",
}

includePaths 裡的路徑就是要處理的 SCSS 的資料夾路徑。

如此一來,就可以到 sample 裡的 SCSS 裡 import (我在 sample 裡的 style.scss import ):

1
@import "abc";

結果:

import 了 abc 之後的結果

參考資料

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×