使用 Angular cli 快速构建或修改 application

346 阅读16分钟

本文通过罗列 angular cli 中的一些常用命令,旨在通过此脚手架工具方便快捷的构建 angular app. 值得注意的是,尽管这些封装起来的命令行单独使用的时候节省的时间不够明显,但是一旦灵活使用就可以快速构建出一个完整架构的现代前端工程化项目。

node 版本大概在 16.14.2 npm 版本大概在 8.5.0 @angular/cli 版本大概在 14.3.0

1. Angular CLi's Main Commands

最先开始的时候让我们来看一看,angular cli 给我们提供了哪些最常用的命令:

  • ng generate - ng g
  • ng lint - ng l
  • ng build - ng b
  • ng serve - ng s
  • ng test
  • ng e2e - ng e
  • ng doc
  • ng help

2. Installing the Angular CLl

使用下面的命令全局安装 angular 脚手架。 npm install -g @angular/cli

使用 angular 脚手架能更好的实践测试驱动

本文的主要内容包括:

  1. 安装并确认 angular 脚手架 npm install -g @angular/cli ng -v
  2. 创建一个新的应用 ng new ngtest --skip-install ng new --help
  3. 使用【配置过的】【自定义的】模板创建代码
  4. 格式化 【检测/修复】 代码
  5. 优化创建的应用
  6. 测试应用程序

安装名为 【angular essentials】 的插件 帮助网站 cli.angular.io

3. --dry-run or -d

-d 可能是最重要的命令之一了,这个命令可以让你预览其它命令执行的后果,但是不会对项目做任何改变。相当于命令的预览。

4. 创建一个新的应用

# ng new my-app
ng new my-app -d
ng new my-app --skip-install # 跳过依赖安装

如果我们不想要测试文件,并且想要给整个应用一个自定义前缀:

ng new my-app --skip-tests --prefix iot --skip-install

可如果我们想要使用 scss 作为样式文件:--style scss

ng new my-app --skip-install --prefix iot --skip-install --style scss

如果我们想要创建 SPA, 那么我们需要使用路由系统:--routing

ng new my-app --skip-install --prefix iot --skip-tests --style scss --routing --dry-run

image.png

创建根路由模块会被自动引用到合适的位置中去。

stack 和 alias:

上述的命令行可以写成:ng new my-app --dS --prefix iot --skip-install --routing d 表示的是 --dry-run 而 -S 表示的是 --skip-tests 我们可以写的更复杂一些的:ng new my-app --dstS --prefix iot --skip-install --routing -s 表示使用内联的 style, 也就是不会生成单独的样式文件,而 -t 表示内敛的 html, 也就似乎不会生成单独的 html 文件。

不必这么麻烦: 一旦创建好项目,在根目录下会有名为 angular.json 的文件,可以在其中进行一些配置,这样在之后使用其它构建命令的时候就不必每次都输一遍这些 flags 了。

5. --help

如果忘记某个命令的使用,我们总是可以使用 --help 获取使用指南,例如:ng serve --help

cf2fe76e-9b67-4656-b83c-65c633dc18e3.jpg

通过上述的配置,在之后使用命令创建 component 的时候就会自动使用 scss 作为样式文件了,无需每次都输入 --style scss. 三种方式自定义 cli:

310d885b-80b7-4360-8e2b-13f6b5503aea.jpg

6. Setting the Angular CLl's Configuration

使用 ng config 命令进行配置,加上 ng config -g 表示此配置在全局内生效。

ng config schematics.@schematics/angular:component.styleext scss

7. lint 格式化代码

使用 ng lint my-app --help 获取关于此命令的相关信息。

image.png

使用 ng lint my-app --format stylish 将格式错误高亮输出(更加利于阅读)

使用 ng lint my-app --fix 检查并修复格式错误。

7. Generating from Blueprints

快速构建代码的基本命令是 ng generate 或者 ng g, 使用时候的基本格式为:ng generate <blueprint> <options>, 下面是一些常见的例子:

  • ng generate component customer - ng g c customer
  • ng generate service customer-data - ng g s customer-data
  • ng generate class customer-model - ng g class customer-modal
  • remember to use --dry-run flexible

8. Common Component Blueprint Options

9. --flat false

一般来说我都希望对组件生成一个单独的文件夹进行管理,因此使用 --flat false 可以实现此目的。

image.png

10. 指定组件的样式封装策略和更新策略

ng g c --flat false -v Emulated -c OnPush alarmdetails 运行之后的效果:

image.png

image.png

11. 创建指令

使用 ng g d directives/search-box 创建新的指令。或者使用 ng g d directives/search-box --flat falseng g d directives/search-box --skip-tests 都是不错的选择。

image.png

12. 创建一个类

除了创建 angular 相关的命令行,cli 还提供了创建常用 js 结构的命令行,例如我们可以通过 ng g cl models/first 快速创建一个类。

image.png

同理:

  • 使用 ng g i ** 创建一个接口
  • 使用 ng g e ** 创建枚举

13. 创建管道

使用 ng g p * 创建最基本的管道类型。

管道一般是在模块中被引入的,因此在创建管道的时候我们可以通过 -m 来指定其被哪一个模块所引用,在创建完毕之后就会被自动引入到此模块中。

我们先创建一个共享模块:ng g m shared --flat false -m app.module 然后再运行:ng g p shared/safe-value -m shared.module 这里使用 -m 制定了引用此 pipe 的模块名称。

image.png

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SafeValuePipe } from './safe-value.pipe';

@NgModule({
  declarations: [
    SafeValuePipe
  ],
  imports: [
    CommonModule
  ]
})
export class SharedModule { }

这里其实还有有点问题的,那就是引入 SafeValuePipe 并没有 exports 出去。

14. 创建模块

需要注意的是创建模块的时候如果想要跳过测试使用的是 --spec false 而不是之前的 --skip-tests.

新建的模块也需要被引入到其它模块中,因此同样需要 -m 指定引用其的模块: -m app.module

创建模块的组件

如果已经有名为 device 的模块了,那么使用 ng g c device 在生成组件之后会被同名模块自动引入。

ng g m device --flat false
ng g c device 

image.png

然后我们就可以创建 device 的子组件了: ng g c device/device-details -d

image.png

注意这里没有将同名模块对其引用的 update 展示出来。注意这是自动的。

创建模块的时候增加路由: --routing

image.png

创建的独有的路由模块会被自动引入:

image.png

路由模块的内容大致为:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class LoggerRoutingModule { }

创建一个模块的时候可以指定引用者,如下:

image.png

image.png

然后为新增的 device 模块增加组件: ng g c device --skip-tests true 此组件会自动被同名模块使用,也就是说不必使用 -m device.module

image.png

15. 创建一个服务

我们使用 ng g s ** 创建一个服务。需要注意的是,创建 service 的时候无法使用 -m 指定模块,这里如果使用 -m 会报错的。

image.png

代码如下:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class DeviceDataService {

  constructor() { }
}

不难看出来,它是自动注入到 root 中去的。

16. 创建一个路由守卫

我们使用 ng g guard ** 创建一个路由守卫。需要注意的是,创建 service 的时候无法使用 -m 指定模块,这里如果使用 -m 会报错的。

image.png

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class IsAuthGuard implements CanActivate {
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return true;
  }
}

从上面的代码中可以看到:

@Injectable({ 
  providedIn: 'root', 
})

也是直接注入到 root 中去的。

17. 关于打包

  • 指定打包目标地址
  • 代码压缩和树摇

关于打包的命令格式基本如下所示:ng build <options> 默认情况下会打包到 /dist/[your app name] 这个文件夹下面。不过打包的地址可以在 angular.json 中进行设置。

两种包分析库:

  1. webpack-bundle-analyzer
npm install webpack-bundle-analyzer@^4.10.2 --save-dev
ng build --stats-json
npx webpack-bundle-analyzer dist/my-app/stats.json

image.png

image.png

成功之后增加一个 script: "stats": "npx webpack-bundle-analyzer dist/my-app/stats.json"

  1. source-map-explorer
npm install source-map-explorer --save-dev
ng build
npx source-map-explorer dist/my-app/main.js

18. 两种环境之间的对比

一般来说我们有两种环境,分别记为 environment.prod.tsenvironment.ts.

对比 devprod 两个环境:

特性/环境开发环境 (ng build)生产环境 (ng build --prod)
环境配置environment.tsenvironment.prod.ts
缓存破坏只包含在 CSS 中引用的图片所有构建文件
源代码映射生成不生成
提取 CSS全局 CSS 输出到 .js 文件是,输出到 CSS 文件
压缩代码 (Uglification)
摇树优化 (Tree-Shaking)
预编译 (AOT)
打包 (Bundling)

ng build 相关的一些 options:

  1. ng build --source-map 打包并且创建 source map
  2. ng build -aot 进行预编译
  3. ng build --watch 开启监视
  4. ng build --prod 采用生产环境下的物料

使用 webpack-bundle-analyzer 对比上述几种不同的 option 打包结果:

企业微信截图_17211908083043.png

企业微信截图_17211909717372.png

19. ng serve 相关的 option

  1. --open 是否自动打开网页
  2. --port 指定端口号
  3. --live-reload 代码更新之后,页面要不要自动更新
  4. --ssl 使用 https 协议
  5. --proxy-config 配置代理

举例说明:

  • ng serve --port 8626
  • ng serve --port 8626 -o
  • ng serve --prod -o

20. 在 serve 运行的过程中安装新包

承担动态安装包的命令是:ng add, 举例如下所示:

  • ng add @angular/pwa
  • ng add @angular/material
  • ng add @angular/elements
  • ng add @ng-bootstrap/schematics

这个我已经验证过了,是完全 ok 的,不用关闭 serve 安装之后再打开。

ng build 的作用不止于此,实际上它还可以:

  1. Adds new capabilities
  2. Downloads dependencies
  3. Update a project configuration
  4. Scaffold new capabilities

21. cli 和 material 联动

一定要注意,这里我们使用的 material 的版本号是:^14.2.7 那么相应的 cdk 的版本号也就是 ^14.2.7

快速创建一个 material 风格的 nav 组件。

image.png

image.png

快速创建一个 material 风格的 dashboard 组件。

image.png

快速创建一个 material 风格的 table 组件。

image.png

这些内置的命令行对于开始一个新的项目来说是非常友好的!

22. Angular cli 和 单元 test

使用 ng test --help 获取关于测试的更多信息。

执行此命令之后会在默认端口号 9876 中打开测试页面,如下所示:

image.png

image.png

测试相关的配置在根目录下面的 karma.conf.js 文件中。不难看出,我们使用的测试工具是 Karma.

和命令 ng test 相关的 option 包括:

  • --code-coverage -- 生成代码的测试报告,这个默认是关闭的。

image.png

同时生成一个名为 coverage 的目录,如下所示:

image.png

我们打开 coverage 下面的 index.html, 显示如下:

image.png

  • --progress -- 将测试进度打印出来,这个默认是开启的。
  • --sourcemaps -- 生成 sourcemap,这个默认是开启的。
  • --watch -- 以监视的方式开启测试,这个默认是开启的。

因此如果你不想要在改变代码之后刷新测试结果,或者你只想对当前代码进行测试并且没有改变代码的打算,那么应该使用的命令为:ng test --watch false

在测试过程中进行调试

首先打开 Karma 网页的开发者工具,使用 ctrl + shift + i, 然后使用 ctrl + shift + p 选择某个 *.spec.ts 文件。

image.png

然后就可以在源文件上打断点并进行调试了:

image.png

23. angular cli 和 e2e 测试

使用 ng e2e 进行端到端的测试,这里先看提示信息:

image.png

执行之后,在终端打印出如下的信息:

image.png

可以看出来,在首次使用的时候,会让选择到底是安装哪个测试工具。

24. 使用 ng update 来更新应用程序

  • 可以更新 Angular 的版本号
  • 可以更新第三方库
  • 可以修改应用程序的代码

使用 ng update 的基本结构:ng update <package> <options>, 其中 options 可以选择的项目有:

  • --dryRun 简写为 -d. 这是独有的预览功能,也很重要!
  • --all 更新 package.json 中所有的依赖。
  • --force 强制更新,很有用,因为冲突是常见的,特别是在这种更新操作中。

image.png

常见的更新命令有:

  • npm install -g @angular/cli
  • npm install @angular/cli
  • ng update @angular/cli
  • ng update @angular/core
  • ng update @angular/material

如果我们单独执行 ng update,则可以获取到当前的更新信息:

image.png

根据提示信息,我们可以做的事情为:

image.png

25. 多项目的工作空间 -- multiple projects workspace

我们的 angular.json 是支持多项目的。回过头来,看我们的项目结构,在 dist 目录下面并不是所有打包之后的文件,而是隔了一层目录,这一层目录名称就是每个项目的名称。

image.png

有了工作空间的这个概念,我们就可以在这些命令的后面加上 ng build, ng serve, ng test, ng e2e 了,这样的话就锁定了我们执行的范围,避免了在整个工作空间中生效。

如何获取工作空间? 我们使用 ng generate --help 可以很清楚的看出来,除了之前说到的一些元素,我们实际上是可以创建 Application 的。

image.png

然后根据提示,我们可以执行 ng generate application plugin 命令。

image.png

然后,在根目录中会出现名为 projects 的子目录:

image.png

然后我们就可以使用 --project 单独测试了:

  • ng test --project plugin

image.png

image.png

  • ng build --project plugin

image.png

image.png

26. 使用 library 创建自己的库

首先我们使用命令 ng generate library --help 查看相关的信息。

image.png

可以看出来,我们可以使用 --dry-run 来预览命令的执行结果。或者使用其它命令,例如:--skip-install 来跳过依赖的安装。这个命令使用的基本格式为:ng generate library <name> <options> 现在就创建一个自己的工具库:iot-tools

先预览一下:ng generate library iot-tools --dry-run

image.png

我们可以看到,所谓的 library 实际上是另外一种特殊的项目罢了。

真正安装之后,在项目目录中表现为:

image.png

我们可以看出,iot-tools 具有一个项目的完整的目录。

image.png

这个项目中的 public-api.ts 文件中向外暴露出一些方法,这些方法在我们使用 import { } from 'iot-tools'; 的时候可以解析出来。

现在我们让这工具库向外导出一个 pipe, 其作用为能够在文本后面加上 $ 符号。

  • 进入到目标项目中去:cd projects/iot-tools
  • 然后在其中创建一个 pipe: ng g p src/lib/pipes/adolla --flat false --skip-import --skip-tests
  • 最后在 public-api.ts 中引入并导出。export * from './lib/pipes/adolla/adolla.pipe';
  • 此时如果想要在其他项目中使用这个 pipe 需要运行 cd ../../ && ng buildiot-tools 打包,打包之后的结果,或者说打包 iot-tools 的终端目录都是在 workspace 的根目录下面的,打包完成之后放在 dist 文件夹下面。

image.png

  • 我们回到原来的项目中:cd ../../
  • 然后尝试使用此 pipe, 在使用的时候是有一些小技巧的。
    • 首先来到 app.component.html 中,在其中写下 <p>{{ "iot" | adolla }}</p>
    • 然后就报红了,说找不到 adolla 这个管道,这是因为我们还没有在 app.module.ts 当中引入这个 pipe
    • app.module.ts 当中引入:import { AdollaPipe } from 'iot-tools'; 然后声明:
    @NgModule({
      declarations: [
        ...,
        AdollaPipe
      ],
    
    这个时候你会发现:Cannot declare 'AdollaPipe' in an NgModule as it's not a part of the current compilation 而解决这个问题需要很强的高级技巧,如下所示:
    import { AdollaPipe } from 'iot-tools';
    
    @Pipe({
      name: 'adolla',
      pure: false
    })
    export class CAdollaPipe extends AdollaPipe { }
    
    @NgModule({
      declarations: [
        ...,
        CAdollaPipe
      ],
    

现在解释为什么可以引用成功,也就是为什么在 app.module.ts 中能够找到 iot-tools 这个库的问题。其秘诀就在 tsconfig.json 这个配置文件中:

/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": true,
    "paths": {
      "iot-tools": [
        "dist/iot-tools"
      ]
    },
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "target": "es2020",
    "module": "es2020",
    "lib": [
      "es2020",
      "dom"
    ]
  },
  "angularCompilerOptions": {
    "enableI18nLegacyMessageIdFormat": false,
    "strictInjectionParameters": true,
    "strictInputAccessModifiers": true,
    "strictTemplates": true
  }
}

正是因为 compilerOptions.paths 中放置了:

      "iot-tools": [
        "dist/iot-tools"
      ]

才会导致 import 的时候先去 dist/iot-toolsiot-tools 然后再去 node_modules 中寻找

6314e409-63c3-437b-ba3d-3e837cc9a58a.jpg

27. 使用 library 之后

创建功能性的 iot-tools 库之后,我们可以将其发送到公共仓库 npm 中去。

  • 让我们保证终端是在 workspace 的根目录下面,然后使用生产模式下的配置文件进行打包:ng build iot-tools --ts-config projects/iot-tools/tsconfig.lib.prod.json

image.png

  • 然后进入到打包目录下面去:cd dist/iot-tools

image.png

  • 最先使用 nrm use npm 切换源。
  • 运行 npm login 登录并输入用户名和密码。
  • 最后运行 npm publish 将代码上传上去,如果上传受到网络问题影响严重,可尝试使用 yarn publish 成功率会高一些。

总结一下 workspace :

当我们创建一个根项目 XXX 之后,在根目录下面会生成名为 src 的文件夹,里面存放的是这个主项目的源代码。这个时候我们使用 yarn build 或者使用 ng build 打包会在根目录下生成 dist/[XXX] 子目录。

而在此根目录下面我们可以使用 ng g application [name] 或者 ng g library [name] 这样的命令创建子项目,子项目都存放在根目录下面的 projects 子目录下面。

现在我们的所有命令在执行的时候如果通过 --project [name] 指定名称,那么就会将范围圈定在这些目录之内,而如果不指定则操作的就操作的是主项目。

image.png

image.png

image.png

28. Angular console

Angular console 本质上类似于 github 的桌面端工具,可以通过 GUI 而不是 CLI 的方式完成相同的功能。

  • Everything the CLl can do
  • Visual experience
  • Discover and install extensions
  • Integrated terminal output

我们在此网站上下载客户端,如下所示:

1f9ebf97-4db4-450b-9d04-754523e4ca46.jpg

那我们一般用它做什么事情呢?

  • Updating Angular
  • Workspaces with multiple projects
  • Generating Angular Libraries
  • Adding Libraries to Angular
  • Angular Console

但是现在已经完全集成到插件 Nx Console 中去了,所以在 vscode 的商城中下载这个插件就可以了。

image.png

如果想学 nx 的话可以来这个网站上:Explore your Workspace | Nx