Angular - library created

Record how to build my own public component library in angular project


随着项目的不断迭代,我们逐渐积累了一系列可复用的业务组件(基于Angular Material。直到某天,老板要开发一个Admin新项目。我们决定抽离出一些公共组件成独立库,不但利于代码复用,还保证了项目整体风格的一致性。记录一下如何在Angular项目中搭建自己的组件库。


项目搭建

在Angular项目中,想搭建一个自己的组件库其实非常的简单, Angular Cli 工具可以帮我们实现绝大部分工作。

git bash 复制代码
npm i @angular/cli -g

闲话少说,开始我们的第一步

初始化新项目。

git bash 复制代码
ng new my-ui --no-create-application

参数 --no-create-application 非常重要,这样在创建工作区时跳过初始应用程序的生成。为什么要创建一个没有初始应用的工程? 这里就要谈到我们的目标了-> 我们要开发一个公共的组件库,当然在开发的工程中同样需要调试和生成组件库的文档。那么二者就可以拆分为两个项目:1. common library 2. library document.
谈到一个工程里面多个项目,我们最好的选择就是monorepo 模型。多项目工作区适用于使用单个存储库全局配置来管理多个 Angular 项目的组织。

创建多项目

打开我们的项目,命令行输入

git bash 复制代码
ng generate application app
ng generate library lib


可以看到在我们的工作区生成了两个独立的项目。
同时cli还帮我们生成了对应的配置文件到angular.json。

编写通用组件

由于本文重点在于流程,这里就偷懒用一个非常简单的测试例子。
由于我们的组件是基于 Angular Material 的封装。所以我们的组件库的必要依赖除了Angular的核心包之外还需要配置 Angular Material的依赖包。
打开 lib 目录下的package.json文件添加依赖项

json 复制代码
{
  "name": "lib",
  "version": "0.0.1",
  "peerDependencies": {
    "@angular/common": "^19.2.0",
    "@angular/core": "^19.2.0",
    "@angular/material": "^19.2.19",
    "@angular/cdk": "^19.2.19"
  },
  "dependencies": {
    "tslib": "^2.3.0"
  },
  "sideEffects": false
}

至于peerDependence, 请到peer-dependencies
安装依赖:

language 复制代码
ng add @angular/material --project=app

这里指定 --project=app 是为了 Set up global Angular Material typography styles。
打开lib> src > lib 新建文件夹button
lib > src > lib > button 新建button-component.

ts 复制代码
import { ChangeDetectionStrategy, Component, Input } from "@angular/core";
import { MatButtonModule } from "@angular/material/button";

@Component({
    selector: 'my-button',
    standalone: true,
    template: `<button mat-button appearance="filled"><ng-content></ng-content></button>`,
    imports: [MatButtonModule],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyButton {}

打开public-api将我们的组件导出

ts 复制代码
export * from './lib/button/button.component';

一切准备就绪,可以给我们的组件库打包了

git bash 复制代码
ng build lib --watch

至于为什么加参数 --watch,了解webpack 和vite的同学应该想到了(目的是当我们修改我们lib项目下的文件时会自动触发打包更新)。执行完命令后我们会发现生成了一个dist目录,并且里面包含了我们的组件。

到了这里其实我们的组件库已经打包成功,可以直接发布了!但不要忘了,我们还有app项目来测试以及为我们的组件库提供文档服务呢。那么本地环境如何使用我们打包好的库呢?

使用和测试组件

其实测试组件库的方法有很多:

发布安装:库执行 npm publish,npm i <package-name>。本地开发的话有些本末倒置了。

本地文件安装: npm i file:../my-ui/dist/lib。

npm link:在库项目 npm link,在应用项目 npm link <package-name>(开发时方便,但不适合 CI)。

但这里最推荐的是 “路径映射 + 本地构建产物”。还记得我们的项目结构吗?可以看上面的截图。我们的lib和app是在同一工作区统一配置的,仔细发现各自的tsconfig都继承自在外层的tsconfig文件,为此可以采用路径映射。 具体怎么实现呢 ?
打开我们项目最外层的tsconfig.json, 新增一条配置:

json 复制代码
{
  "compilerOptions": {
    "paths": {
      "lib": [
        "./dist/lib"
      ]
    },
  }
}

同时库构建产物 dist/lib/package.json 的包名也是 "name": "lib"。
所以在同一工作区内,应用在构建/运行时会把 import { MyButton } from 'lib' 解析到本地的 dist/lib,无需 npm link。

在app中使用组件,打开app.component.ts,导入组件。

ts 复制代码
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { MyButton } from 'lib';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, MyButton],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
})
export class AppComponent {
  title = 'app';
}

再在app.component.html中使用

复制代码
<p>Congratulations! Your app is running. 🎉</p>
<my-button>Click</my-button>

启动我们的app,新开一个命令行窗口

git bash 复制代码
ne serve

打开我们的app可以看到组件成功使用了。

当然完整的流程还要涉及到CI CD 的相关配置,这里就不多说了。