Bun生态下无反射依赖注入实践:dunx库如何解决TypeScript DI痛点
如果你最近在 Bun 生态里做后端开发尤其是从 NestJS 这类框架转过来可能会遇到一个不大不小的“水土不服”问题依赖注入DI用起来没那么顺手了。Bun 很快TypeScript 支持也很好但当你习惯了 NestJS 那种通过装饰器声明依赖、框架自动帮你组装和管理的丝滑体验后回到手动new或者自己写工厂函数总觉得少了点什么。特别是reflect-metadata这个在 Node.js 里配合装饰器做类型反射的库在 Bun 环境下有时会带来一些额外的复杂性和不确定性。最近社区里出现了一个叫dunx的项目它瞄准的就是这个痛点。它的目标很明确在 Bun 运行时里提供一套类似 NestJS 风格的依赖注入体验但不需要依赖reflect-metadata。这听起来像是一个“轮子”但如果你仔细看会发现它解决的不仅仅是一个功能有无的问题更是在 Bun 这个新兴、高性能的运行时里如何平衡开发体验、类型安全和运行时效率的一次具体实践。1. 为什么在 Bun 里做“无反射”的 DI 是个值得关注的问题依赖注入本身不是什么新概念它核心解决的是对象创建和依赖关系的解耦。在大型应用里手动管理成千上万个类的实例化及其依赖很快就会变成一场灾难。像 NestJS 这样的框架通过装饰器如Injectable()、Inject()和 IoC 容器把这种管理自动化了开发者只需要声明“我需要什么”框架负责“如何给你”。这套机制在 Node.js 世界运行良好的一个关键技术支撑是reflect-metadata。这是一个 polyfill为 JavaScript 的装饰器提案提供了读取和写入类型元数据的能力。当你在 NestJS 里写constructor(private service: MyService)时TypeScript 编译器配合emitDecoratorMetadata选项会在编译后的代码里留下类型信息的“痕迹”元数据。reflect-metadata库能在运行时读取这些痕迹从而让 IoC 容器知道MyService这个参数需要注入什么。然而把这一套直接搬到 Bun 里会遇到几个坎运行时差异Bun 是一个全新的 JavaScript 运行时虽然兼容 Node.js API但其底层实现和某些边界行为可能与 Node.js 不同。reflect-metadata作为一个深度依赖 ES 规范和 V8 引擎特定行为的库在 Bun 里可能表现不稳定或者带来不可预知的性能开销和兼容性问题。元数据开销即使能运行reflect-metadata带来的元数据反射本身就有运行时成本。对于追求极致启动速度和执行效率的 Bun 应用来说这可能与初衷相悖。构建与部署复杂性它要求 TypeScript 配置中开启emitDecoratorMetadata这可能会影响编译产物体积和树摇Tree-shaking效果。在一些部署或打包场景下需要额外处理。所以dunx提出的“无reflect-metadata”路径本质上是在问如果我们放弃运行时读取类型元数据这个“黑魔法”还能不能实现同样便捷、且类型安全的依赖注入这迫使解决方案回归到更本质的 JavaScript/TypeScript 特性上。2. dunx 是如何在编译期解决依赖关系的dunx的核心思路可以概括为将运行时反射转变为编译期推导和显式声明。它不再依赖运行时去“猜”类型而是在你编写代码时就通过特定的方式让依赖关系变得明确并由工具在编译阶段进行处理。具体来说它主要依靠两种机制2.1 基于装饰器的显式令牌Token声明这是最接近 NestJS 体验的部分。你依然使用装饰器来标记可注入的类和需要注入的点但依赖关系的标识不再是隐式的类型而是一个显式的“令牌”Token。// 传统 NestJS依赖 reflect-metadata Injectable() export class CatsService { constructor(private repository: CatsRepository) {} // 容器通过 CatsRepository 类型查找 } // 使用 dunx 的风格 import { Injectable, Inject } from dunx; const CATS_REPO_TOKEN Symbol(CatsRepo); Injectable() export class CatsRepository { // ... } Injectable() export class CatsService { constructor( Inject(CATS_REPO_TOKEN) private repository: CatsRepository // 通过令牌注入 ) {} }在上面的dunx示例中CatsRepository类本身被Injectable()装饰但注入时使用的是Inject(CATS_REPO_TOKEN)。这意味着容器不再需要知道repository参数的类型是CatsRepository它只需要根据CATS_REPO_TOKEN这个令牌来提供对应的实例。这个令牌可以是一个Symbol、一个字符串或者一个类本身虽然不推荐用类作令牌以避免循环依赖。为什么这么做去除了类型反射依赖IoC 容器的工作简化为“令牌 - 实例”的映射这是一个纯 JavaScript 对象就能完成的任务完全不需要reflect-metadata。更灵活你可以用同一个令牌注入不同的实现便于测试和配置。依赖关系与具体类名解耦。编译期友好令牌是明确的标识符对打包工具更友好。2.2 利用 TypeScript 的编译期类型与模块系统dunx的另一个关键在于它充分利用了 TypeScript 在编译期的类型检查能力。虽然运行时没有类型信息但在你编写代码的 IDE 里TypeScript 编译器完全知道Inject(CATS_REPO_TOKEN)后面的repository参数你期望是什么类型CatsRepository。dunx的 API 设计会确保你提供的令牌Token与装饰器所装饰的参数类型是匹配的。这种匹配是在编译时由 TypeScript 检查的而不是在运行时发现的。如果令牌对应的提供者Provider返回的类型与参数声明的类型不匹配你会在编码阶段就收到红色波浪线提示。// 假设我们错误地提供了一个错误的实现 Container.provide(CATS_REPO_TOKEN, { value: new WrongRepository() }); // TypeScript 编译错误 // 正确的提供方式 Container.provide(CATS_REPO_TOKEN, { useClass: CatsRepository }); // 类型匹配通过这种设计实现了“编译期类型安全运行时无类型开销”的效果。你获得了优秀的开发体验IDE 自动补全、类型错误提示而运行时容器只处理简单的令牌解析非常高效。3. 从概念到实践搭建一个基础的 dunx 应用理解了原理我们来看看如何实际使用dunx。假设我们要构建一个简单的用户服务它依赖一个用户仓库。3.1 定义令牌与可注入类首先我们定义应用所需的令牌。将令牌集中管理是一个好习惯。// src/tokens.ts export const USER_REPOSITORY_TOKEN Symbol(UserRepository); export const EMAIL_SERVICE_TOKEN Symbol(EmailService);接着创建我们的可注入类。使用Injectable()装饰器标记它们。// src/repositories/user.repository.ts import { Injectable } from dunx; export interface IUserRepository { findById(id: string): PromiseUser | null; save(user: User): Promisevoid; } Injectable() // 标记此类可被容器管理 export class UserRepository implements IUserRepository { private db new Mapstring, User(); // 简单模拟 async findById(id: string): PromiseUser | null { return this.db.get(id) || null; } async save(user: User): Promisevoid { this.db.set(user.id, user); } }// src/services/email.service.ts import { Injectable } from dunx; export interface IEmailService { sendWelcomeEmail(to: string): Promisevoid; } Injectable() export class EmailService implements IEmailService { async sendWelcomeEmail(to: string): Promisevoid { console.log([模拟] 发送欢迎邮件至: ${to}); // 实际调用邮件发送 API } }3.2 在服务中注入依赖现在创建我们的核心业务服务UserService。它需要UserRepository和EmailService。// src/services/user.service.ts import { Injectable, Inject } from dunx; import { USER_REPOSITORY_TOKEN, EMAIL_SERVICE_TOKEN } from ../tokens; import type { IUserRepository } from ../repositories/user.repository; import type { IEmailService } from ./email.service; Injectable() export class UserService { constructor( Inject(USER_REPOSITORY_TOKEN) private readonly userRepo: IUserRepository, Inject(EMAIL_SERVICE_TOKEN) private readonly emailService: IEmailService ) {} async registerUser(name: string, email: string): Promisestring { const user: User { id: crypto.randomUUID(), name, email }; await this.userRepo.save(user); await this.emailService.sendWelcomeEmail(email); return user.id; } }注意这里注入的是接口类型IUserRepository和IEmailService而不是具体的类。这进一步实现了依赖倒置使得UserService只依赖于抽象不依赖于具体实现极大地提高了可测试性和可维护性。3.3 配置与组装容器最后我们需要创建一个 IoC 容器并将令牌与具体的实现类关联起来。这通常在应用入口点如src/main.ts或一个专门的src/container.ts完成。// src/container.ts import { Container } from dunx; import { USER_REPOSITORY_TOKEN, EMAIL_SERVICE_TOKEN } from ./tokens; import { UserRepository } from ./repositories/user.repository; import { EmailService } from ./services/email.service; import { UserService } from ./services/user.service; // 配置提供者Providers Container.provide(USER_REPOSITORY_TOKEN, { useClass: UserRepository, // 当需要 USER_REPOSITORY_TOKEN 时实例化 UserRepository }); Container.provide(EMAIL_SERVICE_TOKEN, { useClass: EmailService, }); // UserService 本身也是 Injectable()容器可以自动解析其依赖并创建它 // 但通常我们也会显式提供它或者通过 Container.resolve() 获取 Container.provide(UserService, { useClass: UserService, }); export { Container };3.4 在应用中使用现在在你的应用代码中你可以从容器中获取完全组装好的UserService实例。// src/main.ts import { Container } from ./container; async function main() { // 从容器解析 UserService。 // 容器会自动处理其构造函数中声明的所有依赖Inject(...) // 按图索骥地创建 UserRepository 和 EmailService 实例并注入进去。 const userService Container.resolve(UserService); const userId await userService.registerUser(张三, zhangsanexample.com); console.log(用户注册成功ID: ${userId}); } main().catch(console.error);整个过程清晰明了声明用Injectable()和Inject(Token)声明依赖关系。配置在容器启动阶段用Container.provide将令牌映射到具体的实现类、值、工厂函数。获取通过Container.resolve获取任意已注册的依赖容器负责完成所有依赖图的创建和注入。4. 进阶使用与工程化考量把基础流程跑通只是第一步。当你想把dunx用于更严肃的项目时有几个工程化的点需要仔细考虑。4.1 依赖的生命周期管理简单的useClass每次resolve都会创建一个新实例瞬态。但在 Web 服务器中很多服务如数据库连接池、配置服务应该是单例的。dunx通常支持通过配置指定生命周期。Container.provide(DATABASE_CONN_TOKEN, { useClass: DatabaseConnection, lifecycle: singleton, // 指定为单例 }); Container.provide(REQUEST_SCOPED_SERVICE_TOKEN, { useClass: RequestContextService, lifecycle: scoped, // 例如每个 HTTP 请求一个实例 });关键点理解并正确设置生命周期是避免内存泄漏和状态污染的关键。数据库连接、配置服务适合单例包含用户请求状态的服务适合请求作用域。4.2 循环依赖的处理循环依赖A 依赖 BB 也依赖 A是 DI 系统中的经典难题。reflect-metadata方案有时能通过延迟解析糊弄过去但本质是糟糕的设计。dunx的显式令牌模式其实更早地暴露了循环依赖问题因为依赖关系在编译时通过令牌声明就很清晰。最好的实践是重构代码打破循环例如引入第三个服务或使用面向接口编程将依赖改为对抽象层的依赖。如果确实无法避免一些 DI 容器提供了forwardRef之类的机制但应视为最后手段。4.3 与 Bun 的 Web 框架集成dunx本身只是一个 DI 容器。要把它用在 Bun 的 Web 框架如 Elysia、Hono里需要做一层“桥接”。这通常意味着创建根容器在应用启动时创建并配置好所有全局单例数据库、配置、核心业务服务。请求作用域容器为每个 HTTP 请求创建一个子容器或作用域用于存放请求相关的服务如用户会话、请求 ID。请求结束时清理该作用域。控制器/路由注入你需要一个机制将容器解析出的服务实例注入到框架的路由处理器中。这可能需要编写自定义的装饰器或利用框架的插件、上下文Context系统。例如在 Elysia 中你可以通过插件将容器实例挂载到Context上然后在路由处理函数中通过Context来获取服务。// 伪代码展示思路 import { Elysia } from elysia; import { Container } from ./container; const app new Elysia() .decorate(container, Container) // 将容器注入 Elysia 上下文 .get(/user/:id, async ({ params, container }) { const userService container.resolve(UserService); const user await userService.getUser(params.id); return user; });核心挑战如何优雅地实现请求作用域的生命周期管理以及如何让路由处理函数方便地获取依赖。这部分的集成代码往往是项目初期需要投入精力设计和封装的地方。4.4 测试变得极其简单DI 的最大优势之一就是便于测试。使用dunx后你可以轻松地为UserService编写单元测试。// tests/user.service.test.ts import { Container } from dunx; import { UserService } from ../src/services/user.service; import { USER_REPOSITORY_TOKEN, EMAIL_SERVICE_TOKEN } from ../src/tokens; // 模拟依赖 const mockUserRepo { findById: jest.fn(), save: jest.fn(), }; const mockEmailService { sendWelcomeEmail: jest.fn(), }; describe(UserService, () { let userService: UserService; beforeEach(() { // 在每个测试前创建一个新的容器作用域并注入模拟对象 const testContainer Container.createChildContainer(); testContainer.provide(USER_REPOSITORY_TOKEN, { useValue: mockUserRepo }); testContainer.provide(EMAIL_SERVICE_TOKEN, { useValue: mockEmailService }); userService testContainer.resolve(UserService); jest.clearAllMocks(); }); it(注册用户时应保存用户并发送邮件, async () { mockUserRepo.save.mockResolvedValue(undefined); mockEmailService.sendWelcomeEmail.mockResolvedValue(undefined); await userService.registerUser(Test, testexample.com); expect(mockUserRepo.save).toHaveBeenCalledTimes(1); expect(mockEmailService.sendWelcomeEmail).toHaveBeenCalledWith(testexample.com); }); });通过创建子容器 (createChildContainer) 并覆盖真实依赖为模拟对象 (useValue)你可以完全隔离被测服务进行纯净的单元测试。这种可测试性是手动管理依赖或使用全局状态难以比拟的。5. 总结dunx 带来了什么又要求我们改变什么dunx的出现为 Bun 生态的开发者提供了一种不依赖reflect-metadata的、类型友好的依赖注入选择。它不是一个要取代 NestJS 的巨型框架而是一个专注解决 DI 问题的库。它带来的价值是清晰的运行时纯净去除对reflect-metadata的依赖减少潜在兼容性问题和性能开销更契合 Bun 的轻量、高效哲学。类型安全充分利用 TypeScript 编译期检查在编码阶段就捕获依赖不匹配的错误同时保持运行时代码的简洁。模式熟悉对于从 NestJS 过来的开发者基于装饰器和令牌的模式学习成本很低。测试友好显式的依赖声明和容器配置让单元测试的模拟和替换变得非常直接。同时它也要求开发者做出一些思维和习惯上的调整从“隐式”到“显式”你需要主动定义和管理令牌而不是完全依赖类型。这增加了少许前期工作但换来了更清晰的依赖契约和灵活性。理解生命周期你需要明确每个服务应该是单例、瞬态还是作用域实例并在容器中正确配置。自行处理框架集成dunx只提供核心 DI 能力与 Web 框架Elysia, Hono 等的深度集成需要你自己或社区来构建中间层。所以是否选择dunx可以基于以下判断如果你的 Bun 项目复杂度逐渐上升手动管理依赖开始成为负担。你重视代码的可测试性和模块化但不想引入像 NestJS 那样重量级的、带有固定范式的框架。你希望避免reflect-metadata可能带来的环境问题。你愿意接受显式声明依赖的范式并自己处理与 Web 框架的集成。那么dunx是一个值得你深入研究和尝试的工具。它代表了一种思路在追求性能与简洁的 Bun 运行时中我们依然可以通过精巧的设计获得高级别的开发体验和代码质量而不必在便利性和控制力之间做单选题。从手动new到自动化 DI这一步跨越带来的长期维护收益在项目规模增长后会愈发明显。

相关新闻

ComfyUI AI视频生成:从零搭建AnimateDiff工作流与避坑指南

ComfyUI AI视频生成:从零搭建AnimateDiff工作流与避坑指南

大家好,我是专注于AI绘画与视频生成领域的技术博主。最近在B站上看到很多同学对ComfyUI的视频生成功能非常感兴趣,但苦于资料零散、环境配置复杂、工作流难以理解。很多新手在尝试时,常常卡在“请安装缺失的包以使用此工作流”这类报错上&…

2026/8/21 5:40:01 阅读更多 →
智能体驱动的文本引导3D编辑:原理、架构与实现挑战

智能体驱动的文本引导3D编辑:原理、架构与实现挑战

1. 项目概述:当3D编辑遇上“智能体”最近在AIGC圈子里,一个词的热度居高不下:Agentic。它不再是传统意义上那个冷冰冰的“代理”,而是被赋予了自主规划、决策和执行能力的“智能体”。这股风潮正从文本、图像生成领域,…

2026/8/21 5:40:01 阅读更多 →
LT8714电源芯片实现SPWM正弦波输出的LTspice仿真全解析

LT8714电源芯片实现SPWM正弦波输出的LTspice仿真全解析

1. 从电源到正弦波:LT8714电路的核心价值与仿真起点如果你手头有一个四象限电源芯片LT8714,想用它直接产生高质量的正弦波SPWM信号,这个想法本身就很值得一试。LT8714本身是一个灵活的电源控制器,常用于升降压或极性反转&#xff…

2026/8/21 5:40:01 阅读更多 →

最新新闻

MA-VLCM:多模态融合如何革新多智能体策略价值评估

MA-VLCM:多模态融合如何革新多智能体策略价值评估

1. 从单智能体到多智能体:价值评估的范式转变在强化学习领域,评估一个策略的好坏,或者说预测一个状态或状态-动作对的长期回报,是核心任务之一。传统的价值函数,无论是状态价值函数V(s)还是动作价值函数Q(s, a)&#x…

2026/8/21 9:03:49 阅读更多 →
网络安全实战:漏洞扫描器对比——Nessus、OpenVAS、Nuclei 实战评测

网络安全实战:漏洞扫描器对比——Nessus、OpenVAS、Nuclei 实战评测

前言:在自动化的浪潮中寻找那把“尺子” 在渗透测试的项目周期里,有一个环节既让人爱,又让人恨,那就是“漏洞扫描”。爱它,是因为它确实能像收割机一样,快速收割掉那些低垂的果实——那些未打补丁的系统、弱…

2026/8/21 9:03:49 阅读更多 →
冒泡排序算法深度解析:从基础实现到优化策略与面试实战

冒泡排序算法深度解析:从基础实现到优化策略与面试实战

1. 项目概述:为什么我们还在聊冒泡排序?在算法面试和日常的编程基础讨论里,冒泡排序(Bubble Sort)大概是那个最常被提起,也最容易被“轻视”的算法。很多刚入门的朋友会觉得:“这不就是个两层循…

2026/8/21 9:03:49 阅读更多 →
开源Winapp2.ini规则库:打造精准免费的Windows系统清理方案

开源Winapp2.ini规则库:打造精准免费的Windows系统清理方案

在 Windows 系统长期使用后,系统盘空间被各种临时文件、缓存和软件残留占用是开发者和管理员经常遇到的痛点。手动清理不仅效率低下,而且容易误删重要文件。虽然市面上有 CCleaner 等知名工具,但其商业版本需要付费,且部分高级功能…

2026/8/21 9:03:49 阅读更多 →
ORB-SLAM3 MLPnPsolver::Refine()

ORB-SLAM3 MLPnPsolver::Refine()

下面是 MLPnPsolver::Refine() 函数的逐行注释,以及背后数学原理与公式说明。 首先理解函数的作用:在RANSAC过程中,当找到一个比较好的模型(内点数超过历史最佳),会用所有内点重新估计一次位姿,以得到更精确的解。这个过程通常叫做“局部优化”或“Refine”。这里用的是…

2026/8/21 9:03:49 阅读更多 →
独立游戏开发中AI工具合规应用与风险规避指南

独立游戏开发中AI工具合规应用与风险规避指南

最近和几个做独立游戏的朋友聊天,发现一个挺有意思的现象:大家聊起AI工具时,态度变得比以前复杂多了。以前是“哪个AI画图强?”“哪个AI写代码快?”,现在更多是“这个AI生成的内容,平台审核能过…

2026/8/21 9:02:49 阅读更多 →

日新闻

机场边检旅客定位系统国产化白皮书:算法、硬件、底座平台全程自主

机场边检旅客定位系统国产化白皮书:算法、硬件、底座平台全程自主

前言随着国家数字基础设施信创替代、关键技术自主可控战略持续深化,口岸智慧安防、边检智能管控领域正全面进入国产化、自主化、安全可控升级周期。当前国内机场边检旅客识别与定位体系长期依赖国外商用视觉算法、进口成像硬件、闭源通用计算平台,存在核…

2026/8/21 0:00:42 阅读更多 →
别再把“数字孪生”当空间智能了!镜像视界揭开四维时空的真正面纱

别再把“数字孪生”当空间智能了!镜像视界揭开四维时空的真正面纱

别再把“数字孪生”当空间智能了!镜像视界揭开四维时空的真正面纱当下数字化建设浪潮中,很多项目将三维可视化、视频贴图叠加的数字孪生等同于空间智能。传统数字孪生更多停留在三维场景复刻,擅长把物理世界“画出来、展示出来”,…

2026/8/21 0:00:42 阅读更多 →
105、车载温度范围-40°C到85°C的影像质量一致性——ISP参数温漂补偿与产线标定策略

105、车载温度范围-40°C到85°C的影像质量一致性——ISP参数温漂补偿与产线标定策略

105、车载温度范围-40C到85C的影像质量一致性——ISP参数温漂补偿与产线标定策略 去年冬天在北方某车厂做A样评审,凌晨四点的黑河试验场,零下三十三度。客户拿了一台冷启动的车,中控屏上倒车影像全是雪花噪点,暗部细节直接糊成一片。我第一反应是sensor温度没上来,暗电流…

2026/8/21 0:00:42 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/21 3:21:33 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/21 0:02:09 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

2026/8/21 6:07:56 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/20 6:11:08 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/20 21:46:49 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/21 0:14:22 阅读更多 →