模板語(yǔ)法
模板是編寫(xiě) Angular 組件最重要的一環(huán),你至少需要深入理解以下知識(shí)點(diǎn)才能玩轉(zhuǎn) Angular 模板:
- 對(duì)比各種 JS 模板引擎的設(shè)計(jì)思路
- Mustache(八字胡)語(yǔ)法肿轨,
{}
- 模板內(nèi)的局部變量
- 屬性綁定掘宪、事件綁定战惊、雙向綁定
- 在模板里面使用結(jié)構(gòu)型指令 ngIf条辟、ngFor精盅、ngSwitch
- 在模板里面使用屬性型指令 NgClass媒熊、NgStyle奇适、NgModel
- 在模板里面使用管道格式化數(shù)據(jù)
對(duì)比各種 JS 模板引擎的設(shè)計(jì)思路
什么是模板引擎?
是指利用某種模板語(yǔ)言將頁(yè)面制成模板芦鳍,再依據(jù)業(yè)務(wù)邏輯將該模板語(yǔ)言翻譯成業(yè)務(wù)數(shù)據(jù)嚷往,從而生成最終展示頁(yè)面。 簡(jiǎn)單的講就是根據(jù)靜態(tài)HTMl元素和業(yè)務(wù)邏輯處理的數(shù)據(jù)結(jié)合渲染生成瀏覽器器需要的頁(yè)面柠衅。
- jQuery -> Handlebars
- React -> jsx 模板寫(xiě)法
- Angular -> 與“指令”緊密結(jié)合的模板語(yǔ)法 (vue 也是一樣)
各種模板引擎的唯一目標(biāo)就是提高開(kāi)發(fā)效率皮仁、縮短時(shí)間成本。綜合來(lái)說(shuō)茄茁,無(wú)論是哪一種前端模板魂贬,大家都比較推崇“輕邏輯”( logic-less )的設(shè)計(jì)思路。
何為“輕邏輯”?
簡(jiǎn)而言之裙顽,所謂“輕邏輯”就是說(shuō)付燥,你不能在模板里面編寫(xiě)非常復(fù)雜的 JavaScript 表達(dá)式。比如愈犹,Angular 的模板語(yǔ)法就有規(guī)定:
- 你不能在模板里面 new 對(duì)象
- 不能使用=键科、+=、-=這類(lèi)的表達(dá)式
- 不能用++漩怎、--運(yùn)算符
- 不能使用位運(yùn)算符
為什么要“輕邏輯”勋颖?
- 本身的 Html 是不識(shí)別 if 、for 等操作的勋锤;模板引擎處于的地位就是幫忙編譯轉(zhuǎn)換純HTMl元素饭玲。
- 在 HTML 加入復(fù)雜的邏輯,會(huì)加大模板引擎編譯的時(shí)間和計(jì)算能力叁执;而且也不建議復(fù)雜邏輯處理放到HTMl里來(lái)做茄厘,保證 HTML 的單一純潔性;使用模板也是來(lái)做簡(jiǎn)單的插值相關(guān)的操作
- JS 版的編譯器需要在瀏覽器里面運(yùn)行谈宛,搞得太復(fù)雜瀏覽器拖不動(dòng)次哈!
- 最根本還是
模板引擎不夠強(qiáng)
, 并不是萬(wàn)能的;
對(duì)于 Angular 來(lái)說(shuō)吆录,強(qiáng)調(diào)“輕邏輯”還有另一個(gè)原因:在組件的整個(gè)生命周期里面窑滞,模板函數(shù)會(huì)被執(zhí)行很多次。你可以想象, Angular 每次要刷新組件的外觀的時(shí)候哀卫,都需要去調(diào)用一下模板函數(shù)巨坊,如果你在模板里面編寫(xiě)了非常復(fù)雜的代碼,一定會(huì)增加渲染時(shí)間聊训,用戶(hù)一定會(huì)感到界面有“卡頓”抱究。
人眼的視覺(jué)延遲大約是100ms到400ms之間恢氯,如果整個(gè)頁(yè)面的渲染時(shí)間超過(guò)400ms带斑,界面基本上就卡得沒(méi)法用了。有一些做游戲的開(kāi)發(fā)者會(huì)追求60fps刷新率的細(xì)膩感覺(jué)勋拟,60分之1秒約等于16.7ms勋磕,如果 UI 整體的渲染時(shí)間超過(guò)了16.7ms,就沒(méi)法達(dá)到這個(gè)要求了敢靡。
輕邏輯( logic-less )帶來(lái)了效率的提升挂滓,也帶來(lái)了一些不方便,比如很多模板引擎都實(shí)現(xiàn)了 if 語(yǔ)句啸胧,但是沒(méi)有實(shí)現(xiàn) else赶站,所以開(kāi)發(fā)者們?cè)诰帉?xiě)復(fù)雜業(yè)務(wù)邏輯的時(shí)候模板代碼會(huì)顯得非常啰嗦。
目前來(lái)說(shuō)纺念,并沒(méi)有完美的方案能同時(shí)兼顧運(yùn)行效率和語(yǔ)法表現(xiàn)能力贝椿,這里只能取一個(gè)平衡。
Mustache 語(yǔ)法
Mustache 語(yǔ)法也就是你們說(shuō)的雙花括號(hào)語(yǔ)法{{...}}陷谱。
關(guān)于 Mustache 語(yǔ)法烙博,你需要掌握3點(diǎn):
- 它可以獲取到組件里面定義的屬性值。
- 它可以自動(dòng)計(jì)算簡(jiǎn)單的數(shù)學(xué)表達(dá)式烟逊,例如:加減乘除渣窜、取模
- 它可以獲得方法的返回值。
插值語(yǔ)法關(guān)鍵代碼實(shí)例:
<h3>
歡迎來(lái)到{{title}}宪躯!
</h3>
public title = 'Mustache 語(yǔ)法';
簡(jiǎn)單的數(shù)學(xué)表達(dá)式求值:
<p>1 + 5 = {{ 1 + 5 }}</p>
調(diào)用組件里面定義的方法:
<p> {{ getStr() }} </p>
public getStr(): string {
return '調(diào)用方法乔宿!'
}
屬性綁定([屬性名])
屬性綁定是用方括號(hào)來(lái)做的,寫(xiě)法:
<img [src]="imgSrc" />
<button [disabled]="isUnchanged">是否禁用</button>
public imgSrc:string = './images.png';
public isUnchanged:boolean = true;
很明顯访雪,這種綁定是單向數(shù)據(jù)綁定详瑞,單一的讀取值而已。
事件綁定 ( (事件名) )
事件綁定是用圓括號(hào)來(lái)做的冬阳,寫(xiě)法:
<button (click)="btnClick($event)">點(diǎn)擊事件</button>
pubic btnClick(e):void {
alert('點(diǎn)擊事件測(cè)試~');
}
雙向綁定 ( [(...)] )
你經(jīng)常需要顯示數(shù)據(jù)屬性蛤虐,并在用戶(hù)作出更改時(shí)更新該屬性。
在元素層面上肝陪,既要設(shè)置元素屬性驳庭,又要監(jiān)聽(tīng)元素事件變化。
雙向綁定是通過(guò)方括號(hào)里面套一個(gè)圓括號(hào) [(...)]
來(lái)做的,模板寫(xiě)法:
<app-sizer [(size)]="fontSizePx"></app-sizer>
public fontSizePx:number = 14;
模板內(nèi)的局部變量
在模板里面使用結(jié)構(gòu)型指令
Angular 有3個(gè)內(nèi)置的結(jié)構(gòu)型指令:*ngIf
饲常、*ngFor
蹲堂、ngSwitch
。ngSwitch
的語(yǔ)法比較啰嗦贝淤,使用頻率小一些, 了解就好柒竞。
*ngIf 代碼實(shí)例:
<p *ngIf="isShow">顯示還是不顯示?</p>
<button (click)="toggleShow()">控制顯示隱藏</button>
public isShow:boolean=true;
public toggleShow():void {
this.isShow = !this.isShow;
}
*ngFor 代碼實(shí)例:
<ul>
<li *ngFor="let item of items; let i = index;">
{{i+1}} - {{ item.name }}
</li>
</ul>
public items:Array<any>=[
{name:"PyCoder"},
{name:"Jun ting"},
{name:"EcmaScript"}
];
*ngSwitch 代碼實(shí)例:
<div [ngSwitch]="mapStatus">
<p *ngSwitchCase="0">下載中...</p>
<p *ngSwitchCase="1">正在讀取...</p>
<p *ngSwitchDefault>系統(tǒng)繁忙...</p>
</div>
public mapStatus:number = 1;
特別注意:一個(gè) HTML 標(biāo)簽上只能同時(shí)使用一個(gè)結(jié)構(gòu)型的指令播聪。
因?yàn)椤敖Y(jié)構(gòu)型”指令會(huì)修改 DOM 結(jié)構(gòu)朽基,如果在一個(gè)標(biāo)簽上使用多個(gè)結(jié)構(gòu)型指令,大家都一起去修改 DOM 結(jié)構(gòu)离陶,到時(shí)候到底誰(shuí)說(shuō)了算稼虎?
那么需要在同一個(gè) HTML 上使用多個(gè)結(jié)構(gòu)型指令應(yīng)該怎么辦呢?有兩個(gè)辦法:
- 加一層空的 div 標(biāo)簽進(jìn)行包裹
- 加一層
<ng-container>
在模板里面使用屬性型指令
使用頻率比較高的3個(gè)內(nèi)置指令是:NgClass
招刨、NgStyle
霎俩、NgModel
。
NgClass 使用案例代碼:
<div [ngClass]="currentClasses">同時(shí)批量設(shè)置多個(gè)樣式</div>
<button (click)="setCurrentClasses()">設(shè)置</button>
public currentClasses: [];
public canSave: boolean = true;
public isUnchanged: boolean = true;
public isSpecial: boolean = true;
public setCurrentClasses ():void {
this.currentClasses = {
'saveable': this.canSave,
'modified': this.isUnchanged,
'special': this.isSpecial
}
}