该文章翻译自链接。权当存档备案
什么是Schematics?
Schematics是改变现存文件系统的生成器。有了Schematics我们可以:
- 创建文件
- 重构现存文件,或者
- 到处移动文件
Schematics能做什么?
总体上,Schematics可以:
- 为Angular工程添加库
- 升级Angular工程中的库
- 生成代码
在你自己的工程或者在你所在的组织中使用Schematics是具有无限可能的。下面一些例子展现了你或者你的组织或如何从创建一个schematics collection中获益:
- 在应用中生成通用UI模板
- 使用预先定义的模板或布局生成组织指定的组件
- 强制使用组织内架构
Angular指定?
现在是,Schematics是Angular生态圈的一部分
CLI集成?
是的,schematics与Angular CLI紧密集成。你可以在下列的CLI命令中使用schematics:
- ng add
- ng generate
- ng update
什么是Collection?
Collection是一系列的schematic。我们会在工程中collection.json中为每个schematic定义元数据。
安装
首先,使用npm
或者yarn
安装schematics的CLI:
$ npm install -g @angular-devkit/schematics-cli
$ yarn add -g @angular-devkit/schematics-cli
起步
起步时我推荐检验下例子schematics并通读代码和相关注释。我们可以使用下列语句生成例子schematics:
$ schematics schematic --name demo
Hello World Demo
这是github链接
Hello World
简单的"Hello World" schematic有助于我们起步。使用schematics CLI创建一个新的schematics的空工程:
$ schematics black --name=hello-world
$ cd hello-world
打开src/collection.json:
{
"$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"hello-world": {
"description": "A blank schematic.",
"factory": "./hello-world/index#helloWorld"
}
}
}
回看一下collection.json结构:
-
$schema
属性指明了检验的schema -
schematics
属性是一个对象,里面每一个键都是schematic的name
- 每个schematic都有
description
属性来描述这个schematic - 每个schematic也有一个
factory
属性,它是一个string,指明了我们的schematic的入口点的文件位置,之后是一个#,然后是要调用的函数。在此处我们会调用在hello-world/index.js文件中的helloWorld()
函数
我们也可以指明一些附加属性:
- 可选的
aliases
属性是一个数组,我们能用来指定我们的schematic的一个或者多个别名。举个例子,在Angular CLI中"generate" schematic的别名是"g"。这允许我们通过$ ng g
调用generate命令 - 可选的
schema
属性可用来指明每个单独schematic的schema和所有的可用的命令行选项
另一个值得注意的是在工程中的package.json文件包含一个新的schematic
属性指向src/collections.json文件:
{
"name": "hello-world",
"version": "0.0.0",
"description": "A blank schematics",
"scripts": {
"build": "tsc -p tsconfig.json",
"test": "npm run build && jasmine src/**/*_spec.js"
},
"keywords": [
"schematics"
],
"author": "",
"license": "MIT",
"schematics": "./src/collection.json",
"dependencies": {
"@angular-devkit/core": "^7.0.2",
"@angular-devkit/schematics": "^7.0.2",
"@types/jasmine": "^2.6.0",
"@types/node": "^8.0.31",
"jasmine": "^2.8.0",
"typescript": "^2.5.2"
}
}
入口函数
打开src/hello-world/index.ts文件,看看内容:
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
export function helloWorld(options: any): Rule {
return (tree: Tree, _context: SchematicContext) => {
return tree;
};
}
- 我们export了会作为"entry function"调用的
helloWorld
函数 - 函数接受一个
options
参数,它是命令行参数的键值对对象 - 这个函数是一个高阶函数,接受或者返回一个函数引用。此处,这个函数返回一个接受
Tree
和SchmaticsContext
对象的函数
什么是Tree
?
文档中指出,Tree
是:
变化的待命区域,包含源文件系统和一系列应用到其上面的变化。
我们能使用tree
完成这些事情:
-
read(path: string): Buffer | null
: 读取指定的路径 -
exists(path: string): boolean
: 确定路径是否存在 -
create(path: string, content: Buffer | string): void
: 在指定路径使用指定内容创建新文件 -
beginUpdate(path: string): UpdateRecorder
: 为在指定路径的文件返回一个新的UpdateRecorder
实例 -
commitUpdate(record: UpdateRecorder): void
: 提交UpdateRecorder
中的动作
我们会在这个文章之后解说UpdateRecorder
。
什么是Rule
?
Rule
是一个根据SchematicContext
为一个Tree
应用动作的函数:
declare type Rule =
(tree: Tree, context: SchematicContext) =>
Tree | Observable | Rule | void;
在上面的hello world例子代码中,helloWorld()
入口函数返回了一个Rule
。
构建和执行
继续往前走,我们来构建和执行我们的schematic:
$ yarn build
$ schematics .:hello-world --foo=bar
需要注意的一个事:
- 我们使用schematics CLI,而非Angular CLI来执行schematic
- 第一个选项是指定collection名,在这个例子中,我们指向了当前文件
.
作为collection - collection名是可选的。如果我们忽略collection名字内建collection会被使用。我们在之前执行"blank"schematic时就使用了内建collection
- 如果我们指定collection名,我们使用冒号来分开schematic名字后面的collection名
- 在这个例子中我们执行了"hello-world" schematic
- 最后我们指明了我们schematic的"foo"选项,值为"bar"
我们在从helloWorld
函数中返回的函数里加一个log语句:
export function helloWorld(options: any): Rule {
return (tree: Tree, _context: SchematicContext) => {
console.log(options);
return tree;
};
}
构建并执行这个schematic,你应该能看到你在schematic中指定的选项的日志:
$ yarn build
$ schematics .:hello-world --foo=bar
{ foo: 'bar' }
Nothing to be done.
生成Hello World模板
我们的schematic现在只能输出用户指定的选项。我们来升级下我们的Rule
函数来使用tree.create()
函数来新建一个新文件:
export function helloWorld(options: any): Rule {
return (tree: Tree, _context: SchematicContext) => {
tree.create("hello-world.html", `Hello ${options.name}
`);
return tree;
};
}
正如我们之前学到的,create()
方法接收我们要新建的文件的path
和文件的content
。在这个例子中,我们简单的使用用户指定的值name
和前面的"Hello"字符串来填充hello-world.html模板。
当我们构建并执行升级过的schematic我们应该看到:
$ yarn build
$ schematics .:hello-world --name="Angular Community" --dry-run
CREATE /hello-world.html (37 bytes)
注意:
- 在执行时指明了
name
选项 - 指明了
dry-run
选项来避免实际的文件创建。移除dry-run
选项会在你当前工作目录中创建hello-world.html模板
单元测试
Angular schematics包含了一个SchematicTestRunner
,用来构建单元测试套件来保证你schematics collection的质量。
让我们来测试一下我们新建的hello-world schematics:
const collectionPath = path.join(__dirname, "../collection.json");
describe("hello-world", () => {
it("works", () => {
const runner = new SchematicTestRunner("schematics", collectionPath);
const tree = runner.runSchematic("hello-world", {}, Tree.empty());
expect(tree.files.length).toEqual(1);
});
});
- 测试使用Jasmine执行
- 我们new了个
SchematicsTestRunner
类,指定了collectionName
和collectionPath
- 使用测试运行程序我们调用了
runSchematic()
,指定了schematicName
,选项对象和源Tree
- 最后我们声称tree的文件数已经增长到了
1
结论
我希望这是你的Angular Schematics学习之旅的开端。
更多资源请浏览:
- Getting Started with Schematics
- Schematics book by Manfred Steyer (免费的电子书,感觉很全)