欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何用DevUI搭建自己的Angular組件庫(kù)

 更新時(shí)間:2021年05月21日 14:45:33   作者:DevUI團(tuán)隊(duì)  
DevUI 是一款面向企業(yè)中后臺(tái)產(chǎn)品的開源前端解決方案,它倡導(dǎo)沉浸、靈活、至簡(jiǎn)的設(shè)計(jì)價(jià)值觀,提倡設(shè)計(jì)者為真實(shí)的需求服務(wù),為多數(shù)人的設(shè)計(jì),拒絕嘩眾取寵、取悅眼球的設(shè)計(jì)。如果你正在開發(fā) ToB 的工具類產(chǎn)品,DevUI 將是一個(gè)很不錯(cuò)的選擇!

前言

作為前端開發(fā)者,隨著公司業(yè)務(wù)的不斷發(fā)展和增長(zhǎng),業(yè)務(wù)對(duì)組件功能、交互的訴求會(huì)越來(lái)越多,不同產(chǎn)品或者團(tuán)隊(duì)之間公用的組件也會(huì)越來(lái)越多,這時(shí)候就需要有一套用于支撐內(nèi)部使用的組件庫(kù),也可以是基于已有組件擴(kuò)展或者封裝一些原生三方庫(kù)。本文會(huì)手把手教你搭建自己的Angular組件庫(kù)。

創(chuàng)建組件庫(kù)

我們首先創(chuàng)建一個(gè)Angular項(xiàng)目,用來(lái)管理組件的展示和發(fā)布,用以下命令生成一個(gè)新的項(xiàng)目

ng new <my-project>

項(xiàng)目初始化完成后,進(jìn)入到項(xiàng)目下運(yùn)行以下cli命令初始化lib目錄和配置, 生成一個(gè)組件庫(kù)骨架

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

my-lib為自己指定的library名稱,比如devui,my-prefix為組件和指令前綴,比如d-xxx,默認(rèn)生成的目錄結(jié)構(gòu)如下

angular.json配置文件中也可以看到projects下面多出了一段項(xiàng)目類型為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"
    }
  }
},
...

關(guān)鍵配置修改

目錄布局調(diào)整

從目錄結(jié)構(gòu)可以看出默認(rèn)生成的目錄結(jié)構(gòu)比較深,參考material design,我們對(duì)目錄結(jié)構(gòu)進(jìn)行自定義修改如下:

修改說(shuō)明:

  • 刪除了my-lib目錄下的src目錄,把src目錄下的test.ts拷貝出來(lái),組件庫(kù)測(cè)試文件入口
  • 把組件平鋪到my-lib目錄下,并在my-lib目錄下新增my-lib.module.ts(用于管理組件的導(dǎo)入、導(dǎo)出)和index.ts(導(dǎo)出my-lib.module.ts,簡(jiǎn)化導(dǎo)入)
  • 修改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'; // 此處按照按需引入方式導(dǎo)入,my-lib對(duì)應(yīng)我們的發(fā)布庫(kù)名


@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"

庫(kù)構(gòu)建關(guān)鍵配置

ng-package.json配置文件,angular library構(gòu)建時(shí)依賴的配置文件

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

關(guān)鍵配置說(shuō)明:

  • dest,lib構(gòu)建輸出路徑,這里我們修改為publish目錄,和項(xiàng)目構(gòu)建dist目錄區(qū)分開
  • lib/entryFile,指定庫(kù)構(gòu)建入口文件,此處指向我們上文的index.ts

whitelistedNonPeerDependencies(可選),如果組件庫(kù)依賴了第三方庫(kù),比如lodash,需要在此處配置白名單,因?yàn)閚g-packagr構(gòu)建時(shí)為了避免第三方依賴庫(kù)可能存在多版本沖突的風(fēng)險(xiǎn),會(huì)檢查package.json的dependencies依賴配置,如果不配置白名單,存在dependencies配置時(shí)就會(huì)構(gòu)建失敗。

package.json配置,建議盡量使用peerDependcies,如果業(yè)務(wù)也配置了相關(guān)依賴項(xiàng)的話

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

詳細(xì)完整的配置,可以參考angular官方文檔 https://github.com/ng-packagr/ng-packagr/blob/master/docs/DESIGN.md

開發(fā)一個(gè)Alert組件

組件功能介紹

我們參考DevUI組件庫(kù)的alert組件開發(fā)一個(gè)組件,用來(lái)測(cè)試我們的組件庫(kù),alert組件主要是根據(jù)用戶傳入的類型呈現(xiàn)不同的顏色和圖標(biāo),用于向用戶顯示不同的警告信息。視覺(jué)顯示如下

組件結(jié)構(gòu)分解

首先,我們看一下alert組件目錄包含哪些文件

目錄結(jié)構(gòu)說(shuō)明:

  • 組件是一個(gè)完整的module(和普通業(yè)務(wù)模塊一樣),并包含了一個(gè)單元測(cè)試文件
  • 組件目錄下有一個(gè)package.json,用于支持二級(jí)入口(單個(gè)組件支持按需引入)
  • public-api.ts用于導(dǎo)出module、組件、service等,是對(duì)外暴露的入口,index.ts會(huì)導(dǎo)出public-api,方便其它模塊

關(guān)鍵內(nèi)容如下:

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


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

定義輸入輸出

接下來(lái)我們就開始實(shí)現(xiàn)組件,首先我們定義一下組件的輸入輸出,alert內(nèi)容我們采用投影的方式傳入,Input參數(shù)支持指定alert類型、是否顯示圖標(biāo)、alert是否可關(guān)閉,Output返回關(guān)閉回調(diào),用于使用者處理關(guān)閉后的邏輯

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';
  // 是否顯示圖標(biāo),用于支持用戶自定義圖標(biāo)
  @Input() showIcon = true;
  // 是否可關(guān)閉
  @Input() closeable = false;
  // 關(guān)閉回調(diào)
  @Output() closeEvent: EventEmitter<boolean> = new EventEmitter<boolean>();
  hide = false;
  constructor() {}


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

定義布局

根據(jù)api定義和視覺(jué)顯示我們來(lái)實(shí)現(xiàn)頁(yè)面布局結(jié)構(gòu),布局包含一個(gè)關(guān)閉按鈕、圖標(biāo)占位和內(nèi)容投影 ,組件關(guān)閉時(shí),我們采用清空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>

到這里,我們組件的頁(yè)面布局和組件邏輯已經(jīng)封裝完成,根據(jù)視覺(jué)顯示再加上對(duì)應(yīng)的樣式處理就開發(fā)完成了。

測(cè)試Alert組件

開發(fā)態(tài)引用組件

組件開發(fā)過(guò)程中,我們需要能夠?qū)崟r(shí)調(diào)試邏輯和調(diào)整UI展示,打開根目錄下的tsconfig.json,修改一下paths路徑映射,方便我們?cè)陂_發(fā)態(tài)就可以本地調(diào)試我們的組件,這里直接把my-lib指向了組件源碼,當(dāng)然也可以通過(guò)ng build my-lib --watch來(lái)使用默認(rèn)的配置, 指向構(gòu)建好的預(yù)發(fā)布文件,此時(shí)這里就要配置成我們修改過(guò)的目錄public/my-lib/*

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

配置完成后,就可以在應(yīng)用中按照npm的方式使用我們正在開發(fā)的庫(kù)了,我們?cè)赼pp.module.ts中先導(dǎo)入我們的正在開發(fā)的組件,這里可以從my-lib.module導(dǎo)入全部組件,或者直接導(dǎo)入我們的AlertModule(前面已經(jīng)配置支持二級(jí)入口)

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 { }

此時(shí)在app.component.html頁(yè)面中就可以直接使用我們正在開發(fā)的alert組件了

<section>
  <dev-alert>我是一個(gè)默認(rèn)類型的alert</dev-alert>
</section>

打開頁(yè)面,就可以看到當(dāng)前開發(fā)的效果,這時(shí)候我們就可以根據(jù)頁(yè)面表現(xiàn)來(lái)調(diào)整樣式和交互邏輯,此處就不繼續(xù)展示了

編寫單元測(cè)試

前面提到我們有一個(gè)單元測(cè)試文件,組件開發(fā)為了保證代碼的質(zhì)量和后續(xù)重構(gòu)組件的穩(wěn)定性,在開發(fā)組件的時(shí)候,有條件的建議加上單元測(cè)試。

由于我們調(diào)整了目錄結(jié)構(gòu),我們先修改一下相關(guān)配置

// angular.json
"my-lib": {
  ...
  "test": {
    "builder": "@angular-devkit/build-angular:karma",
    "options": {
      "main": "projects/my-lib/test.ts", // 這里指向調(diào)整后的文件路徑
      "tsConfig": "projects/my-lib/tsconfig.spec.json",
      "karmaConfig": "projects/my-lib/karma.conf.js"
    }
  },
}


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


"files": [
  "test.ts" // 指向當(dāng)前目錄下的測(cè)試入口文件
]

下面是一個(gè)簡(jiǎn)單的測(cè)試參考,只簡(jiǎn)單測(cè)試了type類型是否正確,直接測(cè)試文件中定義了要測(cè)試的組件,場(chǎng)景較多的時(shí)候建議提供demo,直接使用demo進(jìn)行不同場(chǎng)景的測(cè)試。

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>我是一個(gè)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);
    });
  }

通過(guò)執(zhí)行 ng test my-lib就可以執(zhí)行單元測(cè)試了,默認(rèn)會(huì)打開一個(gè)窗口展示我們的測(cè)試結(jié)果

到這一步,組件開發(fā)態(tài)引用、測(cè)試就完成了,功能和交互沒(méi)有問(wèn)題的話,就可以準(zhǔn)備發(fā)布到npm了。

更多測(cè)試內(nèi)容參考官方介紹:https://angular.cn/guide/testing

發(fā)布組件

組件開發(fā)完成后,單元測(cè)試也滿足我們定義的門禁指標(biāo),就可以準(zhǔn)備發(fā)布到npm提供給其他同學(xué)使用了。

首先我們構(gòu)建組件庫(kù),由于ng9之后默認(rèn)使用ivy引擎。官方并不建議把 Ivy 格式的庫(kù)發(fā)布到 NPM 倉(cāng)庫(kù)。因此在發(fā)布到 NPM 之前,我們使用 --prod 標(biāo)志構(gòu)建它,此標(biāo)志會(huì)使用老的編譯器和運(yùn)行時(shí),也就是視圖引擎(View Engine),以代替 Ivy。

ng build my-lib --prod

構(gòu)建成功后,就可以著手發(fā)布組件庫(kù)了,這里以發(fā)布到npm官方倉(cāng)庫(kù)為例

如果還沒(méi)有npm賬號(hào),請(qǐng)到官網(wǎng)網(wǎng)站注冊(cè)一個(gè)賬號(hào),選用public類型的免費(fèi)賬號(hào)就可以

已有賬號(hào),先確認(rèn)配置的registry是否指向npm官方registry https://registry.npmjs.org/

在終端中執(zhí)行npm login登錄已注冊(cè)的用戶

準(zhǔn)備工作都完成后,進(jìn)入構(gòu)建目錄,這里是publish目錄,然后執(zhí)行 npm publish --access public就可以發(fā)布了,注意我們的庫(kù)名需要是在npm上沒(méi)有被占用的,名字的修改在my-lib目錄下的package.json中修改。

npm發(fā)布參考: https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry

如果是內(nèi)部私有庫(kù),按照私有庫(kù)的要求配置registry就可以了,發(fā)布命令都是一樣的。

以上就是如何用DevUI搭建自己的Angular組件庫(kù)的詳細(xì)內(nèi)容,更多關(guān)于DevUI搭建自己的Angular組件庫(kù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論