Schematics

原理图(Schematics)

UpdateTime: 20200513

本文主要介绍本人从入门 Schematics 到开发 @nekoi/angular-schematicsnpmgithub)的流程与部分心得,可当做简单的教程阅读。

准备

  1. 全局安装 @angular-devkit/schematics-cli 以使用 schematics 命令
1
$ npm install -g @angular-devkit/schematics-cli
  1. 创建新的Schematics项目
1
$ schematics blank <schematic-name>

入门

创建一个原理图入门项目:

1
2
3
$ schematics blank my-schematics
$ cd my-schematics
$ npm i

1. 原理图文件构成

  • collection.json 文件是整个原理图库的主要定义文件,并且包含该库中所有可用原理图的定义。
1
2
3
4
5
6
7
8
9
10
11
{
"$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"my-schematics": {
//原理图名称
"description": "A blank schematic.",
"factory": "./my-schematics/index#mySchematics"
// 也可以使用 ./my-schematics/index , 需要将 index.ts 中 export function mySchematics(){} 更改为 export default function(){}
}
}
}
  • index.ts
1
2
3
4
5
6
import { Rule, SchematicContext, Tree } from "@angular-devkit/schematics";
export function mySchematics(_options: any): Rule {
return (tree: Tree, _context: SchematicContext) => {
return tree;
};
}
  • schema.json (需手动创建)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"$schema": "http://json-schema.org/schema",
"id": "MyFirstSchematic",
"title": "Hello Option Schema",
"type": "object",
"description": "My First Schematic",
"properties": {
"name": {
//参数名
"type": "string", //参数类型
"description": "The name of file",
"$default": {
//设为默认参数
"$source": "argv", //从命令行命令传入
"index": 0 //在命令中的默认位置。
//例如:schematics .:my-schematics Eliwa (默认位置)
//对比 schematics .:my-schematics --name=Eliwa (标准传入形式)
}
}
},
"required": ["name"]
}

2. 原理图基本相关概念

  • factory
    原理图工厂函数将接受一些自定义的参数 _options 并返回一条规则。
  • Rule
    Rule(规则)对象定义了一个函数,它接受 Tree,在一个 SchematicContext(原理图上下文)中进行转换,并返回一个新的 Tree 。原理图的主文件 index.ts 定义了一组实现原理图逻辑的规则。
  • Tree

Schematics are all about code generation and changing of existing files!

原理图都是关于代码生成和现有文件的更改。

  • Tree(树)是工作空间中每个文件的虚拟表示。
    使用虚拟文件树有以下一些好处:1)只有在每个示意图都成功运行的情况下,我们才会提交对树的更改。 2)在 debug 模式下(使用 --dry-run 标识),可以预览文件的改动而不影响实际的文件系统。 3)I/O 操作仅发生在整个处理过程结束后。

总结:原理图由一个导出工厂函数的主文件组成。使用原理图选项调用此函数。工厂返回一个规则,这个规则通过文件系统的虚拟表示(也就是树和原理图上下文)来调用。

3. 一个最简单的例子

1
2
3
4
5
6
7
//index.ts
export function mySchematics(_options: any): Rule {
return (tree: Tree, _context: SchematicContext) => {
tree.create("hello.js", `console.log('Hello Schematics');`);
return tree;
};
}

构建并在同一根目录下运行:

1
2
$ npm run build
$ schematics .:my-schematics

运行结果:显示 CREATE hello.js (32 bytes) 但没有实际文件生成。

原因:在使用相对路径调用原理图时默认为调试模式,每次使用时添加 --debug=false--dry-run=false 关闭调试模式。

再次调用 schematics .:my-schematics --debug=false,至此,成功生成一个 hello.js 文件,其内容为 console.log('Hello Schematics');

4. 向原理图传入自定义选项

collection.json 中引入创建的 schema.json

1
2
3
4
5
6
7
8
9
10
{
"$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"my-schematics": {
"description": "A blank schematic.",
"factory": "./my-schematics/index#mySchematics",
"schema": "./my-schematics/schema.json" // add this
}
}
}

同时,也可以创建一个 schema.d.ts 来为我们的原理图提供代码及类型的检查。

1
2
3
export interface Schema {
name: string;
}

随后于 index.ts 中引入并使用。

1
2
3
4
5
6
7
8
9
10
import { Rule, SchematicContext, Tree } from "@angular-devkit/schematics";
import { Schema } from "./schema";

export function mySchematics(_options: Schema): Rule {
return (tree: Tree, _context: SchematicContext) => {
const { name } = _options;
tree.create("hello.js", `console.log('Hello ${name}');`);
return tree;
};
}

5. 使用原理图模板

./my-schematics/ 下创建 files 文件夹。 files 文件夹下创建一个 __name@dasherize__.component.ts.template 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { Component, OnInit } from '@angular/core';

@Component({
selector: 'my-<%= dasherize(name) %>',
templateUrl: './<%= dasherize(name) %>.component.html',
styleUrls: ['./<%= dasherize(name) %>.component.css']
})
export class <%= classify(name) %>Component implements OnInit {

constructor() { }

ngOnInit() {
}
}

模板文件书写:

  • 模板语言使用 EJS
  • @angular-devkit/core 中引入 strings ,使用其中的 dasherize / classify / underscore 等方法对 name 进行处理。

修改转换规则:

  • url() 指定模板文件路径。
  • applyTemplate() 接受向模板文件嵌入的参数及处理函数,转换文件同时删去 .template 后缀。
  • apply() 对模板源应用多个规则,并返回转换后的模板。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// index.ts
import {
Rule,
SchematicContext,
Tree,
mergeWith,
applyTemplates,
apply,
url,
} from "@angular-devkit/schematics";
import { Schema } from "./schema";
import { strings } from "@angular-devkit/core";

export function mySchematics(_options: Schema): Rule {
return (tree: Tree, _context: SchematicContext) => {
return mergeWith(
apply(url("./files"), [
applyTemplates({
...strings,
name: _options.name,
}),
])
)(tree, _context); //(tree, _context)防止TS编译器报警 "tree 未使用"
};
}

至此,可以在使用该原理图的目录下成功输出处理后的模板文件。


正式开发

主要 table 组件原理图开发

需求

  1. 生成常用列表页相关 Component 文件。
  2. 在最近一级 module.ts 中引入该 Component 并添加至 declarations中。

思路

ng generate component 功能的源代码作参考,调用 @schematics/angular 中的方法。

主要代码

问题及解决

  1. Q:新建 angular 项目测试该原理图可用,在实际 angular 后台项目中测试不可用。
    A:发现原理图中 @schematics/angular 等版本号为 9.1.2 ,实际项目 @angular/cli 使用 7.2.4 版,尝试更改相关依赖的版本为 7.2.4,引发下一问题。
  2. Q:更改版本后代码报错无法编译。
    A:发现旧版本中缺少 getWorkspace 方法。重新引入 getProject 方法并修改部分代码,成功编译并在实际项目中测试可用。

ng add

为了添加 ng add 支持,在本原理图根目录下执行 schematics blank --name=ng-add ,创建 ng-add原理图,调用 table 原理图及 schema.json 设置。

  • collection.json
1
2
3
4
5
"ng-add": {
"description": "Add schematic.",
"factory": "./ng-add/index",
"schema": "./table/schema.json"
}
  • index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import {
chain,
Rule,
schematic,
SchematicContext,
Tree,
} from "@angular-devkit/schematics";

export default function (_options: any): Rule {
return (host: Tree, context: SchematicContext) => {
return chain([schematic("table", _options)])(host, context);
};
}

发布

本地测试

在正式发布前,可以先在本地打包并测试其安装及使用。以本次开发的@nekoi/angular-schematics为例:

  1. 执行 npm pack ,打包得到 @nekoi/angular-schematics-1.0.0.tgz
  2. 在目标 Angular 项目下执行 npm i --no-save @nekoi/angular-schematics-1.0.0.tgz
  3. 在需要生成模板文件的文件夹下执行 ng g @nekoi/angular-schematics:table --name=userList

发布至 NPM

1
$ npm publish

@nekoi/angular-schematics 使用

1
2
3
4
5
6
第一种
$ ng add @nekoi/angular-schematics <page-name>

第二种
$ npm i -D @nekoi/angular-schematics
$ ng g @nekoi/angular-schematics:table <page-name>

参考

Total Guide To Custom Angular Schematics

Schematics を作ってみよう

Use Angular Schematics to Simplify Your Life

github.com/angular/angular-cli


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!