原理图(Schematics)
UpdateTime: 20200513
本文主要介绍本人从入门 Schematics 到开发 @nekoi/angular-schematics
(npm,github)的流程与部分心得,可当做简单的教程阅读。
准备
- 全局安装
@angular-devkit/schematics-cli
以使用 schematics
命令
| $ npm install -g @angular-devkit/schematics-cli
|
- 创建新的
Schematics
项目
| $ schematics blank <schematic-name>
|
入门
创建一个原理图入门项目:
| $ schematics blank my-schematics $ cd my-schematics $ npm i
|
1. 原理图文件构成
collection.json
文件是整个原理图库的主要定义文件,并且包含该库中所有可用原理图的定义。
| { "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json", "schematics": { "my-schematics": { "description": "A blank schematic.", "factory": "./my-schematics/index#mySchematics" } } }
|
| import { Rule, SchematicContext, Tree } from "@angular-devkit/schematics"; export function mySchematics(_options: any): Rule { return (tree: Tree, _context: SchematicContext) => { return tree; }; }
|
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 } } }, "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. 一个最简单的例子
| export function mySchematics(_options: any): Rule { return (tree: Tree, _context: SchematicContext) => { tree.create("hello.js", `console.log('Hello Schematics');`); return tree; }; }
|
构建并在同一根目录下运行:
| $ 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
。
| { "$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" } } }
|
同时,也可以创建一个 schema.d.ts
来为我们的原理图提供代码及类型的检查。
| export interface Schema { name: string; }
|
随后于 index.ts
中引入并使用。
| 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
文件。
| 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
| 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); }; }
|
至此,可以在使用该原理图的目录下成功输出处理后的模板文件。
正式开发
主要 table 组件原理图开发
需求
- 生成常用列表页相关
Component
文件。 - 在最近一级
module.ts
中引入该 Component
并添加至 declarations
中。
思路
以 ng generate component
功能的源代码作参考,调用 @schematics/angular
中的方法。
主要代码
问题及解决
- Q:新建 angular 项目测试该原理图可用,在实际 angular 后台项目中测试不可用。
A:发现原理图中 @schematics/angular
等版本号为 9.1.2
,实际项目 @angular/cli
使用 7.2.4
版,尝试更改相关依赖的版本为 7.2.4
,引发下一问题。 - Q:更改版本后代码报错无法编译。
A:发现旧版本中缺少 getWorkspace
方法。重新引入 getProject
方法并修改部分代码,成功编译并在实际项目中测试可用。
ng add
为了添加 ng add
支持,在本原理图根目录下执行 schematics blank --name=ng-add
,创建 ng-add
原理图,调用 table
原理图及 schema.json
设置。
| "ng-add": { "description": "Add schematic.", "factory": "./ng-add/index", "schema": "./table/schema.json" }
|
| 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
为例:
- 执行
npm pack
,打包得到 @nekoi/angular-schematics-1.0.0.tgz
- 在目标 Angular 项目下执行
npm i --no-save @nekoi/angular-schematics-1.0.0.tgz
- 在需要生成模板文件的文件夹下执行
ng g @nekoi/angular-schematics:table --name=userList
发布至 NPM
@nekoi/angular-schematics
使用
| 第一种 $ 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