你可以创建自己的原理图来对 Angular 项目进行操作。库开发人员通常会把这些原理图与他们的库打包在一起,以便把它们与 Angular CLI 集成在一起。你也可以创建独立的原理图来操作 Angular 应用中的文件和目录结构,以便为你的开发环境定制它们,并让它们符合你的标准和约束。多个原理图还可以串联起来,通过运行其它原理图来完成复杂的操作。
在应用程序中操作代码可能既强大又危险。比如,创建一个已存在的文件会出错,如果出现这种情况,就应该放弃已应用的所有其它更改。Angular 原理图工具通过创建虚拟文件系统来防止副作用和错误。原理图描述了一个可应用于虚拟文件系统的转换管道。当原理图运行时,转换就会被记录在内存中,只有当这些更改被确认有效时,才会应用到实际的文件系统中。
原理图的公共 API 定义了表达其基本概念的类。
Tree
(树)表示。Tree
数据结构包含一个基础状态 base(一组已经存在的文件)和一个 暂存区 staging(需要应用到 base 的更改列表)。在进行修改的过程中,你并没有真正改变它的 base,而是把那些修改添加到了暂存区。Rule
(规则)对象定义了一个函数,它接受 Tree
,进行转换,并返回一个新的 Tree
。原理图的主文件 index.ts
定义了一组实现原理图逻辑的规则。Action
(动作)表示。有四种动作类型:Create
、Rename
、Overwrite
和 Delete
。SchematicContext
对象表示。传给规则的上下文对象可以访问该原理图可能会用到的工具函数和元数据,包括一个帮助调试的日志 API。上下文还定义了一个合并策略,用于确定如何将这些更改从暂存树合并到基础树中。可以接受或忽略某个更改,也可以抛出异常。
当你使用 Schematics CLI 创建一个新的空白原理图时,它所生成的入口函数就是一个规则工厂。RuleFactory
对象定义了一个用于创建 Rule
的高阶函数。
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
// You don't have to export the function as default.
// You can also have more than one rule factory per file.
export function helloWorld(_options: any): Rule {
return (tree: Tree, _context: SchematicContext) => {
return tree;
};
}
你的这些规则可以通过调用外部工具和实现逻辑来修改你的项目。比如,你需要一个规则来定义如何将原理图中的模板合并到宿主项目中。
规则可以利用 @schematics/angular
包提供的实用工具。寻求辅助函数来处理模块、依赖、TypeScript、AST、JSON、Angular CLI 工作区和项目等等。
import {
JsonAstObject,
JsonObject,
JsonValue,
Path,
normalize,
parseJsonAst,
strings,
} from '@angular-devkit/core';
规则可以从调用者那里收集选项值,并把它们注入到模板中。规则可用的选项及其允许的值和默认值是在原理图的 JSON 模式文件 <schematic>/schema.json
中定义的。可以用 TypeScript 接口来为这个模式定义变量或枚举的数据类型。
该模式定义了原理图中使用的变量的类型和默认值。比如,假设的 “Hello World” 原理图可能具有以下模式定义(schema)。
{
"properties": {
"name": {
"type": "string",
"minLength": 1,
"default": "world"
},
"useColor": {
"type": "boolean"
}
}
}
可以在 @schematics/angular
中看到 Angular CLI 命令原理图的模式文件范例。
原理图提示能将用户交互引入到原理图执行过程中。可以配置原理图选项,以向用户显示可自定义的问题。在执行原理图之前会显示提示,然后将用户的响应用作选项的值。这使得用户可以指导原理图的操作,而无需深入了解可用选项的全部范围。
比如,这个 “Hello World” 原理图可能会要求用户提供他的名字,并显示该名字以代替默认名字 “world”。要定义这样的提示,请将 x-prompt
属性添加到 name
变量的模式中。
类似地,你可以添加一个提示,以允许用户确定原理图在执行其 hello 操作时是否将使用颜色。带有两个提示的模式如下。
{
"properties": {
"name": {
"type": "string",
"minLength": 1,
"default": "world",
"x-prompt": "What is your name?"
},
"useColor": {
"type": "boolean",
"x-prompt": "Would you like the response in color?"
}
}
}
这些范例使用提示语法的简写形式,仅提供问题的文本。在大多数情况下,这就是所需要的。但是请注意,这两个提示要求使用不同类型的输入。使用简写形式时,将根据属性的模式自动选择最合适的类型。在该范例中,name
提示使用 input
类型,因为它是一个字符串属性。useColor
提示使用 confirmation
类型,因为它是布尔属性。在这种情况下,“是” 对应于 true
而 “否” 对应于 false
。
支持三种输入类型。
输入类型 | 详情 |
---|---|
确认 | 是或否的问题;布尔选项的理想选择。 |
输入 | 文字输入;字符串或数字选项的理想选择。 |
清单 | 预定义的一组允许值。 |
简而言之,类型是根据属性的类型和约束来推断的。
属性模式 | 提示类型 |
---|---|
"type": "boolean" | 确认(“yes” = |
"type": "string" | 输入 |
"type": "number" | 输入(仅接受有效数字) |
"type": "integer" | 输入(仅接受有效数字) |
"enum": […] | 列表(枚举成员成为列表中的选择项) |
在以下范例中,该属性采用枚举值,因此原理图将自动选择列表类型,并根据可能的值创建菜单。
"style": {
"description": "The file extension or preprocessor to use for style files.",
"type": "string",
"default": "css",
"enum": [
"css",
"scss",
"sass",
"less",
"styl"
],
"x-prompt": "Which stylesheet format would you like to use?"
}
提示运行时会根据 JSON 模式中提供的约束条件自动验证提供的响应。如果该值不可接受,则提示用户输入新值。这样可以确保传递到原理图的任何值都符合原理图实现的期望,因此你无需在原理图的代码中添加其它检查。
在需要对提示进行其它自定义和控制情况下,x-prompt
字段也支持长格式语法。在这种形式下,x-prompt
字段值是带有子字段的 JSON 对象,这些子字段可自定义提示的行为。
字段 | 数据值 |
---|---|
type |
|
message | 字符串(必填) |
items | 字符串和/或“标签/值”对象(仅对 |
下面的长格式范例来自 CLI 用来生成应用程序的原理图的 JSON 模式。它定义提示,允许用户选择要用于正在创建的应用程序的样式预处理器。通过使用长格式,原理图可以为菜单选项提供更明确的格式。
"style": {
"description": "The file extension or preprocessor to use for style files.",
"type": "string",
"default": "css",
"enum": [
"css",
"scss",
"sass",
"less"
],
"x-prompt": {
"message": "Which stylesheet format would you like to use?",
"type": "list",
"items": [
{ "value": "css", "label": "CSS" },
{ "value": "scss", "label": "SCSS [ https://sass-lang.com/documentation/syntax#scss ]" },
{ "value": "sass", "label": "Sass [ https://sass-lang.com/documentation/syntax#the-indented-syntax ]" },
{ "value": "less", "label": "Less [ http://lesscss.org/ ]" }
]
},
},
定义原理图选项的 JSON 模式支持扩展,以允许对提示及其相应行为进行声明式定义。无需其它逻辑或更改原理图代码即可支持提示。以下 JSON 模式是 x-prompt
字段的长格式语法的完整描述。
{
"oneOf": [
{ "type": "string" },
{
"type": "object",
"properties": {
"type": { "type": "string" },
"message": { "type": "string" },
"items": {
"type": "array",
"items": {
"oneOf": [
{ "type": "string" },
{
"type": "object",
"properties": {
"label": { "type": "string" },
"value": { }
},
"required": [ "value" ]
}
]
}
}
},
"required": [ "message" ]
}
]
}
原理图有自己的命令行工具。使用 Node 6.9 或以上版本,全局安装 Schematics 命令行工具:
npm install -g @angular-devkit/schematics-cli
这将安装可执行文件 schematics
,你可以用它在自己的项目文件夹中创建一个新的原理图集合、把一个新的原理图添加到一个现有的集合中,或者扩展一个现有的原理图。
在下面的章节中,我们将使用 CLI 创建一个新的原理图集合,以介绍文件和目录结构,以及一些基本概念。
但是,原理图的最常见用途是将 Angular 库与 Angular CLI 集成在一起。可以直接在 Angular 工作区的库项目中创建原理图文件,而无需使用 Schematics CLI。
下列命令用来在同名的新项目文件夹中创建一个名为 hello-world
的新原理图。
schematics blank --name=hello-world
blank
原理图是由 Schematics CLI 提供的。该命令用于创建一个新的项目文件夹(该集合的根文件夹),并在该集合中创建一个最初的命名原理图。
转到 collection 文件夹,安装你的 npm 依赖,然后在常用的编辑器中打开这个新集合,看看所生成的文件。比如,如果你正在使用 VSCode:
cd hello-world
npm install
npm run build
code .
最初的原理图与项目文件夹的名字相同,是在 src/hello-world
中生成的。可以把相关的原理图添加到这个集合中,并修改所生成的骨架代码来定义原理图的功能。每个原理图的名称在集合中都必须是唯一的。
使用 schematics
命令运行一个命名原理图。按以下格式提供项目文件夹的路径、原理图名称和所有必选项。
schematics <path-to-schematics-project>:<schematics-name> --<required-option>=<value>
该路径可以是绝对路径,也可以是执行该命令的当前工作目录的相对路径。比如,要运行刚生成的原理图(它没有必选项),请使用下面的命令。
schematics .:hello-world
要把一个原理图添加到现有的集合中,请使用和新建原理图项目相同的命令,不过要改为在该项目的文件夹下运行该命令。
cd hello-world
schematics blank --name=goodbye-world
该命令会在你的集合中生成一个新的命名原理图,它包含一个主文件 index.ts
及其相关的测试规约。它还会把这个新原理图的名字(name),说明(description)和工厂函数(factory function)添加到 collection.json
文件中此集合的 JSON 模式中。
集合的根文件夹中包含一些配置文件、node_modules
文件夹和 src/
文件夹。src/
文件夹包含该集合中各个命名原理图的子文件夹,以及一个模式文件(collection.json
),它是集合中各个原理图的模式定义。每个原理图都是用名称,描述和工厂函数创建的。
{
"$schema":
"../node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"hello-world": {
"description": "A blank schematic.",
"factory": "./hello-world/index#helloWorld"
}
}
}
$schema
属性指定了 CLI 进行验证时所用的模式。schematics
属性列出了属于这个集合的各个命名原理图。每个原理图都有一个纯文本格式的描述,以及指向主文件中自动生成的那个入口函数。factory
属性指向自动生成的那个入口函数。在这个例子中,你会通过调用 helloWorld()
工厂函数来调用 hello-world
原理图。schema
是一个 JSON 模式文件,它定义了本原理图中可用的命令行参数。aliases
指定了一个或多个可用来调用此原理图的字符串。比如,Angular CLI “generate” 命令的原理图有一个别名 “g”,这就可以让你使用命令 ng g
。当你使用 Schematics CLI 创建空白原理图项目时,该集合的第一个成员是一张与该集合同名的空白原理图。当你把这个新的命名原理图添加到本集合中时,它会自动添加到 collection.json
模式中。
除了名称和描述外,每个原理图还有一个 factory
属性,用于标识此原理图的入口点。在本例中,你通过在主文件 hello-world/index.ts
中调用 helloWorld()
函数来调用此原理图中定义的功能。
该集合中每个命名原理图都有以下主要部分。
部分 | 详情 |
---|---|
index.ts | 定义命名原理图中转换逻辑的代码。 |
schema.json | 原理图变量定义。 |
schema.d.ts | 原理图变量。 |
files/ | 要复制的可选组件/模板文件。 |
原理图可以在 index.ts
文件中提供它全部的逻辑,不需要额外的模板。你也可以在 files/
文件夹中提供组件和模板来为 Angular 创建动态原理图,比如那些独立的 Angular 项目。这个 index 文件中的逻辑会通过定义一些用来注入数据和修改变量的规则来配置这些模板。