本系列文章內容梳理自如下來源:css
官方的教程,其實已經很詳細且易懂,這裏再次梳理的目的在於複習和鞏固相關知識點,剛開始接觸學習 Angular 的仍是建議以官網爲主。html
由於這系列文章,更多的會帶有我我的的一些理解和解讀,因爲目前我也纔剛開始接觸 Angular 不久,在該階段的一些理解並不必定是正確的,擔憂會有所誤導,因此仍是以官網爲主。前端
接觸 Angular 大概一個月吧,期間寫了個項目,趁如今稍微有點時間,來回顧梳理一下。node
其實,若是前端網站並非特別複雜,那麼使用 Angular 無非也就是常跟幾個重要的知識點打交道,在官網的核心知識的第一節中就將這些知識點羅列出來了,也就是:架構概覽。typescript
畫了這個圖來大概表示下 Angular 的架構概覽,基本涉及到一些常見的重要的知識點了,好比:npm
不一樣的類型,文件名一般會都按照必定的規範來命名,以便直接看出該文件的角色。編程
固然,文件命名只是給開發人員來方便維護、辨別,對於 Angular 來講,這些都是一份份的 ts 文件代碼,因此,都須要在相對應的文件中加上一些裝飾器好比:@Directive,@Pipe,@Component,@NgModel 等這些,纔可以讓 Angular 識別出該文件的角色、用途。json
基本上,用 Angular 作一個簡單的前端項目,就是跟上面這些打交道,理清它們各自的用途及用法,還有之間的聯繫,基本上,就能夠上手進行一些開發了。bootstrap
固然,像在 Service 服務中,還會有異步編程、HttpClient 網絡編程的相關知識點;後端
在 Component 組件中,也還會有表單、動畫相關的編程知識點,這些都是須要進一步去深刻學習研究,但從整體架構上來看,就是要先了解以上這些知識點了。
一個 Angular 項目,至少會有一個模塊,即最少都會有一份用 @NgModel 聲明的 ts 文件,代表該文件做爲模塊角色,來管理其餘角色。
其餘角色包括:組件、指令、管道、服務等等,這些角色必須在模塊文件中聲明瞭,纔可以被該模塊內的其餘角色所使用,並且同一個組件、指令、管道不容許同時在多個模塊中進行聲明,只能經過模塊 exports 給其餘模塊使用。
Angular 裏的模塊,並不等同於 Android 項目中的模塊概念。
在 Android 項目代碼中,可能咱們會根據功能來進行模塊的劃分,但這個模塊僅僅是抽象上的概念,也就是建個包,把代碼都集中管理。
而 Angular 裏的模塊,不只能夠在項目結構上集中管理同一個模塊的代碼文件,還能夠爲模塊內的代碼提供一個運行的上下文。
意思就是說,不一樣模塊在運行期間互不影響,就好像各自運行在各自的沙箱容器中同樣。舉個簡單的例子,在不一樣模塊中聲明相同的變量名,或相同的 css 的類選擇器,它們之間並不會起衝突。
固然,模塊之間能夠有交互,模塊能夠依賴於另外一模塊,模塊內的能夠共享資源等等,因此,NgModel 中有許多須要配置的聲明項,好比:
在 Angular 中,大多數的模式就是,一個根模塊管理着不少功能模塊,而後,每一個模塊管理本身模塊內部所使用到的組件、指令、管道、服務、或者須要依賴於其餘模塊,若是該模塊內部的這些角色,有些能夠供其餘模塊使用,那麼就須要對外暴露。
一個項目這麼多模塊,Angular 並不會一開始就把全部模塊都加載,而是惰性加載,按需加載。
那麼,何時會去加載呢?
就是等某個模塊內部的組件被使用的時候會加載,而組件是何時會被使用的呢?
有兩個時機,一是組件被直接調用;二是觸發了路由去加載;
路由一般的配置方式是用一個 @NgModel 聲明的模塊,但只用其中兩項配置:imports 和 exports,imports 用來導入當前模塊全部組件與 url 的映射表,而 exports 用來將這些映射表信息暴露,以供相對應的模塊去引入使用。
固然,你不想抽離路由配置,直接將其配置在對應模塊的 imports 內也能夠,抽離的話,相對獨立,可維護。
區別於傳統的前端網頁的跳轉方式,Angular 項目是一個單頁應用,所謂的單頁應用就是說只有一個頁面,全部頁面的跳轉,實際上是將當前頁面的顯示內容進行替換,頁面仍舊只有一個,並不會打開新的頁面。
而頁面的跳轉,一般有如下幾種場景:
這些場景,路由的工做機制都可以很好的支持。
若是網頁很簡單,只有一個首頁,並不存在頁面跳轉場景,那麼能夠不用配置路由,只須要在 index.html 中配置根視圖,以及在根模塊的 bootstrap 中配置根視圖組件便可。
但若是項目劃分紅了多個功能模塊,那麼應該交由每一個模塊管理本身的路由表,然後選擇一個上層模塊,來統一關聯各個模塊路由,有兩種方式:一是在上層模塊的 imports 內按照必定順序來導入各個功能模塊;但這種方式想要按照路由層級來查看路由表就比較麻煩,須要到各個模塊內部去查看或者藉助一些工具。
另外一種方式是,在上層模塊的路由表中使用 loadChildren 加載各個功能模塊,而後各個功能模塊默認路由都顯示成空視圖,各自內部再經過配置 children 的路由表方式來管理各個模塊內部本身的路由表。
在 Angular 中,最常接觸的應該就是組件了。
我是這麼理解的,組件能夠是你在界面上看到的任何東西,能夠是一個頁面,能夠是頁面上的一個按鈕。
而對於瀏覽器解析並呈現前端頁面時,Html、CSS、JavaScript 這三分文件一般都是須要的,而 Angular 是使用了 TypeScript,因此一個組件,其實就包括了:Html,CSS,TypeScript。
在 Angular 中,能夠說,是以組件爲單位來組成頁面的,組件是核心,由於 Angular 提供的功能基本都是用來爲組件服務的。
以上,是個人理解。
但要注意,官網教程中,不少地方的組件描述,更多時候是傾向於表示 TypeScript 的那份文件,由於對於組件來講,TypeScript 能夠說是它的核心,CSS 只是樣式文件,Html 更相似於模板存在。
因此這裏將組件和模板放在一塊兒講,由於就像開頭那張圖同樣,組件是一份 TypeScript 文件,在該文件中,定義了這個組件的模板(template)來源和 CSS 樣式來源。
模板提供了該組件的呈現結構,而 TypeScript 裏定義了組件的數據來源及交互行爲,它們兩一塊兒組織成一個視圖呈現給用戶。
既然,這份 TypeScript 的組件文件和模板文件須要共同合做,那麼它們之間就少不了交互,因此就涉及到不少所謂的模板語法,也就是所謂的組件和模板之間的交互方式。
好比,當要往模板中嵌入 TypeScript 中的變量數據時,可使用 {{value}}
這種語法形式,一樣的,還有模板中標籤的屬性綁定,事件回調註冊的交互方式的語法。
總之,Angular 支持雙向數據綁定,是一種以數據驅動的思想來讓頁面進行交互刷新的方式,區別於傳統的前端模式。在以往,若是須要動態的更新 DOM 上的信息時,須要先獲取到相對應的元素實例對象,而後調用相應的 DOM API 來操縱 DOM;
而使用 Angular 的話,能夠直接在模板的相應元素中,將某個屬性與 TypeScript 文件中某個變量直接進行綁定,後續這個變量值變化時,Angular 會自動去更新相應 DOM 的屬性,也就是說,本來那些操縱 DOM 的代碼,Angular 幫咱們作了,咱們不用再本身去處理了。
另外,注意,以上出現的 TypeScript 的描述,你能夠理解成官網中的組件,我之因此不想用組件的方式來進行描述,是由於,我以爲,組件是一個總體,它自己就包括了 TypeScript 文件和模板文件,因此官網中說的組件和模板的交互,我以爲,換成組件中的 TypeScript 文件與模板文件的交互更爲適合。
固然,這只是我目前階段的理解。
服務是一個廣義上的概念,一般用來處理那些跟 UI 交互無關的事情,好比網絡請求的工做等。
因此它也是爲組件服務,並且 Angular 有一套依賴注入機制,也就是說,組件只須要告訴 Angular,它須要哪些服務,至於這些服務的實例是何時建立,交給誰去管理等這些組件內部都不用本身去處理了。
Angular 會自動建立相關的服務實例,而後在組件適當的時候,將這個實例注入給組件去使用。
這種模式跟之前在 Android 端開發時有所區別,在 Android 端中,當須要業務層某個實例對象時,一般都須要本身內部去初始化,或者這個實例是個單例的話,也須要本身去實現單例。
但在 Angular 中,你能夠藉助它依賴注入的機制,來讓 Angular 幫你去作這些依賴的對象的實例管理的事,若是須要一個全局的單例服務,那麼能夠將該服務聲明成 root 即全局可用;若是須要一個模塊內的單例,那麼能夠在該模塊的 providers 中聲明該服務;若是須要一個組件本身的實例對象,那麼能夠在組件的元數據塊的 providers 中配置該服務。
總之,就是,跟 UI 交互無關的工做,能夠抽到服務中去處理,而該服務實例的管理,交給 Angular 就能夠了,組件只須要告訴 Angular 它須要哪一種形式的服務便可。
那麼,組件是怎麼告訴 Angular 的呢?
一樣在 Android 項目或者後端項目中,也有一些依賴注入框架,那些一般都是藉助註解的方式來實現。
但在 Angular 中,不用這麼麻煩,直接在組件的構造函數的參數中,聲明某個服務類型的參數便可。
指令也是爲組件服務的,可是,是在組件的模板文件中來使用。
由於組件的模板,其實就是一份 HTML 文件,基於 HTML 的標籤之上,加上一些 Angular 的模板語法,而 Angular 在將這份 HTML 文件代碼交給瀏覽器解析以前,會先自行解析一遍,去將模板中不屬於 HTML 的那些語法解析出相應的行爲。
而指令分爲結構型指令和屬性型指令,它們的區別,其實就在於,一個是改變 DOM 的結構,一個是改變 DOM 元素的樣式。
因此說,指令的目的,其實就是簡化一些操縱 DOM 的工做,好比你須要讓某些按鈕都具備統一的行爲和樣式,當被點擊時先作什麼,再作什麼。
實現這個,你固然能夠在 TypeScript 中去書寫這些邏輯,但要應用到每一個按鈕上,就比較繁瑣。
這個時候,就能夠將這些工做都封裝到指令內部,而後在每一個按鈕標籤上加上該指令,Angular 在解析模板時,發現了這個指令,就會爲每一個按鈕都加上這麼一段程序邏輯。
我我的以爲,指令的功能,讓咱們處理一些相同的行爲,能夠更好的去封裝,減小冗餘和繁瑣。
固然,上面舉的場景,也能夠本身封裝個按鈕組件,而後在其餘模板中,不使用原生按鈕,而使用封裝後的按鈕組件,也能夠達到目的。
因此,組件其實也是指令的一種,但組件的實現方式會比較重,有時候,只須要封裝一些簡單的行爲邏輯,就能夠直接藉助指令的方式封裝。
指令的原理也很簡單,在模板中某個元素標籤上,添加上某個指令後,解析到這個指令時,會進入這個指令的相關工做,而指令內部,會獲取到一個當前指令掛載的元素標籤對象,既然都拿到這個對象了,那麼,在指令內部想對這個元素作什麼,均可以了。
指令還有另外一個通途,一般用來擴展原有的功能,由於可能項目中,在模板裏使用的組件或者 HTML 元素的標籤由於種種原生無權或不方便進行修改,而又想在其基礎上擴展一些功能,此時就能夠利用指令來實現。
管道一樣是爲組件服務,也一樣是在組件的模板文件中來使用。
它的用途,在於,將數據按照必定的規則進行轉換,好比 Object 對象,轉換成 json 格式數據,再好比,long 型的時間,轉換成具體的時間日期等等。
Angular 中已經內置了一些管道,也能夠自定義管道。
大概瞭解了 Angular 的架構概覽,接下去就來看看一個簡單的 Angular 項目結構,以及各個文件、模塊的用途,稍微講一下。
這是用 WebStrom 建立一個 Angular 項目後,自動生成的簡單架構。
在利用 Angular Cli 工具生成腳手架時,默認就已經生成了不少配置項,並且此時,項目已是能夠運行的,由於也自動生成了一個根模塊和根視圖,默認頁面是 Angular 的歡迎界面。
挑幾個來說講。
這是 Angular-CLI 的配置文件,而 Angular-CLI 是自動化的工程構建工具,也就是利用這個工具,能夠幫助咱們完成不少工做,好比建立項目、建立文件、構建、打包等等。
本來的 HTML、CSS、JavaScript 的前端開發模式,並無工程的概念,只要用瀏覽器打開 HTML 文件就可以運行。而 Angular 引入了 TypeScript,Scss 等瀏覽器並不沒法識別的語言,天然,要讓瀏覽器運行 Angular 項目以前,須要進行一次編譯,一次轉換。
這些工做就能夠藉助 Angular-CLI 來進行。另外,建立一個模塊,建立一個組件,也均可以經過 Angular-CLI 來。
那麼,在建立這些文件或者說,打包編譯這些項目文件時,該按照怎樣的規則,就是參照 angular.json 這份配置文件。
大概看一下內容:
{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", // 默認的配置項,好比默認配置了 ng g component 生成組件時應該生成哪些文件等等 "version": 1, "newProjectRoot": "projects", "projects": { "daView": { // 項目的配置 "root": "", "sourceRoot": "src", // 源代碼路基 "projectType": "application", // 項目的類型,是應用仍是三方庫(library) "prefix": "app", // 利用命令生成 component 和 directive 的前綴 "schematics": {}, // 替換掉第一行的 schema.json 中的一些默認配置項,不如建立組件時,不要生成spec文件 "architect": { // 執行一些構造工做時的配置 "build": { // 執行 ng build 時的一些配置項 "builder": "@angular-devkit/build-angular:browser", "options": { "outputPath": "dist/daView", // 編譯後的文件輸出的位置 "index": "src/index.html", // 構建所需的模板 Index.html "main": "src/main.ts", // 構建所需的文件 "polyfills": "src/polyfills.ts", // 構建所需的文件 "tsConfig": "src/tsconfig.app.json", // 對 typescript 編譯的配置文件 "assets": [ // 構建所需的資源 "src/favicon.ico", "src/assets" ], "styles": [ // 構建所需的樣式文件,能夠是 scss "src/styles.css" ], "scripts": [] // 構建所需的三方庫,好比 jQuery }, "configurations": {/*...*/} }, "serve": {/*...*/}, // 執行 ng serve 時的一些配置項 "extract-i18n": {/*...*/}, "test": {/*...*/}, "lint": {/*...*/} } } }, "daView-e2e": {/*...*/}, "defaultProject": "daView" }
因此,利用 Angular-CLI 生成的初始項目中,有許多基本的文件,這些文件,基本也都在 angular.json 中被配置使用了,每一個配置文件基本都有各自的用途。
好比,tslint 用來配置 lint 檢查,tsconfig 用來配置 TypeScript 的編譯配置,其餘那些 html,css,ts,js 文件基本都是 Angular 項目運行所需的基礎文件。
對於一個工程項目來講,依賴的三方庫管理工具也很重要,在 Android 項目中,一般是藉助 Gradle 或 maven 來管理三方庫。
而在 Angular 項目中,是使用 npm 來進行三方庫的管理,對應的配置文件就是 package.json。
在這份配置文件中,配置了項目所須要的三方庫,npm 會自動去將這些三方庫下載到 node_modules
目錄中。而後,再去將一些須要一塊兒打包的三方庫在 angular.json 中進行配置。
以上就是利用 Angular-CLI 建立項目生成的初始架構中各個文件的大概用途,下面講講 Angular 項目的大概運行流程。
在 src 中的 index.html
文件就是單頁應用的頁面文件,裏面的 body 標籤內,自動加入了一行根視圖的組件:
<app-root></app-root>
就是根組件 AppComponent (自動生成的)的組件標籤,當 Angular 在 HTML 文件中發現有組件標籤時,就會去加載該組件所屬的模塊,並去解析組件的模板文件,將其嵌入到 HTML 文件的組件標籤中。
看一下自動生成的根模塊的部份內容:
//app.module.ts import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
//app.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'daView'; }
app.module.ts
文件用 @NgModule 表示該文件角色是模塊,並在內部配置了它的組件 AppComponent,這樣 AppComponent 組件就只屬於該模塊了,並可以在該模塊內的其餘組件中被使用。
另外,因爲該模塊是根模塊,因此還須要配置 bootstrap,設置應用的根視圖,這個配置須要和 index.html
裏的 body 標籤內的根視圖組件是同一個組件,不然運行時就會報錯了。
當項目中模塊多了的時候,各模塊之間基本是經過路由或者組件來進行相互關聯。
好比,咱們新建立個 Home 模塊,而後在根模塊中建立個 app-routing 路由配置文件:
//app-routing.module.ts import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = [ { path: 'home', loadChildren: './home/home.module#HomeModule' } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
而後在 app.module.ts 的 imports 中將該路由配置導入,這樣當路由到 home 時,會去加載 home 模塊,而後看看 home 模塊的路由配置:
//home-routing.module.ts import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import {HomeComponent} from './home.component'; import {HomeCenterComponent} from './component/home-center.component'; const routes: Routes = [ { path: '', children: [ { path: '', component: HomeCenterComponent } ] } ]; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule] }) export class HomeRoutingModule { }
home 模塊的默認視圖爲空,但交由其子視圖來控制,因此,當導航到 home 時,home 模塊會去加載它內部的 HomeCenterComponent 組件。
以上,是當項目中有多模塊時,個人處理方式。
當按照這種方式來實現時,對於瞭解一個 Angular,就有必定的規律可循了:
你們好,我是 dasu,歡迎關注個人公衆號(dasuAndroidTv),公衆號中有個人聯繫方式,歡迎有事沒事來嘮嗑一下,若是你以爲本篇內容有幫助到你,能夠轉載但記得要關注,要標明原文哦,謝謝支持~