使用Angular CLI時的6個最佳實踐和專業技巧

使用Angular CLI開發angular應用程序是一種很是愉快的體驗!Angular團隊爲咱們提供了使人驚歎的CLI,它支持了任何重要項目開箱即用所需的大部分東西。javascript

規範化的項目結構與全面的測試能力(包括單元測試和端到端測試),腳手架,支持使用特定的環境配置去構建產品。這在構建每個新項目時候節約了大量時間。感謝Angular團隊!?css

雖然Angular CLI的工做的很好,但咱們能夠利用一些潛在的配置和最佳實踐使咱們的項目更好!前端

咱們將要學習什麼?

  1. 具備Core(核心),Shared(共享),lazy-loaded Feature modules(延遲加載功能模塊)體系結構的最佳實踐
  2. 爲app和environments文件夾使用別名來支持更乾淨的導入
  3. 爲何和如何使用Sass ,Angular Material
  4. 如何組織好的產品構建方式
  5. 如何向PhantomJS說再見以及使用Headless Chrome來替代(測試)
  6. 如何發佈咱們的項目經過自動生成更新日誌和正確的版本號

1. 關於項目的的文件結構

好的, 咱們使用Angular CLI生成了新的項目,可是如今呢?咱們應該繼續在一些隨機文件夾生成咱們的服務和組件嗎?咱們應該如何組織咱們的項目?java

一個好的指導原則應該是遵循將應用程序分紅至少三個不一樣的模塊: Core(核心模塊), Shared(共享模塊) 和 Feature(功能模塊) (不過咱們可能須要更多的功能模塊。?).git

核心模塊

全部服務都應該在覈心模塊實現。典型的例子好比認證服務或用戶服務。讓咱們看個例子。github

/* 3rd party libraries */
import { NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';

/* our own custom services  */
import { SomeSingletonService } from './some-singleton/some-singleton.service';

@NgModule({
  imports: [
    /* 3rd party libraries */
    CommonModule,
    HttpClientModule,
  ],
  declarations: [],
  providers: [
    /* our own custom services  */
    SomeSingletonService
  ]
})
export class CoreModule {
  /* make sure CoreModule is imported only by one NgModule the AppModule */
  constructor (
    @Optional() @SkipSelf() parentModule: CoreModule
  ) {
    if (parentModule) {
      throw new Error('CoreModule is already loaded. Import only in AppModule');
    }
  }
}

共享模塊

全部的「dumb(啞)」組件和管道都應該在這裏實現。這些組件不能從核心模塊或其餘特性模塊的構造函數中的導入和注入服務。它們應該使用組件的模板中的屬性接收全部數據。這一切都歸結到這一事實,SharedModule(共享模塊)沒有任何依賴於咱們的應用程序的其他部分。web

這也是導入和從新導入Angular Material 組件庫的完美場所。chrome

/* 3rd party libraries */
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule  } from '@angular/forms';
import { MdButtonModule } from '@angular/material';

/* our own custom components */
import { SomeCustomComponent } from './some-custom/some-custom.component';

@NgModule({
  imports: [
    /* angular stuff */
    CommonModule,
    FormsModule,

    /* 3rd party components */
    MdButtonModule,
  ],
  declarations: [
    SomeCustomComponent
  ],
  exports: [
    /* angular stuff */
    CommonModule,
    FormsModule,

    /* 3rd party components */
    MdButtonModule,

    /* our own custom components */
    SomeCustomComponent
  ]
})
export class SharedModule { }

如何使用Angular CLI編寫項目結構

咱們能夠在建立新項目後當即生成核心和共享模塊。這樣,咱們就能夠從一開始就準備生成額外的組件和服務。npm

運行命令ng generate module core生成核心模塊。而後在core文件夾建立index.ts文件,再從新導出CoreModule。咱們將從新導出額外的公共服務,這些服務在整個開發過程當中均可以使用。 json

完成後,咱們能夠對shared module(共享模塊)執行一樣的操做。

功能模塊

咱們將爲應用程序的每個獨立特性建立多個功能模塊。Feature modules(功能模塊)應該只能從CoreModule導入服務。若是功能模塊A須要從功能模塊B導入服務,能夠考慮將該服務遷移到CoreModule。

在某些狀況下,須要只是某些功能模塊共享的服務,將它們移動到核心是沒有意義的。在這種狀況下,咱們能夠建立特殊的共享功能模塊,如本文後面所述。

經驗法則是: 咱們建立的功能模塊儘可能不依賴其餘功能模塊,僅僅服務由CoreModule提供,組件由SharedModule提供

這將保持咱們的代碼乾淨,易於維護和擴展的新功能。它還減小了重構所需的工做量。若是遵循得當,咱們將確信對一個功能的更改不會影響或破壞咱們的應用程序的其他部分。

延遲加載

咱們應該儘量延遲加載咱們的功能模塊。理論上,只有一個功能模塊應該在應用程序啓動時同步加載以顯示初始內容。每一個其餘功能模塊應該在用戶觸發導航後緩慢加載。

2. app 和 environments 的別名使用

咱們的app 和 environments文件夾使用別名將使咱們可以實現乾淨的導入,在咱們的應用程序這將是一致的。

假設,但一般狀況。咱們正在研究一個組件,它位於功能模塊A中的三個文件夾深處,咱們但願從核心模塊中導入位於兩個文件夾深處的服務。這將致使導入聲明看起來有點像import { SomeService } from '../../../core/subpackage1/subpackage2/some.service'

絕對不是最乾淨的導入申明…

更糟糕的是,每當咱們想改變這兩個文件中任何一個的位置時,咱們的導入語句都會中斷。相比之下,更短的導入申明import { SomeService } from "@app/core"。看起來更好,不是嗎?

可以使用別名必須添加URL地址和路徑屬性,咱們tsconfig.json文件像這樣…

{
  "compilerOptions": {
    "...": "reduced for brevity",
    
    "baseUrl": "src",
    "paths": {
      "@app/*": ["app/*"],
      "@env/*": ["environments/*"]
    }
  }
}

咱們還添加了@env別名,以便可以從import { environment } from "@env/environment"中使用同一個導入申明輕鬆地從應用程序的任何地方訪問環境變量。它將爲全部指定的environments工做,由於它將根據傳遞給ng build命令的--env標誌自動解析正確的環境配置文件。

經過咱們的路徑,咱們如今能夠像這樣導入environment 和 services …

/* 3rd party libraries */
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';

/* globally accessible app code (in every feature module) */
import { SomeSingletonService } from '@app/core';
import { environment } from '@env/environment';

/* localy accessible feature module code, always use relative path */
import { ExampleService } from './example.service';

@Component({
  /* ... */
})
export class ExampleComponent implements OnInit {
  constructor(
    private someSingletonService: SomeSingletonService,
    private exampleService: ExampleService
  ) {}
}

在上面的例子中,你也許注意到咱們直接導入服務SomeSingletonService是經過@app/core,代替了@app/core/some-package/some-singleton.service。這得感謝核心模塊的入口文件index.ts從新導出了每個公共實體。咱們在每個包(文件夾)都建立了一個文件index.ts,它看起來像這樣...

export * from './core.module';
export * from './auth/auth.service';
export * from './user/user.service';
export * from './some-singleton-service/some-singleton.service';

在大多數的應用程序中,特殊功能模塊的組件和服務的一般只須要訪問服務的 CoreModule 和組件的SharedModule。有時這可能不足以解決特定的業務狀況,咱們還須要某種「shared feature module(共享功能模塊)」,它爲其餘功能模塊的有限子集提供功能。

在這種狀況下,咱們將獲得相似的東西import { SomeService } from '@app/shared-feature';。和核心模塊同樣,共享功能模塊使用別名@app進行訪問。

圖片描述

3. 使用SASS

SASS是一個CSS的預處理器,支持有趣的東西,像variables(儘管CSS將要實現變量),functions,mixin等等。

SASS在Angular Material Components官方庫的多主題中進行有效的使用。項目中默認選擇使用SASS能夠假設是安全的。

爲了使用SASS咱們在使用Angular CLI生成咱們的項目時候必須用命令ng new command --style scss 。這就設置了大部分必須的配置。默認狀況下不添加stylePreprocessorOptions includePaths,咱們能夠本身設置成根目錄 "./" 和 可選的 "./themes"。

{
  "apps": [
    {
      "...": "reduced for brevity",
      
      "stylePreprocessorOptions": {
        "includePaths": ["./", "./themes"]
      }
    }
  ]
}

這有助於咱們的編輯器找到導入標誌,而且經過Angular Material的變量和工具函數的代碼完成來加強開發人員的體驗。

When theming Angular Material apps it’s a good practice to extract theme definitions into separate themes folder, one theme per file.

4. 應用產品構建

Angular CLI聲稱的項目只提供了一個很是簡單開箱即用的構建腳本ng build。爲了生成產品級的工件,咱們必須本身作一些定製。

咱們在package.json中添加腳本"build:prod": "ng build --target production --build-optimizer --vendor-chunk"

Target Production

這是一個標誌能使代碼壓縮,以及還有不少默認的有用的構建標誌。使用以下:

  • --environment prod —使用 environment.prod.ts 文件設置環境變量
  • --aot —預編譯,提早編譯. 這將在將來的Angular CLI是默認設置,可是如今咱們必須手動啓動。
  • --output-hashing all — 將生成的文件的散列內容添加到文件名中,以方便瀏覽器緩存破壞(文件內容的任何更改都會致使不一樣的哈希值,所以瀏覽器被迫加載新版本的文件)
  • --extract-css true — 將全部的css提取到到單獨的樣式表文件
  • --sourcemaps false — 禁用source maps的生成
  • --named-chunks false — 禁用使用可讀的名字,用數字替代

Other useful flags

  • --build-optimizer — 新的功能,致使更小的捆綁,但更長的構建時間,因此慎用!(未來也應該默認啓用)
  • --vendor-chunk — 將全部第三方(庫)代碼提取到單獨的塊中

官方文檔檢查其餘有用的配置項,也許在我的項目中用得上。

5. Phantom JS 死了! Headless Chrome 永存!

PhantomJS是一個很是著名的無頭的瀏覽器。這是事實上的解決方案,在CI服務器和許多開發機器上運行前端測試。

雖然有好的,這是現代ECMAScript支持滯後。更重要的是,它是不規範的行爲,在許多狀況下致使頭痛,當測試經過本地沒有問題,但他們仍然在CI環境出現問題。

幸運的是,咱們再也不須要處理它了!

圖片描述

就像官方文檔說的同樣…

Headless Chrome 是在Chrome 59上運行。這是在Headless環境下運行Chrome瀏覽器的一種方式。本質上,運行沒有Chrome的Chrome!它將Chrome和閃爍渲染引擎提供的全部現代Web平臺特性帶到命令行。

很棒,那咱們在Angular CLI上使用它呢?

咱們在package.json上添加腳本"test": "ng test --browser ChromeHeadless --single-run""watch": "ng test --browser ChromeHeadless"

很簡單,對吧!

6. 使用標準的提交信息 & 自動生成更新日誌

對咱們感興趣的項目的新特性和bug修復有一個快速概述老是很棒的。

讓咱們爲用戶提供一樣的便利!

手動寫更改日誌將是極其繁瑣,並且容易出錯的任務,因此它最好是自動化的過程。有不少可用的工具能夠作這項工做,可是讓咱們看下standard-version

這個工具根據Conventional Commits specification把全部提交自動生成和更新changelog.md文件,而且正確地肯定咱們項目的新版本。

常規提交定義了強制類型、可選(範圍):其次是提交消息。還能夠添加可選的正文和頁腳,它們都由空行分隔。讓咱們來看看ngx-model庫全部提交信息的一個實踐例子。

fix(dependency): multiple versions of rxjs in single project (TS90010)

BREAKING CHANGE: rxjs is now peerDependency instead of dependency

closes #1

標準版本將正確地撞擊(bump)項目的主要版本,由於在提交主體中存在着BREAKING CHANGE關鍵字。

生成的 CHANGELOG.md 文件將像這個樣子….

圖片描述

看起來很好!那麼咱們如何在咱們的項目中使用它呢?

首先咱們須要經過命令npm install -D standard-version安裝到devDependencies ,而後添加腳本"release": "standard-version"到package.json文件中。

咱們也能夠經過git pushnpm publish使整個過程自動化。本例子中使用腳本"release": "standard-version && git push — follow-tags origin master && npm publish"

注意咱們使用&&連接命令是平臺相關的,只能在基於Unix的系統上(所以也對Windows Cygwin,gitbash,或新的win10)。

附贈 Use resource root (Intellij IDEA, Webstorm only)

IntelliJ IDEA永遠不會找到全部默認狀況下(帶有錯誤的標記和殘廢的紅色代碼)的路徑。幸運的是,解決方案很是簡單。只需選擇SRC文件夾並將其標記爲Sources Root。

圖片描述

很棒! 你終於讀完了它!

我但願你能找到一些有用的技巧和最佳實踐!請支持這篇文章,以便向更多的受衆傳播這些建議!

參考資源


相關文章
相關標籤/搜索