发布于 2026-01-06 1 阅读
0

Angular 10 + Storybook + npm 包 = NG 设计系统 💜 - 分步指南 1. 背景和决策 👨🏽‍💻 2. 如何实现 Angular UI 工具包 / NG UI 库 3. 测试 Storybook、原子导入和 Web 组件等 4. 总结 Angular 10.x Storybook 库

Angular 10 + Storybook + npm 包 = NG 设计系统💜 - 分步指南

1. 背景与决策 👨🏽‍💻

2. 如何实现 Angular UI 套件/NG UI 库

3. 测试 Storybook、原子导入和 Web 组件等。

4. 结论

Angular 10.x Storybook 库

这里使用的Angular版本是10.x

简而言之——任务是:

使用 Angular 创建一个可独立运行并可扩展到多种产品使用的 UI 工具包。

或者用专业术语来说:

npm i @your-company/your-ui-kit

import { Button } from '@your-company/your-ui-kit'
Enter fullscreen mode Exit fullscreen mode

最终结果:

  • 基于 Angular 的库已发布为@your-prefix/pattern-lib
  • 使用 Storybook 预览库元素
  • 使用和导出全局scss样式

1. 背景与决策 👨🏽‍💻

我们的产品应用基于Angular 框架。我们希望它具有可扩展性,以便即使在其他产品中也能快速复用组件。
这些组件必须始终与品牌视觉风格保持一致。

为了实现这一目标,我们希望采用事实上的标准做法:创建一个以模式库(以下简称模式库)形式实现的设计系统。

1.1. 对 Web Components 说“不”,对 NG Components 说“是”👀

如果你在不同的平台上关注我,就会知道我非常喜欢Web Components但是

使用合适的工具完成合适的工作🛠

Mercedes-Benz.io,我们使用 Web Components 和Stencil来创建模式库。之所以做出这样的决定,是因为我们的产品技术栈中包含 React、Vue、Angular,甚至还有部分 Elm,因此我们需要找到一种与框架无关的库方案。

然而,独立于框架的缺点在于,由于混合了不同的技术,无法从该框架中获得最佳性能——尤其是在你的需求是“启用 SSR”时,情况会变得非常糟糕。

所以归根结底:你需要根据你的业务模式来权衡利弊。

促成基于 Angular 的 PatternLib 诞生的过程

  1. Angular 在编译过程中会进行大量的预处理。因此,编译器可以确保在使用 Angular 时获得最佳的整体输出。Angular 无法优化您放在 Web 组件中的代码。

  2. 只有当你计划长期支持多个框架时,维护一个与框架无关的解决方案才有意义。

  3. 团队规模小:避免在 Stencil 和 Angular 等工具之间频繁切换。坚持使用 Angular 能带来更好的整体效果,因为我们可以应用相同的开发准则。

  4. 当混合使用两种技术时,你不一定能从强大的编辑器帮助中受益——或者你需要付出更多努力才能实现这一点。

  5. 如果您仍然希望从您的库中输出 Web 组件,您仍然可以调整您的构建以实现此目的。


2. 如何实现 Angular UI 套件/NG UI 库

2.1. 预备设置

我想很明显,您需要提前@angular/cli安装并做好准备。

除此之外:首先你需要测试你的库,以确认你的工作做得不错。因此,在创建实际的库之前,我们需要先创建一个使用该库的应用程序。

ng new my-app --prefix=ma --style=scss --legacyBrowsers=true 
Enter fullscreen mode Exit fullscreen mode

我们使用这种方法prefix是为了安全起见,并将其与任何使用的库(可能没有前缀的库)隔离。如果您不打算支持 IE,
则不需要这种方法。legacyBrowsers

2.2 创建库

my-app在您的文件所在的目录中(也就是在其父目录中./my-app/../运行以下命令。

ng new design-system --create-application=false --prefix=ds --style=scss
Enter fullscreen mode Exit fullscreen mode

重要提示:在我用于此项目的 Angular 10.1.4 版本中,该设置--style=scss没有任何效果。这是因为我们创建了一个空应用程序,而 Angular 目前还没有预定义该设置的选项(我们将在本教程中尽快修复此问题)。

我们之所以称之为父模块,design-system是为了避免与下面的子模块混淆。
现在您的目录结构应该如下所示:

.
├── my-app
├── design-system
Enter fullscreen mode Exit fullscreen mode

现在,我们将在目录下design-system创建包含组件的实际库:

ng generate library pattern-lib --prefix=pl
Enter fullscreen mode Exit fullscreen mode

首先,你需要将包的作用域限定为你的库,以便更容易识别它。

所以去fromdesign-system/projects/pattern-lib/package.json改成namepattern-lib@your-company/pattern-lib

如上所述:--style=scss由于目前无法预先定义 alibrary或空值,我们需要自行解决此application问题。请执行以下操作:

  1. 前往design-system/angular.json
  2. 在以下位置搜索您添加的图书馆:"projects": { ...}
  3. 将该schematics属性添加到您的projects/pattern-lib

我的项目中,它看起来大概是这样的:

...
"projects": {
    "pattern-lib": {
      "projectType": "library",
      "schematics": {
        "@schematics/angular:component": {
          "style": "scss"
        }
      },
...
Enter fullscreen mode Exit fullscreen mode

2.3.button在库中创建组件

ng generate component button --project=pattern-lib
Enter fullscreen mode Exit fullscreen mode

此命令将创建一个名为 . 的组件button。您的目录结构应包含以下目录:

.
├── my-app
├── design-system
   ├── projects
      ├── pattern- lib
         ├── src
            ├── lib
               ├── button
Enter fullscreen mode Exit fullscreen mode

你可能会问:嘿,David,为什么这么多子文件夹?我不能直接把它放在根目录的 src 文件夹里吗?

答案:Angular 允许在一个 Angular 项目根目录中控制多个库/组件/模块等,并且它@angular/cli针对这种结构进行了优化。如果我们在一个默认ng new my-pattern-lib应用程序中构建一个模式库,这当然可行,但我们实际上是从安全之路走向了一条充满风险的道路,因为那样我们就不得不对工具进行一些修改才能实现我们想要的功能(公开和发布库的各个部分)。


打开button.component.ts并确保类中定义了两个属性,如下所示:

import { Component, OnInit, Input } from '@angular/core';

@Component({
  selector: 'pl-button', // pl is our prefix
  templateUrl: './button.component.html',
  styleUrls: ['./button.component.css']
})
export class ButtonComponent implements OnInit {
  @Input('label') label: string | null;
  @Input('pink') pink: boolean;

  constructor() { }

  ngOnInit(): void {
  }

}

Enter fullscreen mode Exit fullscreen mode

简要说明:通过显式设置属性(例如@Input('pink') pink: boolean;),组件会公开其可编辑字段,这在 Storybook 中稍后会非常有用,因为 Storybook 可以自动检测字段类型。


现在打开button.component.html并装入:

<button [attr.is-pink]="pink" [ngClass]="{'make-pink': pink}">{{label ? "😎 " + label : "No Label provided 🧐"}}</button>
Enter fullscreen mode Exit fullscreen mode

我们[attr.is-pink]="pink"用它来调试遇到的错误。不过,它并非必需品 😉

打开button.component.scss并装入:

button {
  background: blue;
  padding: 1rem 2rem;
  border-radius: 3px;
  appearance: none;
  border: 0;
  -webkit-appearance: none;
  -moz-appearance: none;
  font-size: 1.5rem;
  letter-spacing: 1px;
  color: white;
  box-shadow: 0 4px 10px rgba(55, 55, 55, 0.3),
    0 6px 35px rgba(55, 55, 200, 0.7);
  cursor: pointer;

  &.make-pink {
    background: #ff00a2;
    box-shadow: 0 4px 10px rgba(55, 55, 55, 0.3), 0 6px 35px rgb(200 55 150 / 70%);
  }
}
Enter fullscreen mode Exit fullscreen mode

现在你想构建并查看按钮的实际效果——但它只是一个库,而不是一个单页应用程序 (SPA),那么该怎么做呢?

2.4. 运行它:故事书来帮忙。

通常情况下,添加 Storybook 非常简单,因为有很多现成的示例可以立即使用。但是,这些示例并不适用于 Angular 库。

2.4.1. 安装 Storybook 并将其绑定到我们的图书馆

  1. npx -p @storybook/cli sb init --type angular在您的design-systemAngular 项目中运行。

  2. 打开.storybook/tsconfig.json并修改扩展行"extends": "../projects/pattern-lib/tsconfig.lib.json"(如果尚未设置)

2.4.2. 设置我们的按钮故事

  1. design-system/stories删除所有内容
  2. 创建一个名为 `<filename>` 的文件Intro.stories.mdx,内容如下:
import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="Library Example/Intro" />

<style>{`
  .box {
    margin: 2rem 0;
    padding: 2rem;
    background: rgba(233,233,233,0.7);
    border-radius: 3px;
  }
`}</style>

# Hello you!

<div class="box">
  This is the Pattern Library Intro File to provide a guiding path or whatever else you want to do here. Feel free!
</div>

![](https://media.giphy.com/media/yJFeycRK2DB4c/giphy.gif)
Enter fullscreen mode Exit fullscreen mode
  1. 创建一个名为 `<filename>` 的文件Button.stories.mdx,内容如下:
import { Story, Meta } from '@storybook/angular/types-6-0';
import { ButtonComponent } from '../projects/pattern-lib/src/lib/button/button.component';

export default {
  title: 'Atomics/Buttons',
  component: ButtonComponent,
  argTypes: {
    label: { control: 'text' }, 
    // we need to override here since in Angular it could be null as well (see button.component.ts) and therefore it would become an ambigious data type for storybook
  }
} as Meta;

const Template: Story<ButtonComponent> = (args: ButtonComponent) => ({
  component: ButtonComponent,
  props: args,
});

export const FancyBlueButton = Template.bind({});
FancyBlueButton.args = {
  label: 'Button',
};

export const FancyPinkButton = Template.bind({});
FancyPinkButton.args = {
  label: 'Pink version',
  pink: true,
};
Enter fullscreen mode Exit fullscreen mode

2.4.3. 快跑,快跑,快跑!

npm run storybook在项目文件夹中触发design-system并等待它(希望能够成功)启动。

因此,您应该会看到类似这样的内容:

故事书

好东西!


快速总结:到目前为止,我们创建了一个 Angular 库,其中包含一个按钮组件,并将 Storybook 连接到它,以便在不运行实际的 Angular SPA 的情况下查看和构建它。

2.5. 全局 SCSS 和组件组成

2.5.1. 全局 SCSS

  1. 前往design-system/projects/pattern-lib/src/
  2. 创建目录styles/
  3. 创建一个文件名global.scss包含一些随机内容的文件,例如body { background: red }

在故事书中使用它

为了确保 Storybook 可以应用全局 scss,请前往.storybook/preview.js并将其作为 webpack 导入添加,如下所示:

import '!style-loader!css-loader!sass-loader!./../projects/pattern-lib/src/styles/global.scss';
Enter fullscreen mode Exit fullscreen mode

就这样。Storybook 现在会始终使用该文件作为所有视图的全局文件。

在 Angular 中使用它

在 Angular 中使用全局 SCSS 应该不会有问题,因为你可以@import在项目中的任何 SCSS 文件中使用它,例如:

@import '../../styles/global';
Enter fullscreen mode Exit fullscreen mode

但是 >>警告:请勿将实际的 CSS 定义(例如 <style>)导入body { background: red }到您的 Angular 组件中,因为这些定义会被重复,从而大幅降低性能。

如果你想为你的 Angular 应用创建一个全局 CSS 文件,你当然可以在你的库中定义它(而且应该这样做),但你应该使用 npm 包导出它(参见第 2.6 节),以便你的 Angular 单页应用可以包含它。

在任何 Angular 应用程序(不等同于 Angular 库)中,您都可以添加以下代码来angular.json定义应用程序的全局样式:

"styles": [
  "node_modules/@your-company/pattern-lib/styles/style.scss"
],
Enter fullscreen mode Exit fullscreen mode

2.5.2. 组件组成

如果您想在 Storybook 中组合多个组件,请参阅https://www.learnstorybook.com/intro-to-storybook/angular/en/composite-component/ 。

pattern-lib2.6.在 Angular 应用中使用my-app

现在我们希望能够在我们创建的任何应用程序中使用我们的模式——以 npm 包的形式。

下图形象地展示了这一点:

等等!重要!

在使用我们的库之前,我们需要从中导入CommonModule@angular/common否则我们[ngClass]的按钮绑定将无法工作(更多信息请参见:https: //stackoverflow.com/a/45573086)。

所以,design-system/pattern-lib打开pattern-lib.module.ts它并进行调整,使其符合以下条件:

//... other imports
import { CommonModule } from '@angular/common';

@NgModule({
  ...
  imports: [
    CommonModule
  ],
  ...
})
Enter fullscreen mode Exit fullscreen mode

2.6.1. 导出按钮以供使用

如果我们希望按钮能在库之外使用,我们需要pattern-lib.module.ts像这样将其导出:

//.. other imports
import { ButtonComponent } from './button/button.component';


@NgModule({
  ...
  exports: [PatternLibComponent, ButtonComponent]
})

Enter fullscreen mode Exit fullscreen mode

2.6.2. 通过本地绑定使用库包

进入目录design-system并运行

ng build --project=pattern-lib
Enter fullscreen mode Exit fullscreen mode

前往design-system/dist/pattern-lib并运行npm link

现在,您的软件包应该可以在您的计算机上以软件包名称找到@your-company/pattern-lib

现在,打开你的 Angular 单页应用my-app并运行它npm link @your-company/pattern-lib。这基本上相当于一个本地应用npm install,但它不会出现在你的 package.json 文件中。或者,npm install ../design-system/dist/pattern-lib如果你愿意,也可以这样做。

将链接库添加到我们的my-app

  1. 转到my-app/src/app/app.module.ts并添加库导入
import { PatternLibModule } from '@your-company/pattern-lib';
Enter fullscreen mode Exit fullscreen mode
  1. 同时将其添加到imports以下字段app.module.ts
@NgModule({
  ...
  imports: [
    ...
    PatternLibModule,
  ],
  ...
})
Enter fullscreen mode Exit fullscreen mode
  1. 请前往my-app/src/app/app.component.html并删除所有内容,然后替换为以下内容:
<style>
  :host {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
    font-size: 14px;
    color: #333;
    box-sizing: border-box;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }
</style>


<div>
  <p>Hello I am the Angular SPA and following a button should be shown and it should be blue: </p>
  <dvpl-button label="hello"></dvpl-button>

  <p>And this one should be pink: </p>
  <dvpl-button label="Hello pink?" [pink]="true"></dvpl-button>
</div>

<!-- you only need this if you added a router: -->
<router-outlet></router-outlet>
Enter fullscreen mode Exit fullscreen mode

现在你应该可以npm start在你的my-appSPA 中运行,并且你应该看到你的 Angular 应用程序正确地使用了我们的库。

运行应用程序

2.6.3. 将软件包与npm注册表一起使用

既然它已经在本地运行了,这应该很容易做到。

  1. 首先,选择一个 npm 仓库(例如 Github 或 npmjs)。

  2. .npmrc在Angular 根目录下创建一个design-system类似于以下内容的文件:

registry=https://registry.npmjs.org/
always-auth=true
email=YOUR_EMAIL_GOES_HERE
Enter fullscreen mode Exit fullscreen mode
  1. 使用以下命令登录npm login:它应该会要求您输入文件中提供的注册表凭据.npmrc

  2. 登录后,您只需npm publish在当前目录下运行design-system/dist/pattern-lib命令即可发布您构建的库!请务必不要在根目录下运行,而应在 `<path>` 目录下运行dist!因为如果您npm publish在根目录下运行,它只会发布所有源代码,而不会发布编译后的库。

  3. 现在你可以像在本地安装一样使用它,但这次是从指定的仓库安装npm install @your-company/pattern-lib,🎉

3. 测试 Storybook、原子导入和 Web 组件等。

还有很多可以讨论的内容——但我不想在这里过多赘述:如何不仅测试组件,还要使用 Storybook 进行测试,如何更原子地导出(如果需要,只需导入一个按钮而不是整个库),以及如何将其作为 Web 组件发布到其他框架。

但这或许值得另写一篇博文。请告诉我你的想法!

4. 结论

您使用 Angular 创建了一个设计系统仓库,并且可以发布不同版本。
此外,您还可以在任何您想要的 Angular 应用中使用它们 😎。

请在此处查找示例的源代码:

GitHub 标志 activenode / angular-10-storybook-library

一个用于实现独立设计系统的 Angular 库







资料来源:

设计参考:
包装设计图标由xnimrodx制作,来自 flaticon.com

文章来源:https://dev.to/activenode/angular-10-storybook-npm-package-ng-design-system-step-by-step-2dn2