前言
从本文开始将逐步介绍 NG-NEST UI 库的项目源码结构和组件是如何设计制作的。
环境准备
通过以下命令来下载 ng-nest 源码:
$ git clone https://github.com/NG-NEST/ng-nest.git$ cd ng-nest$ npm install
核心目录介绍
| 文件夹名称 | 说明 | | docs | 非组件文档,项目简介、快速上手、教程等 || lib | 组件文件夹,包含框架组件源码 || scripts | ts 脚本,主要用来生成文档页面组件以及相关的路由配置 || src | 文档项目,生成的文档会自动放到 src/main/docs 下 |
package.json 文件中的 scripts 说明
..."scripts": { "ng": "ng", "postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points", // 加快 ngcc 编译 "start": "ng serve", // 启动文档项目 "start:en": "ng serve --configuration=en", "start:zh": "ng serve --configuration=zh", "build": "node --max_old_space_size=81920 ./node_modules/@angular/cli/bin/ng build --prod ", // 打包文档项目 "build:en": "ng build --prod --configuration=production-en", "build:zh": "ng build --prod --configuration=production-zh", "build:ng-nest-ui": "node --max_old_space_size=81920 ./node_modules/@angular/cli/bin/ng build ng-nest-ui --prod && npm run build:scss", // 打包组件库并拷贝相关scss样式文件 "build:docs": "npm run build:scripts && node ./scripts/build/generate/docs", // 生成文档页面 "build:scripts": "tsc -p scripts", // ts 脚本编译成 js "build:scss": "cpx ./lib/ng-nest/ui/style/**/* ./dist/ng-nest/ui/style", // 拷贝组件库样式文件 "test": "ng test ng-nest-site", // 测试文档项目 "test:ng-nest-ui": "ng test ng-nest-ui", // 组件测试,通过此处开发测试单个组件 "lint": "ng lint", "e2e": "ng e2e", "extract": "ng xi18n --output-path=locale" // 文档项目的本地化配置},...
组件结构
我们以 Button 组件来介绍一下组件的结构,所有组件的结构都是遵循此格式。
lib/ng-nest/ui/button├── examples 示例代码,组件的示例说明通过此处生成├── style 样式 @mixin 和 样式参数定义├── button.component.html ├── button.component.scss├── button.component.spec.ts 测试文件,测试模式下直接访问对应的关键子调试单个组件├── button.component.ts├── button.module.ts 组件模块,声明模块中的视图,依赖的模块,以及导出的视图├── button.property.ts 组件属性( Input 输入和 Ouput 输出参数),以及相关的类型定义,文档中的组件参数说明通过此处生成├── buttons.component.scss├── buttons.component.ts├── index.ts├── package.json ng-packagr 配置,单独引入组件├── public-api.ts 导出所有当前组件中的 class 文件└── readme.md 组件文件以及API
Core 文件夹
lib/ng-nest/ui/core 文件夹主要用来定义公共的函数、接口、动画、服务等,如Checkbox 组件中的 checkbox.property.ts:
.../** * Checkbox Property */@Component({ template: '' })export class XCheckboxProperty extends XControlValueAccessor<boolean | Array<any>> implements XCheckboxOption { /** * 多选框数据 */ @Input() @XDataConvert() data: XData<XCheckboxNode> = []; /** * 按钮样式 */ @Input() @XInputBoolean() button: XBoolean; ...}
XControlValueAccessor 是一个表单的抽象泛型类,boolean | Array<any> 表示控件 ngModel 中绑定的值可能是 boolean(data为单个值)或者 Array 数组(data为多个值),其中还定义了表单通用属性以及方法,并实现了 Angular 中做自定义表单控件需要的接口ControlValueAccessor :
export abstract class XControlValueAccessor<T> extends XFormProp implements ControlValueAccessor { ... value: T; onChange: (value: T) => void; onTouched: () => void; writeValue(value: T): void { this.value = value; } registerOnChange(fn: (value: T) => void): void { this.onChange = fn; } registerOnTouched(fn: () => void): void { this.onTouched = fn; } setDisabledState(disabled: boolean) { this.disabled = disabled; } ...}
data 属性前面的 @XDataConvert() 是一个数据转换的装饰器,Checkbox 的选项数据可以支持以下任意一种格式(最终都被转换成统一的格式):
dataOne = ['QQ', '微信', '钉钉', '微博'];dataTwo = ['QQ', '微信', { label: '钉钉', disabled: true }, '微博'];dataThree = [ { label: 'QQ', icon: 'ado-qq' }, { label: '微信', icon: 'ado-wechat' }, { label: '钉钉', icon: 'ado-dingding' }, { label: '微博', icon: 'ado-weibo' }];dataFour = new Observable((x) => { // 替换成http请求,或者data直接定义成 Observable 对象 x.next(['QQ', '微信', '钉钉', '微博']); x.complete();});
data 属性的类型 XData<XCheckboxNode> 是一个联合类型,通过 XData<T>来定义:
// 数据类型export type XData<T> = T[] | Observable<T[] | any[]> | any[] | Function;
button 属性前面的 @XInputBoolean() 装饰器主要是使组件支持以下的写法,可以省略传入true 值(写了属性,没有传入值默认转换成 true):
<x-checkbox [data]="data" button></x-checkbox>
Core 文件夹里面还封装了 Angular 官方的 HttpClient 请求、路由复用的配置、localStorage 和 sessionStorage 服务、树结构的类型定义等等。
Style 文件夹
lib/ng-nest/ui/style 定义公共的 @mixin 和参数,并把 css3 中的 var 参数关联到 scss 的变量中,然后各个组件的 style 文件样式中调用。
总结
通过以上内容我们对整个项目有了一个大致的了解,由于单个组件的内容是集中的,模板、示例、样式、配置、测试、文档等,我们可以很容易的处理组件问题。之后会逐步从简单的组件切入,带领大家去体验如何设计制作组件。