本文通过罗列 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 gng lint - ng lng build - ng bng serve - ng sng testng e2e - ng eng docng help
2. Installing the Angular CLl
使用下面的命令全局安装 angular 脚手架。
npm install -g @angular/cli
使用 angular 脚手架能更好的实践测试驱动
本文的主要内容包括:
- 安装并确认 angular 脚手架
npm install -g @angular/cling -v - 创建一个新的应用
ng new ngtest --skip-installng new --help - 使用【配置过的】【自定义的】模板创建代码
- 格式化 【检测/修复】 代码
- 优化创建的应用
- 测试应用程序
安装名为 【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
创建根路由模块会被自动引用到合适的位置中去。
stack 和 alias:
上述的命令行可以写成:
ng new my-app --dS --prefix iot --skip-install --routingd 表示的是 --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
通过上述的配置,在之后使用命令创建 component 的时候就会自动使用 scss 作为样式文件了,无需每次都输入 --style scss.
三种方式自定义 cli:
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 获取关于此命令的相关信息。
使用 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 customerng generate service customer-data-ng g s customer-datang generate class customer-model-ng g class customer-modal- remember to use --dry-run flexible
8. Common Component Blueprint Options
--flat新建的代码是否需要被单独包装在一个子文件下面。--inline-templateor-t是否使用内联的 html--inline-styleor-s是否使用内联的样式文件--spec是否对新增的代码生成对应的测试文件--view-encapsulationor-v指定视图层封装策略; 详情见:深入理解 Angular 中的 View Encapsulation - 掘金 (juejin.cn)--change-detectionor-c指定组件更新策略;详情见:深入理解 Angular 中的变化检测机制 - 掘金 (juejin.cn)--dry-runor-d是否是预览
9. --flat false
一般来说我都希望对组件生成一个单独的文件夹进行管理,因此使用 --flat false 可以实现此目的。
10. 指定组件的样式封装策略和更新策略
ng g c --flat false -v Emulated -c OnPush alarmdetails 运行之后的效果:
11. 创建指令
使用 ng g d directives/search-box 创建新的指令。或者使用 ng g d directives/search-box --flat false 和 ng g d directives/search-box --skip-tests 都是不错的选择。
12. 创建一个类
除了创建 angular 相关的命令行,cli 还提供了创建常用 js 结构的命令行,例如我们可以通过 ng g cl models/first 快速创建一个类。
同理:
- 使用
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 的模块名称。
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
然后我们就可以创建 device 的子组件了:
ng g c device/device-details -d
注意这里没有将同名模块对其引用的 update 展示出来。注意这是自动的。
创建模块的时候增加路由: --routing
创建的独有的路由模块会被自动引入:
路由模块的内容大致为:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class LoggerRoutingModule { }
创建一个模块的时候可以指定引用者,如下:
然后为新增的 device 模块增加组件: ng g c device --skip-tests true 此组件会自动被同名模块使用,也就是说不必使用 -m device.module
15. 创建一个服务
我们使用 ng g s ** 创建一个服务。需要注意的是,创建 service 的时候无法使用 -m 指定模块,这里如果使用 -m 会报错的。
代码如下:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class DeviceDataService {
constructor() { }
}
不难看出来,它是自动注入到 root 中去的。
16. 创建一个路由守卫
我们使用 ng g guard ** 创建一个路由守卫。需要注意的是,创建 service 的时候无法使用 -m 指定模块,这里如果使用 -m 会报错的。
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 中进行设置。
两种包分析库:
- 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
成功之后增加一个 script: "stats": "npx webpack-bundle-analyzer dist/my-app/stats.json"
- source-map-explorer
npm install source-map-explorer --save-dev
ng build
npx source-map-explorer dist/my-app/main.js
18. 两种环境之间的对比
一般来说我们有两种环境,分别记为 environment.prod.ts 和 environment.ts.
对比 dev 和 prod 两个环境:
| 特性/环境 | 开发环境 (ng build) | 生产环境 (ng build --prod) |
|---|---|---|
| 环境配置 | environment.ts | environment.prod.ts |
| 缓存破坏 | 只包含在 CSS 中引用的图片 | 所有构建文件 |
| 源代码映射 | 生成 | 不生成 |
| 提取 CSS | 全局 CSS 输出到 .js 文件 | 是,输出到 CSS 文件 |
| 压缩代码 (Uglification) | 否 | 是 |
| 摇树优化 (Tree-Shaking) | 否 | 是 |
| 预编译 (AOT) | 否 | 是 |
| 打包 (Bundling) | 是 | 是 |
ng build 相关的一些 options:
ng build --source-map打包并且创建source mapng build -aot进行预编译ng build --watch开启监视ng build --prod采用生产环境下的物料
使用 webpack-bundle-analyzer 对比上述几种不同的 option 打包结果:
19. ng serve 相关的 option
--open是否自动打开网页--port指定端口号--live-reload代码更新之后,页面要不要自动更新--ssl使用 https 协议--proxy-config配置代理
举例说明:
ng serve --port 8626ng serve --port 8626 -ong serve --prod -o
20. 在 serve 运行的过程中安装新包
承担动态安装包的命令是:ng add, 举例如下所示:
ng add @angular/pwang add @angular/materialng add @angular/elementsng add @ng-bootstrap/schematics
这个我已经验证过了,是完全 ok 的,不用关闭 serve 安装之后再打开。
而 ng build 的作用不止于此,实际上它还可以:
Adds new capabilitiesDownloads dependenciesUpdate a project configurationScaffold new capabilities
21. cli 和 material 联动
一定要注意,这里我们使用的 material 的版本号是:^14.2.7 那么相应的 cdk 的版本号也就是 ^14.2.7
快速创建一个 material 风格的 nav 组件。
快速创建一个 material 风格的 dashboard 组件。
快速创建一个 material 风格的 table 组件。
这些内置的命令行对于开始一个新的项目来说是非常友好的!
22. Angular cli 和 单元 test
使用 ng test --help 获取关于测试的更多信息。
执行此命令之后会在默认端口号 9876 中打开测试页面,如下所示:
测试相关的配置在根目录下面的 karma.conf.js 文件中。不难看出,我们使用的测试工具是 Karma.
和命令 ng test 相关的 option 包括:
--code-coverage-- 生成代码的测试报告,这个默认是关闭的。
同时生成一个名为 coverage 的目录,如下所示:
我们打开 coverage 下面的 index.html, 显示如下:
--progress-- 将测试进度打印出来,这个默认是开启的。--sourcemaps-- 生成 sourcemap,这个默认是开启的。--watch-- 以监视的方式开启测试,这个默认是开启的。
因此如果你不想要在改变代码之后刷新测试结果,或者你只想对当前代码进行测试并且没有改变代码的打算,那么应该使用的命令为:ng test --watch false
在测试过程中进行调试
首先打开 Karma 网页的开发者工具,使用 ctrl + shift + i, 然后使用 ctrl + shift + p 选择某个 *.spec.ts 文件。
然后就可以在源文件上打断点并进行调试了:
23. angular cli 和 e2e 测试
使用 ng e2e 进行端到端的测试,这里先看提示信息:
执行之后,在终端打印出如下的信息:
可以看出来,在首次使用的时候,会让选择到底是安装哪个测试工具。
24. 使用 ng update 来更新应用程序
- 可以更新 Angular 的版本号
- 可以更新第三方库
- 可以修改应用程序的代码
使用 ng update 的基本结构:ng update <package> <options>, 其中 options 可以选择的项目有:
--dryRun简写为-d. 这是独有的预览功能,也很重要!--all更新 package.json 中所有的依赖。--force强制更新,很有用,因为冲突是常见的,特别是在这种更新操作中。
常见的更新命令有:
npm install -g @angular/clinpm install @angular/cling update @angular/cling update @angular/coreng update @angular/material
如果我们单独执行 ng update,则可以获取到当前的更新信息:
根据提示信息,我们可以做的事情为:
25. 多项目的工作空间 -- multiple projects workspace
我们的 angular.json 是支持多项目的。回过头来,看我们的项目结构,在 dist 目录下面并不是所有打包之后的文件,而是隔了一层目录,这一层目录名称就是每个项目的名称。
有了工作空间的这个概念,我们就可以在这些命令的后面加上 ng build, ng serve, ng test, ng e2e 了,这样的话就锁定了我们执行的范围,避免了在整个工作空间中生效。
如何获取工作空间? 我们使用 ng generate --help 可以很清楚的看出来,除了之前说到的一些元素,我们实际上是可以创建 Application 的。
然后根据提示,我们可以执行 ng generate application plugin 命令。
然后,在根目录中会出现名为 projects 的子目录:
然后我们就可以使用 --project 单独测试了:
ng test --project plugin
ng build --project plugin
26. 使用 library 创建自己的库
首先我们使用命令 ng generate library --help 查看相关的信息。
可以看出来,我们可以使用 --dry-run 来预览命令的执行结果。或者使用其它命令,例如:--skip-install 来跳过依赖的安装。这个命令使用的基本格式为:ng generate library <name> <options> 现在就创建一个自己的工具库:iot-tools
先预览一下:ng generate library iot-tools --dry-run
我们可以看到,所谓的 library 实际上是另外一种特殊的项目罢了。
真正安装之后,在项目目录中表现为:
我们可以看出,iot-tools 具有一个项目的完整的目录。
这个项目中的 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 build将iot-tools打包,打包之后的结果,或者说打包 iot-tools 的终端目录都是在workspace的根目录下面的,打包完成之后放在 dist 文件夹下面。
- 我们回到原来的项目中:
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-tools 找 iot-tools 然后再去 node_modules 中寻找。
27. 使用 library 之后
创建功能性的 iot-tools 库之后,我们可以将其发送到公共仓库 npm 中去。
- 让我们保证终端是在 workspace 的根目录下面,然后使用生产模式下的配置文件进行打包:
ng build iot-tools --ts-config projects/iot-tools/tsconfig.lib.prod.json
- 然后进入到打包目录下面去:
cd dist/iot-tools
- 最先使用
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]指定名称,那么就会将范围圈定在这些目录之内,而如果不指定则操作的就操作的是主项目。
28. Angular console
Angular console 本质上类似于 github 的桌面端工具,可以通过 GUI 而不是 CLI 的方式完成相同的功能。
Everything the CLl can doVisual experienceDiscover and install extensionsIntegrated terminal output
我们在此网站上下载客户端,如下所示:
那我们一般用它做什么事情呢?
Updating AngularWorkspaces with multiple projectsGenerating Angular LibrariesAdding Libraries to AngularAngular Console
但是现在已经完全集成到插件 Nx Console 中去了,所以在 vscode 的商城中下载这个插件就可以了。
如果想学 nx 的话可以来这个网站上:Explore your Workspace | Nx