Dify UI(dify-ui)公共 API 编写规范:子路径导出、命名约定与泛型契约

发布时间:2026/10/25 5:12:50

Dify UI(dify-ui)公共 API 编写规范:子路径导出、命名约定与泛型契约 Dify UI(dify-ui)公共 API 编写规范:子路径导出、命名约定与泛型契约【免费下载链接】difyBuild Agentic workflows, RAG pipelines, with rich AI model and tool support on one collaborative workspace. Deploy on cloud, VPC, or self-hosted, so teams move from prototype to production without rebuilding the stack.项目地址: https://gitcode.com/GitHub_Trending/di/dify本文以 Dify 仓库中 dify-ui 公共 API 编写指南 为核心,系统讲解langgenius/dify-ui包在什么算公共 API这一边界问题上的全部规则:每个src/primitive/index.tsx如何充当显式的公共 API 边界、package.json#exports子路径与组件命名的对应关系、泛型契约如何端到端保留,以及如何用本地类型测试保护这些契约。读完本文,你可以在该 UI 基元包中正确新增或修改一个组件,使它的导出面、类型与测试完全符合包的既有约定。包背景:dify-ui 是什么包 README 对langgenius/dify-ui的定义是:面向 Dify 产品的独立 UI 基元、设计令牌、CSS-first 的 Tailwind 样式,以及cn()工具。绝大多数交互型基元是围绕 Base UI(Headless 组件库)编写的薄而有主张的包装器;由 Dify 自研的基元则使用语义化 HTML、cva、cn与 Dify 设计令牌。有两个前提决定了公共 API这个概念在此包中格外重要:包是 workspace 私有的(private: true),但 README 明确声明其公共子路径被当作稳定的包边界对待;包故意不提供根 barrel(根入口桶导出),消费者只能通过公共子路径导入,例如:import { Button } from langgenius/dify-ui/button import { Dialog, DialogContent, DialogTrigger } from langgenius/dify-ui/dialog import { Field, FieldLabel } from langgenius/dify-ui/field import { Input } from langgenius/dify-ui/input import { cn } from langgenius/dify-ui/cn import langgenius/dify-ui/styles.css因此,每个子路径导出了什么、以什么名字导出,直接决定了下游web等应用包的可用面。AGENTS.md 进一步划定了包边界:不得从应用包导入,不得依赖路由、i18n、应用状态、schema、数据获取或业务 API;并约定了契约归属——导入、导出、命名、公共类型、泛型与 anatomy 的归属人正是本文档(Public API authoring)。一、index.tsx即显式的公共 API 边界编写指南的第一条规则:每个src/primitive/index.tsx都是一个显式的公共 API 边界。具体要求:实现细节保持模块私有(module-local);完整的公共面通过文件底部的独立清单式导出发布,即分别使用export { ... }(值导出)与export type { ... }(类型导出)两个清单;禁止散落的内联导出与清单式导出混用,禁止通配符导出(export *)。Checkbox 基元 是该规则的完整样板。它的文件主体定义了CheckboxRoot、CheckboxIndicator、Checkbox、CheckboxSkeleton四个组件,但文件顶部没有任何export关键字;全部公共面集中在文件末尾:export { Checkbox, CheckboxIndicator, CheckboxRoot, CheckboxSkeleton } export type { CheckboxIndicatorProps, CheckboxProps, CheckboxRootProps, CheckboxSkeletonProps }这样做的好处是:任何人在 15 秒内就能看全这个子路径对外暴露了什么,新增/删除导出只发生在文件底部的两处清单,不会有人无意间把一个内部辅助函数导出出去。二、子路径与命名规则每个公共基元都要有配套的 exports 子路径package.json 中的exports字段是子路径的单一事实来源,每个基元一条,types与import条件同时指向src/primitive/index.tsx:exports: { ./styles.css: ./src/styles/styles.css, ./cn: { types: ./src/cn.ts, import: ./src/cn.ts }, ./button: { types: ./src/button/index.tsx, import: ./src/button/index.tsx }, ./checkbox: { types: ./src/checkbox/index.tsx, import: ./src/checkbox/index.tsx } // ……dialog、drawer、select、tabs、toast 等约 40 个子路径,均遵循同一结构 }指南要求:包内组件之间使用相对导入互相引用,消费者只通过公共子路径导入。新增一个基元时,必须先在这里登记一个同名字段,否则即使源码写好了,子路径也无法被包外解析。规范化边界不用Root后缀命名规则是:规范边界(即组件本身)使用基元原名的组件 同名的Props类型:Select配SelectProps、Drawer配DrawerProps;只有当同一个子路径同时导出底层 anatomy(原子部件)和高层便利组件时,才保留Root后缀,典型如 Checkbox 子路径:CheckboxRoot(可自由组装指示器的底层部件)与Checkbox(自带CheckboxIndicator的便利组合)并存;每个运行时组件都必须有一个准确且可导入的、与组件同名的 props 类型。上游 Base UI 部件若未被改动,直接用直接别名(type XProps BaseXNS.Root.Props);Dify 自研组合组件则在 Dify UI 边界处定义自己的组合 props,而不是照抄上游的形状;当某一个 prop 会改变相关 prop 的合法形状时(如受控/非受控状态、单选/多选),应使用**可辨识联合(discriminated union)**表达。SegmentedControl的类型测试(segmented-control 测试)正好演示了这种值形状受 prop 约束的契约:// ts-expect-error segmented controls require either value or defaultValue SegmentedControl aria-labelMissing value SegmentedControlItem valueoneOne/SegmentedControlItem /SegmentedControl缺少value/defaultValue的写法在类型层面就是非法的。className包装的统一写法当 Dify 的包装器消费上游的className时,标准写法是先Omit掉上游的className(Base UI 的 state-callback 形式),再暴露className?: string,并用cn()合并默认样式类与调用方传入的类。Tabs 子路径 是精简而标准的例子:type TabsListProps OmitBaseTabsNS.List.Props, className { className?: string } function TabsList({ className, ...props }: TabsListProps) { return BaseTabs.List className{cn(flex gap-4, className)} {...props} / }注意其中Tabs本身是未改动的直接别名——指南要求未改动的 Base UI 部件使用直接别名:type TabsProps BaseTabsNS.Root.Props const Tabs BaseTabs.Root三、端到端的泛型契约指南Generic contracts一节的核心主张:泛型关系必须端到端保留,覆盖 picker 的Value与Multiple、表单值、radio/slider 的值,以及 overlay 的 payload 与 handle 等。具体规则:不要用any或写死的string抹掉调用方拥有的类型;unknown只作为被独立消费的、其 value JSX 无法从父级推断类型的 anatomy的安全默认值;不要为 root 单独添加一个泛型,当可独立渲染的 anatomy 可能产生 root 推断类型之外的值时尤其如此——应保持上游契约,直到整个组件家族都能强制同一种值类型;不要宣传一套完整 anatomy 无法强制的类型关系。指南点名了Tabs:它有意跟随 Base UI 的非泛型 root,因为其当前 tab 值类型是any | null;在整套 anatomy 能强制统一值类型之前,不要为它虚构泛型关系。这也解释了 tabs/index.tsx 中Tabs只是一个纯别名、不引入任何泛型参数的事实;上游 anatomy 的各部件若各自拥有不同的语义、交互或定位行为,应原样保留;只有当包确实新增了一份共享契约时才创建 Dify 自研的便利组件,不要为了缩短调用点而隐藏基元部件。仓库中泛型契约的真实验证在本地测试里。SegmentedControl 的类型示例 展示了调用方如何显式声明值类型,并让泛型穿透到 Item:SegmentedControlnumber value{10} onValueChange{() {}} aria-labelPage size SegmentedControlItemnumber value{10}10/SegmentedControlItem SegmentedControlItemnumber value{20}20/SegmentedControlItem /SegmentedControlradio-group 的类型测试 则验证了值的形状约束:boolean radio 项不接受string值(用ts-expect-error boolean radio items should not accept string values守护)。四、把公共面保持在最小Keep the public surface small一节的判断标准很明确:一个类型不会仅仅因为 Base UI 命名了它、或某次实现曾经导出过它就是公共的;除了与组件匹配的 props 之外,只在该类型与某个公共工厂配对、或真实消费者必须独立命名它时才导出;以下类别默认私有:state、事件细节与原因(reasons)、actions、受控状态辅助函数、context 值、render 辅助函数、样式辅助函数,以及上游的透传别名。原因是:公共 props 本身已经为内联 render 与事件回调提供了上下文类型,单独导出这些类型只会放大维护面;当包装器通过cn()消费className时,省略上游的 state-callback 形式,改为暴露className?: string——公共类型必须描述包装器真正实现了的行为,而不是它透传的、但实际未生效的上游能力。对照 checkbox 子路径 可以完整检验这条标准:导出的 4 个组件 4 个同名 Props 类型一一对应,没有任何 Base UI 内部的 state/handler/context 类型泄漏到子路径;而OmitBaseCheckboxNS.Root.Props, className的写法正是省略上游 className 形态、暴露className?: string的落地。五、用证据守护契约:Evidence 部分指南最后一节Evidence规定了守护上述契约的工作方式:使用本地公共子路径类型测试,保护泛型推断、必需的 prop 关联关系,以及故意设计成会报错的非法用法;在修改任何源自上游的契约之前,先阅读当前官方 Base UI 文档与已安装的类型声明(base-ui/react),而不是凭记忆改动。落地形态在包内随处可见:各基元的__tests__/index.spec.tsx中混入了纯类型断言组件(如SegmentedControlTypeExamples、radio-group 的ts-expect-error用例),它们不参与运行时断言,只让tsc在类型检查阶段验证契约。配套的维护手段是 package.json 中的脚本:命令作用type-check(即tsc)全量类型检查,类型测试用例在此被验证test(即vp test --project unit)运行单元测试项目storybook/test:storybook本地启动 Storybook / 运行 Storybook 视觉与 a11y 测试项目也就是说,对 dify-ui 的一次改动是否守约,最终由三把锁共同把关:类型检查(泛型与 props 关联)、单元与浏览器测试(运行时行为)、Storybook 项目(文档化基线)。AGENTS.md 也提醒:只有当组件拥有其类型、stories 与上游文档都无法表达的实质性 Dify 专属契约时,才需要为它单独建立本地 README,不要为了完备而创建。六、修改 dify-ui 公共 API 前检查清单综合 authoring 文档 与仓库现状,新增或修改一个基元的公共面时,按序确认:package.jsonexports中是否有(或新增)与该子路径完全对应的条目,且types/import均指向src/primitive/index.tsx;文件底部是否只存在两个清单式导出(export { ... }与export type { ... }),无内联散乱导出、无export *;组件名是否去掉了多余的Root后缀,同名XxxProps是否可导入且准确;Root是否只用于anatomy 便利组件共存的子路径(如CheckboxRoot/Checkbox);受控/非受控、单值/多值等形状分叉是否用了可辨识联合;调用方拥有的泛型(Value、Multiple 等)是否端到端保留,没有被any/string抹掉;root-only 泛型是否被克制;被导出的每个类型是否都与公共工厂配对或被真实消费者独立命名;state、事件细节、context 值等是否保持私有;className包装是否统一为Omit上游, className { className?: string }cn()合并;是否有(或更新)了本地类型测试,包括用ts-expect-error守护的故意错误用法;动手改上游契约前,是否已核对官方 Base UI 文档与已安装的base-ui/react类型声明。这套规则的本质,是把公共 API 边界从一个隐含约定提升为可检查、可测试、可评审的显式契约:子路径列表即目录,文件底部清单即边界,类型测试即证据。【免费下载链接】difyBuild Agentic workflows, RAG pipelines, with rich AI model and tool support on one collaborative workspace. Deploy on cloud, VPC, or self-hosted, so teams move from prototype to production without rebuilding the stack.项目地址: https://gitcode.com/GitHub_Trending/di/dify创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/24 13:41:10

猫抓完整教程:浏览器资源嗅探与网页视频下载

猫抓完整教程:浏览器资源嗅探与网页视频下载 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 猫抓(cat-catch)是…

2026/10/25 5:08:09

MySQL、PostgreSQL与Oracle数据库审计机制深度解析

1. 数据库审计机制的核心价值与挑战在数据驱动的商业环境中,数据库审计已从合规检查项演变为安全运维的核心组件。我经历过一次典型的审计失效案例:某金融系统遭遇SQL注入攻击,由于未开启语句级审计,导致无法追溯攻击路径&#xf…

2026/10/25 5:08:09

从极简命名到完整落地:轻量级项目开发全流程与避坑指南

1. 从“rea”这个标题说起:一个极简命名背后的完整项目思维第一次看到“rea”这个标题,很多人会愣一下——三个字母,没有上下文,没有说明,甚至不知道它指代什么。但恰恰是这种极简命名,在技术圈和创意圈里越…

2026/10/25 5:08:09

无可挑剔的交付:用流程与清单打磨impeccable标准

今天想认真聊一个词:impeccable。我第一次认真记住这个英文单词,并不是因为背单词,而是因为收到过一份让我印象深刻的交付物。那份交付物的功能其实很基础,但每一个按钮的点击状态、每一处文案的断句、每一张图片的压缩体积都处理…

2026/10/25 5:08:09

AI Agent新阶段:从聊天到干活,Muse登顶与SDK开源的启示

Muse 登顶 App Store 第一那天,我的开发者群直接炸了。大家关注的其实不是这个 App 本身,而是同一天官方甩出的 SDX 开源。一个产品做到 App Store 榜首,说明面向普通用户的路走通了;而 SDK 开源,说明他们打算把这条路…

2026/10/25 5:08:09

AI Agent如何驱动企业数字化转型与智能化升级

1. AI Agent为何成为企业数字化转型的核心引擎最近两年,企业技术架构领域最显著的变化之一,就是AI Agent从实验室概念快速演变为企业基础设施的关键组成部分。这种现象背后反映的是企业数字化转型进入深水区后,对智能化能力的刚性需求。作为从…

2026/10/25 5:03:08

端侧AI越跑越慢?功耗、散热与供电三条物理锁链揭秘

跑端侧AI的人,大概率都碰到过这样一个怪现象:模型没换、代码没动、开发板还是同一块,刚开机的时候token刷刷往外蹦,跑个十来二十分钟,速度肉眼可见地往下掉;等电量再低一些,整个推理延迟能翻倍。…

2026/10/25 0:02:22

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/25 0:02:23

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/25 0:02:23

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/25 0:02:22

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/25 0:02:23

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/25 0:02:23

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑