后端讲完,转到前端。JHipster 9 的 Angular 21 主线已经全面 Zoneless + signals 化,生成的代码风格与三年前的 RxJS 时代差别很大。这一篇的目标不是讲 Angular 语法,而是给一张”生成的代码怎么读”的地图——新人拿到项目第一天该看哪些文件、按什么顺序看、每一层的惯例是什么。我们组用这张图把前端新人的一周适应期压到了两天。
前端工程全景:目录导览表
生成的 src/main/webapp/app/ 结构:
| 目录 | 内容 | 改动频率 |
|---|---|---|
entities/ | 业务实体的列表/详情/编辑/删除组件,按实体分目录 | 高(业务主战场) |
admin/ | 运营管理:用户管理、指标、健康检查、日志、审计、配置 | 低 |
core/ | 基础设施:认证、拦截器、错误处理、事件总线 | 低 |
layout/ | 页面骨架:navbar、sidebar、footer、主容器 | 中(品牌化) |
shared/ | 共享组件、管道、指令、模型 | 中 |
config/ | 路由总装、权限常量、动画 | 低 |
home/ | 首页 | 中 |
login/ | 登录相关组件 | 低 |
app.component.ts | 根组件 | 基本不动 |
两个工程级文件值得先看:proxy.conf.js(dev 时代理到后端 8080,解决跨域)和 application.yml 里 jhipster.clientApp 相关配置。前者是本地联调的关键——新手最常见的”接口 404”就是把代理地址改坏了。
entities/:一个实体的标准四件套
以 Customer 为例:
entities/customer/
├── customer.model.ts // 领域模型(TS interface/class)
├── customer.service.ts // 数据服务(httpResource + signals)
├── customer.route.ts // 懒加载路由
├── customer.routes.ts? // 路由配置(新路由器风格)
├── list/customer.component.html / .ts
├── detail/customer-detail.component.html / .ts
├── update/customer-update.component.html / .ts
└── delete/customer-delete-dialog.component.*.ts
四个组件对应四种交互:列表(分页表格)、详情(只读卡片)、编辑(新建/修改复用)、删除确认(模态框)。路由是懒加载的:
export const customerRoute: Route = {
path: 'customer',
component: CustomerComponent,
data: {
authorities: ['ROLE_USER'],
pageTitle: 'myApp.customer.home.title',
},
canActivate: [userRouteAccessGuard],
};
authorities 与 canActivate 是权限的路由级入口,第 14 篇展开。pageTitle 指向 i18n key,全站文案零硬编码。
数据服务:httpResource 与信号
Angular 21 生成的 service 已经是”资源为中心”的写法,典型形态:
@Injectable({ providedIn: 'root' })
export class CustomerService {
protected resourceUrl = SERVER_API_URL + 'api/customers';
query(req?: Partial<QueryParams>): HttpResource<RestResponse<Customer[]>> {
return httpResource<RestResponse<Customer[]>>(
() => {
const params = new URLSearchParams();
if (req?.page !== undefined) params.set('page', String(req.page));
if (req?.sort) params.set('sort', req.sort);
return `${this.resourceUrl}?${params.toString()}`;
},
{ defaultValue: { body: [], headers: new HttpHeaders() } }
);
}
create(customer: Partial<Customer>): Observable<HttpResponse<Customer>> {
return this.http.post<HttpResponse<Customer>>(this.resourceUrl, customer);
}
}
与旧版 Observable 风格的本质区别:
| 维度 | 旧(RxJS 手动订阅) | 新(httpResource + signals) |
|---|---|---|
| 数据形态 | Observable,组件里 subscribe | Resource,.value() 读信号 |
| 依赖追踪 | 手动管理(isLoading、error 变量) | 内建 isLoading()、error() |
| URL 变化重取 | 手动 switchMap | URL 信号变化自动 refetch |
| Zoneless 契合 | 依赖 Zone 触发变更检测 | 信号驱动,无 Zone 也精准更新 |
读生成组件时的固定三问:数据来自哪个 resource?哪些信号驱动 URL?(分页/排序状态)用户操作改的是哪个信号?顺着这三问读,任何生成组件十分钟就能理清。
状态管理:信号优先,够用就不加库
JHipster 不引入 NgRx/NGXS 这类全局状态库,生成的状态策略分三层:
- 服务端状态:
httpResource直接映射 URL,缓存与重取交给 resource 机制; - 组件局部状态:
signal/computed,列表的排序、筛选、选中行都是组件内信号; - 跨组件共享状态:小型 service 持有 signal(比如登录用户信息、语言切换)。
@Injectable({ providedIn: 'root' })
export class AccountService {
private readonly _account = signal<Account | null>(null);
readonly account = this._account.asReadonly();
readonly isAuthenticated = computed(() => this._account() !== null);
readonly hasAuthority = (authority: string) =>
computed(() => this._account()?.authorities.includes(authority) ?? false);
}
什么时候才值得上 NgRx?我们的经验阈值:三个以上不相干模块需要响应同一份频繁变化的客户端状态(比如协同编辑、复杂购物车)。五年里我们只在报价编辑器一个模块引入过局部状态库,其余场景 signal service 全部够用。别为了简历上的关键词给项目加抽象税。
共享组件:生成器送的下水道系统
shared/ 里最常被忽视也最常被用到的东西:
- alert 组件:订阅全局事件总线,把后端错误/成功提示渲染成 alert 条——任何 Resource 层抛的错误经过这里统一展示;
- error 处理:
ErrorInterceptor把非 2xx 响应翻译成结构化 Error 对象(对应后端第 11 篇讲的错误体),组件里不需要手写错误解析; - item-count:分页条上的”第 1-20 条,共 137 条”文案组件;
- sort 指令/组件:表头排序的通用交互;
- 通用管道:日期、数值、字节格式化,绑定 i18n。
纪律:这些组件可以复用、不建议改造。想换 UI 风格时的正确姿势是第 15 篇的主题——用主题与包装层,不动生成文件。
admin 模块:白捡的运营后台
admin/ 是 JHipster 送的一整套管理界面,功能与后端 actuator 端点一一对应:
| 页面 | 路径 | 用途 |
|---|---|---|
| User management | /admin/user-management | 用户增删改查、激活/授权 |
| Metrics | /admin/metrics | JVM/HTTP/连接池实时指标 |
| Health | /admin/health | 各组件健康状态(db、mail、disk) |
| Configuration | /admin/configuration | Spring 环境属性查看 |
| Logs | /admin/logs | 运行时调整日志级别 |
| Audits | /admin/audits | 认证审计(谁登录/登出) |
| Tracker(微服务) | /admin/tracker | WebSocket 在线用户活动 |
全部默认 ROLE_ADMIN 保护。运营上线前建议把 Metrics 和 Health 页面给 SRE 同事开通权限——比教他们用 curl 调 actuator 便宜得多。
读代码路线图
给新人的两小时任务清单,按依赖方向从外到内:
app.routes.ts与app.component.ts:路由骨架与布局装配;layout/navbar/:全局导航怎么组织;core/auth/:登录态怎么存、拦截器怎么附加 token(配合第 14 篇精读);- 任选一个业务实体的四件套:list → service → model → update,走一遍数据流;
shared/alert/:错误提示链路;admin/metrics:感受基础设施能力边界。
读代码的顺序就是数据流动的顺序:URL → 路由守卫 → 组件 → service/resource → 后端 API。反过来(从后端 API 找前端调用方)适合改 bug 时用,IDE 的 find usages 比 grep 准。
小结
- 生成的前端结构与后端同构:entities 对应业务、admin 对应运维、core/shared 对应基础设施;
- Angular 21 生成代码已是 httpResource + signals 风格,读组件抓三问:resource 是谁、URL 由哪些信号驱动、操作改哪个信号;
- 状态管理三级火箭:resource 管服务端状态、signal 管局部状态、signal service 管共享状态,库是最后选项;
- 共享组件是下水道系统——用就好,别装修;
- admin 模块是白捡的运营后台,上线前记得给 SRE 开权限。
系列导航
- 上一篇:异步与消息——Kafka 集成
- 下一篇:从登录页读懂前端安全流
- 相关阅读:改造 UI——不动生成代码的优雅方式 · 表单与表格的工程化 · 测试全景——单元到端到端