如何用DevUI搭建自己的Angular组件库

前言

作为前端开发者,随着公司业务的不断发展和增长,业务对组件功能、交互的诉求会越来越多,不同产品或者团队之间公用的组件也会越来越多,这时候就需要有一套用于支撑内部使用的组件库,也可以是基于已有组件扩展或者封装一些原生三方库。本文会手把手教你搭建自己的Angular组件库。

创建组件库

我们首先创建一个Angular项目,用来管理组件的展示和发布,用以下命令生成一个新的项目

ng new <my-project>

项目初始化完成后,进入到项目下运行以下cli命令初始化lib目录和配置, 生成一个组件库骨架

ng generate library <my-lib> --prefix <my-prefix>

my-lib为自己指定的library名称,比如devui,my-prefix为组件和指令前缀,比如d-xxx,默认生成的目录结构如下

angular.json配置文件中也可以看到projects下面多出了一段项目类型为library的配置

"my-lib": {
  "projectType": "library",
  "root": "projects/my-lib",
  "sourceRoot": "projects/my-lib/src",
  "prefix": "dev",
  "architect": {
    "build": {
      "builder": "@angular-devkit/build-ng-packagr:build",
      "options": {
          "tsConfig": "projects/my-lib/tsconfig.lib.json",
          "project": "projects/my-lib/ng-package.json"
      },
  "configurations": {
    "production": {
      "tsConfig": "projects/my-lib/tsconfig.lib.prod.json"
    }
  }
},
...

关键配置修改

目录布局调整

从目录结构可以看出默认生成的目录结构比较深,参考material design,我们对目录结构进行自定义修改如下:

修改说明:

  • 删除了my-lib目录下的src目录,把src目录下的test.ts拷贝出来,组件库测试文件入口
  • 把组件平铺到my-lib目录下,并在my-lib目录下新增my-lib.module.ts(用于管理组件的导入、导出)和index.ts(导出my-lib.module.ts,简化导入)
  • 修改angular.json中my-lib下面的sourceRoot路径,指向my-lib即可

修改如下:

// my-lib.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AlertModule } from 'my-lib/alert'; // 此处按照按需引入方式导入,my-lib对应我们的发布库名

@NgModule({
  imports: [ CommonModule ],
  exports: [AlertModule],
  providers: [],
})
export class MyLibModule {}

// index.ts
export * from './my-lib.module';

//angular.json
"projectType": "library",
"root": "projects/my-lib",
"sourceRoot": "projects/my-lib", // 这里路径指向我们新的目录
"prefix": "devui"

库构建关键配置

ng-package.json配置文件,angular library构建时依赖的配置文件

{
  "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
  "dest": "../../publish",
  "lib": {
    "entryFile": "./index.ts"
  },
  "whitelistedNonPeerDependencies": ["lodash-es"]
}

关键配置说明:

  • dest,lib构建输出路径,这里我们修改为publish目录,和项目构建dist目录区分开
  • lib/entryFile,指定库构建入口文件,此处指向我们上文的index.ts

whitelistedNonPeerDependencies(可选),如果组件库依赖了第三方库,比如lodash,需要在此处配置白名单,因为ng-packagr构建时为了避免第三方依赖库可能存在多版本冲突的风险,会检查package.json的dependencies依赖配置,如果不配置白名单,存在dependencies配置时就会构建失败。

package.json配置,建议尽量使用peerDependcies,如果业务也配置了相关依赖项的话

{
  "name": "my-lib",
  "version": "0.0.1",
  "peerDependencies": {
    "@angular/common": "^9.1.6",
    "@angular/core": "^9.1.6",
    "tslib": "^1.10.0"
  }
}

详细完整的配置,可以参考angular官方文档 https://github.com/ng-packagr/ng-packagr/blob/master/docs/DESIGN.md

开发一个Alert组件

组件功能介绍

我们参考DevUI组件库的alert组件开发一个组件,用来测试我们的组件库,alert组件主要是根据用户传入的类型呈现不同的颜色和图标,用于向用户显示不同的警告信息。视觉显示如下

组件结构分解

首先,我们看一下alert组件目录包含哪些文件

目录结构说明:

  • 组件是一个完整的module(和普通业务模块一样),并包含了一个单元测试文件
  • 组件目录下有一个package.json,用于支持二级入口(单个组件支持按需引入)
  • public-api.ts用于导出module、组件、service等,是对外暴露的入口,index.ts会导出public-api,方便其它模块

关键内容如下:

// package.json
{
  "ngPackage": {
    "lib": {
      "entryFile": "public-api.ts"
    }
  }
}

//public-api.ts
/*
* Public API Surface of Alert
*/
export * from './alert.component';
export * from './alert.module';

定义输入输出

接下来我们就开始实现组件,首先我们定义一下组件的输入输出,alert内容我们采用投影的方式传入,Input参数支持指定alert类型、是否显示图标、alert是否可关闭,Output返回关闭回调,用于使用者处理关闭后的逻辑

import { Component, Input } from '@angular/core';
// 定义alert有哪些可选类型
export type AlertType = 'success' | 'danger' | 'warning' | 'info';

@Component({
  selector: 'dev-alert',
  templateUrl: './alert.component.html',
  styleUrls: ['./alert.component.scss'],
})
export class AlertComponent {
  // Alert 类型
  @Input() type: AlertType = 'info';
  // 是否显示图标,用于支持用户自定义图标
  @Input() showIcon = true;
  // 是否可关闭
  @Input() closeable = false;
  // 关闭回调
  @Output() closeEvent: EventEmitter<boolean> = new EventEmitter<boolean>();
  hide = false;
  constructor() {}

  close(){
    this.closeEvent.emit(true);
    this.hide = true;
  }
}

定义布局

根据api定义和视觉显示我们来实现页面布局结构,布局包含一个关闭按钮、图标占位和内容投影 ,组件关闭时,我们采用清空dom的方式处理。

<div class="dev-alert {{ type }} " *ngIf="!hide">
  <button type="button" class="dev-close" (click)="close()" *ngIf="closeable"></button>
  <span class="dev-alert-icon icon-{{ type }}" *ngIf="showIcon"></span>
  <ng-content></ng-content>
</div>

到这里,我们组件的页面布局和组件逻辑已经封装完成,根据视觉显示再加上对应的样式处理就开发完成了。

测试Alert组件

开发态引用组件

组件开发过程中,我们需要能够实时调试逻辑和调整UI展示,打开根目录下的tsconfig.json,修改一下paths路径映射,方便我们在开发态就可以本地调试我们的组件,这里直接把my-lib指向了组件源码,当然也可以通过ng build my-lib --watch来使用默认的配置, 指向构建好的预发布文件,此时这里就要配置成我们修改过的目录public/my-lib/*

"paths": {
  "my-lib": [
    "projects/my-lib/index.ts"
  ],
  "my-lib/*": [
    "projects/my-lib/*"
  ],
}

配置完成后,就可以在应用中按照npm的方式使用我们正在开发的库了,我们在app.module.ts中先导入我们的正在开发的组件,这里可以从my-lib.module导入全部组件,或者直接导入我们的AlertModule(前面已经配置支持二级入口)

import { AlertModule } from 'my-lib/alert';
// import { MyLibModule } from 'my-lib';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    // MyLibModule
    AlertModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

此时在app.component.html页面中就可以直接使用我们正在开发的alert组件了

<section>
  <dev-alert>我是一个默认类型的alert</dev-alert>
</section>

打开页面,就可以看到当前开发的效果,这时候我们就可以根据页面表现来调整样式和交互逻辑,此处就不继续展示了

编写单元测试

前面提到我们有一个单元测试文件,组件开发为了保证代码的质量和后续重构组件的稳定性,在开发组件的时候,有条件的建议加上单元测试。

由于我们调整了目录结构,我们先修改一下相关配置

// angular.json
"my-lib": {
  ...
  "test": {
    "builder": "@angular-devkit/build-angular:karma",
    "options": {
      "main": "projects/my-lib/test.ts", // 这里指向调整后的文件路径
      "tsConfig": "projects/my-lib/tsconfig.spec.json",
      "karmaConfig": "projects/my-lib/karma.conf.js"
    }
  },
}

//my-lib 目录下的tsconfig.spec.json  

"files": [
  "test.ts" // 指向当前目录下的测试入口文件
]

下面是一个简单的测试参考,只简单测试了type类型是否正确,直接测试文件中定义了要测试的组件,场景较多的时候建议提供demo,直接使用demo进行不同场景的测试。

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { Component } from '@angular/core';
import { AlertModule } from './alert.module';
import { AlertComponent } from './alert.component';
import { By } from '@angular/platform-browser';

@Component({
  template: `
    <dev-alert [type]="type" [showIcon]= "showIcon"[closeable]="closeable"    (closeEvent)="handleClose($event)">
    <span>我是一个Alert组件</span>
    </dev-alert>
  `
})
class TestAlertComponent {
  type = 'info';
  showIcon = false;
  closeable = false;
  clickCount = 0;
  handleClose(value) {
    this.clickCount++;
  }
}

describe('AlertComponent', () => {
  let component: TestAlertComponent;
  let fixture: ComponentFixture<TestAlertComponent>;
  let alertElement: HTMLElement;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [AlertModule],
      declarations: [ TestAlertComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TestAlertComponent);
    component = fixture.componentInstance;
    alertElement = fixture.debugElement.query(By.directive(AlertComponent)).nativeElement;
    fixture.detectChanges();
  });

  describe('alert instance test', () => {
    it('should create', () => {
      expect(component).toBeTruthy();
    });
  });

  describe('alert type test', () => {
    it('Alert should has info type', () => {
      expect(alertElement.querySelector('.info')).not.toBe(null);
    });

    it('Alert should has success type', () => {
      // 修改type,判断类型改变是否正确
      component.type = 'success';
      fixture.detectChanges();
      expect(alertElement.querySelector('.success')).not.toBe(null);
    });
  }

通过执行 ng test my-lib就可以执行单元测试了,默认会打开一个窗口展示我们的测试结果

到这一步,组件开发态引用、测试就完成了,功能和交互没有问题的话,就可以准备发布到npm了。

更多测试内容参考官方介绍:https://angular.cn/guide/testing

发布组件

组件开发完成后,单元测试也满足我们定义的门禁指标,就可以准备发布到npm提供给其他同学使用了。

首先我们构建组件库,由于ng9之后默认使用ivy引擎。官方并不建议把 Ivy 格式的库发布到 NPM 仓库。因此在发布到 NPM 之前,我们使用 --prod 标志构建它,此标志会使用老的编译器和运行时,也就是视图引擎(View Engine),以代替 Ivy。

ng build my-lib --prod

构建成功后,就可以着手发布组件库了,这里以发布到npm官方仓库为例

如果还没有npm账号,请到官网网站注册一个账号,选用public类型的免费账号就可以

已有账号,先确认配置的registry是否指向npm官方registry https://registry.npmjs.org/

在终端中执行npm login登录已注册的用户

准备工作都完成后,进入构建目录,这里是publish目录,然后执行 npm publish --access public就可以发布了,注意我们的库名需要是在npm上没有被占用的,名字的修改在my-lib目录下的package.json中修改。

npm发布参考: https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry

如果是内部私有库,按照私有库的要求配置registry就可以了,发布命令都是一样的。

以上就是如何用DevUI搭建自己的Angular组件库的详细内容,更多关于DevUI搭建自己的Angular组件库的资料请关注我们其它相关文章!

(0)

相关推荐

  • Angular 2使用路由自定义弹出组件toast操作示例

    本文实例讲述了Angular 2使用路由自定义弹出组件toast操作.分享给大家供大家参考,具体如下: 原理: 使用Angular2的命名路由插座,一个用来显示app正常的使用,一个用来显示弹出框, <router-outlet name='apps'></router-outlet> <router-outlet name='popup'></router-outlet> 浏览器的导航栏中则这样显示 http://127.0.0.1:4200/(popup

  • Angular性能优化之第三方组件和懒加载技术

    概述 应该有很多人都抱怨过 Angular 应用的性能问题.其实,在搭建Angular项目时,通过使用打包.懒加载.变化检测策略和缓存技术,再辅助第三方组件,便可有效提升项目性能. 为了帮助开发者深入理解和使用Angular,本文将以在线表格编辑为例,演示如何借助懒加载技术,在基于 Angular的框架中实现在线导入导出Excel以及数据在线填报的功能. 环境准备 1.全局安装Angular CLI:npm install -g @angular/cli 2.使用Angular CLI创建一个新

  • angular组件间通讯的实现方法示例

    前言 一个Angular应用一般情况下包含多个组件,而且要让组件互相之间能进行通讯(数据传送),这样才能构成一个有机的完整系统. 1.情景引入 下面例举一个实际遇到的情况: 上图页面包含两个组件,"新增班级组件"和"选择教师组件",在新增班级时需要选择改班级的管理教师,管理教师列表要从数据库中获取.选择好教师后,"选择教师组件"要把选择的教师对象传递回"新增班级组件",这其中涉及到对象传递称为通讯. 2.组件关系 组件之间有几

  • angular组件间传值测试的方法详解

    前言 我们知道angular组件间通讯有多种方法,其中最常用的一种方法就是借助于 @Input 和 @Output 进行通讯.具体如何通讯请参考angular组件间通讯,本文不再赘述,我们来讲讲关于此方法如何进行单元测试. 创建假组件 我们单元测试父组件与子组件的的交互是否符合我们的要求,我们在父组件进行测试,就需要模拟一个假的子组件出来,这样排除其他因素对测试的影响. 比如现在我在分页组件里写了一个每页大小选择组件,现在要测试一下组件间交互.现在分页组件就是我们的父组件,每页大小组件就是我们的

  • Angular2使用SVG自定义图表(条形图、折线图)组件示例

    本文实例讲述了Angular2使用SVG自定义图表(条形图.折线图)组件.分享给大家供大家参考,具体如下: 要求:用户将数据作为参数传进来,通过类型决定渲染何种类型的图标. demo: html: <ngo-chart [inputParams]="options"></ngo-chart> ts: options = { type: 'line', //图表类型 xAxis: { //X轴的数据 data: ['Mon', 'Tue', 'Wed', 'Thu

  • 利用Angular7开发一个Radio组件的全过程

    一.准备工作 Angular7(以下简称ng7),已经跟之前版本大有不同.新建工程后,可方便创建library(简称lib),lib是什么呢?就是一个npm包的源码包.npm作为强大的包管理器,已经成为很多FEer分享智慧成果的法器.本文主要介绍本人写的一个radio组件. 二.开发组件radio过程 1.使用ng cli,新建工程,创建lib // 安装ng cli npm install -g @angular/cli // 新建工程 ng new ng-project // 进入ng-pr

  • angular6开发steps步骤条组件

    本文实例为大家分享了angular6开发steps步骤条组件的实现代码,供大家参考,具体内容如下 1.先展示步骤条效果 2.使用angular命令快速创建组件 ng g c component/steps 创建的组件在component文件夹下面 打开steps.component.ts文件,可以看到selector的值是app-steps import { Component, OnInit} from '@angular/core'; @Component({ selector: 'app-

  • 使用Angular material主题定义自己的组件库的配色体系

    本期为Angular系列的第一篇文章,我会从这里搭建Angular sample项目.组件库.主题.然后每个组件等.使之成为一个比较通用的组件库系列文章,目的有二: 1.自己在写系列文章过程中不断夯实基础.不断学习补缺: 2.分享给一些不熟悉angular及自定义组件的同学,使之快速上手并提高. 1. 使用Angular CLI命令行工具生成一个Angular sample的项目:这里添加了一个optional的参数--style=scss,是为了后面使用angular material的the

  • 使用 Angular RouteReuseStrategy 缓存(路由)组件的实例代码

    使用 Angular RouteReuseStrategy 缓存组件 Cache components with Angular RouteReuseStrategy RouteReuseStrategy provider 允许我们控制 Angular 路由和组件生命周期的行为. 当我们在组件间切换的时候,Angular都会销毁上一个组件,并且创建一个新的组件.在大多数情况下,我们可能不想让它这样工作,因为每次加载一个组件,可能会有很多类似HTTP请求一样的昂贵的操作. 这时候就需要RouteR

  • 如何用DevUI搭建自己的Angular组件库

    前言 作为前端开发者,随着公司业务的不断发展和增长,业务对组件功能.交互的诉求会越来越多,不同产品或者团队之间公用的组件也会越来越多,这时候就需要有一套用于支撑内部使用的组件库,也可以是基于已有组件扩展或者封装一些原生三方库.本文会手把手教你搭建自己的Angular组件库. 创建组件库 我们首先创建一个Angular项目,用来管理组件的展示和发布,用以下命令生成一个新的项目 ng new <my-project> 项目初始化完成后,进入到项目下运行以下cli命令初始化lib目录和配置, 生成一

  • 使用VitePress搭建及部署vue组件库文档的示例详解

    目录 安装vitepress 目录结构 文档首页 配置 导航栏配置 侧边栏 部署到GitHub Pages 每个组件库都有它们自己的文档.所以当我们开发完成我们自己的组件库必须也需要一个组件库文档.如果你还不了解如何搭建自己的组件库可以看这里->从零搭建Vue3组件库.看完这篇文章你就会发现原来搭建和部署一个组件库文档是那么的简单.当然部署也不需要你有自己的服务器,你只要有github即可.由于我们的组件库还没有完成,所以下面就以element-plus作为示例来搭建一个文档吧. 安装vitep

  • vue3使用Vite打包组件库从0搭建过程详解

    目录 手动搭建一个用于测试组件库组件 Vue3 项目 初始化 ts 搭建一个基于 vite 的 vue3 项目 安装插件 配置 vite.config.ts 新建入口 html 文件 app.vue 入口 main.ts 配置脚本启动项目 手动搭建一个用于测试组件库组件 Vue3 项目 本篇文章将在项目中引入 typescript,以及手动搭建一个用于测试组件库组件 Vue3 项目 因为我们是使用 Vite+Ts 开发的是 Vue3 组件库,所以我们需要安装 typescript.vue3,同时

  • 对于组件库的思考及技术梳理详解

    目录 为什么要做? 组件库的技术选型 组件库的方向 组件库的设计 结语 为什么要做? 18年的时候觉得写ui组件库的人都好牛,出于好奇看了elementui.iview和Vant的源码.从那之后就萌生了自己也可以尝试搭个组件库试试,一是可以学习到很多知识,二是可以向牛人靠齐.可那时候又是个菜鸟,从0到1搭建对我来说是件很难的事情.当时借助从vue-cli创建的项目,做了修修改改,这样简单的组件库就出来了vvmui,现在回头看看那时候做的东西是真的菜... 近些年来,随着前端项目的复杂度越来越高,

  • 动态创建Angular组件实现popup弹窗功能

    起步: 直接使用ngIf 把弹窗的DOM直接放在页面底下隐藏,通过ngIf这样的指令控制其显示. 改进: 封装成angular模块,通过服务控制其显示 直接使用ngIf的话,让人不爽的地方就在于不够通用,每个页面都得加DOM.改进的话可以把比较通用的一些DOM封装成组件,统一添加到全局页面中,并将显示的控制交给一个angular服务来控制其显示. 比如定义了两个组件(DialogComponent, AlertComponent),将他们都添加到AppComponent下,然后提供一个Popup

  • Angular组件化管理实现方法分析

    本文实例分析了Angular组件化管理实现方法.分享给大家供大家参考,具体如下: 在做sass产品页面的时候,往往每个页面的header和footer都是一样的,还有最近我做的页面,类似datetimepicker这种组件,其实都是可以复用的代码,所以如果能把这些公用的UI组件提取出来,对于维护就会方便很多啦!! angular框架就支持这种组件化管理,不过也有优缺点,我先来说实现方法哈! index.html:没有用到路由,所以js都是src生引进来的 <head> <title>

  • 教你搭建按需加载的Vue组件库(小结)

    按需加载的原理 按需加载,本质上是把一个组件库的不同组件 拆分成不同文件 ,按照需要引用对应的文件,而该文件暴露一个 install方法 ,供Vue.use使用. 比如:我只想引用element库里的一个Button组件 import Button from 'element-ui/lib/Button.js' import Button from 'element-ui/lib/theme-chalk/Button.css' Vue.use(Button); 上面的写法比较繁琐,而且需要知道每

  • Vue cli3 库模式搭建组件库并发布到 npm的流程

    市面上目前已有各种各样的UI组件库,比如 Element 和 iView ,他们的强大毋庸置疑.但是我们面临的情况是需求越来越复杂,当它们不能再满足我们需求的时候,这个时候就有必要开发一套属于自己团队的组件库了. 所以本文的目的就是让读者能通过此文,小能做一个简单的插件供人使用,大能架构和维护一个组件库不在话下. 以下一个简单的颜色选择器插件 vColorPicker 讲述从开发到上线到npm的流程. vColorPicker 插件 DEMO 一.技术栈 如何通过新版脚手架创建项目,这里就不提了

  • React中如何引入Angular组件详解

    前言 为了在我的编辑器中使用 Angular,我用 Angular 编写了一个重命名功能.而为了使用它,我得再次使用一次 customEvent ,而在这个微前端架构的系统中,其事件通讯机制已经相当的复杂.在这部分的代码进一步恶化之前,我得尝试有没有别的方式.于是,我想到了之前在其它组件中使用的 Web Components 技术,而 Angular 6 正好可以支持. 下面话不多说了,来一起看看详细的介绍吧 HTML 中引入 Web Components 我所需要做的事情也相当的简单,只需要将

随机推荐