跳转至

Web UI

原语

控件来自 components/ui。 AGENTS.md 说明了如何使用和扩展它们。

不要为原始的 <button>、<input>、<select> 或 <textarea> 设置样式,使其看起来像某个原语提供的控件。当元素本身不是那种控件时,原始元素是合适的:语义行、标签页、调整大小手柄、色板、编辑器表面,以及任何行为或几何形状在设计上有意不同的东西。

样式

在渲染该元素的组件中使用 Tailwind 类来设置样式。

  • 全局 CSS(位于 index.css 或任何其他样式表中)仅用于拥有该元素的组件上的类无法表达的内容:生成的 Markdown 或命令式 DOM、自定义元素和 shadow root、动画、运行时主题变量,以及浏览器或 Electron 集成。
  • 对于类已覆盖的静态值,不要使用 style={{ ... }} 或内联 <style>。运行时计算的值没有问题。
  • 仅主题规则使用 @variant dark 和 @variant light,而不使用 .dark 选择器。

组合

Composer 横幅是参考实现。

  • ComposerBanner.tsx 导出一个包含小插槽的对象。插槽拥有自己的类,接受 className,并展开其余 props,使用 cn 合并类。变体和密度是 Root 上的 props。需要控件的插槽会渲染 Button。
  • ComposerBannerStack.tsx 组合这些插槽,并且只负责堆叠行为:优先级、展开和折叠、关闭过渡。
  • 使用方只需几行插槽代码,插槽自带横幅样式:ComposerPlanFollowUpBanner.tsx。

以同样的方式构建新 UI。组合或扩展组件,而不是复制其标记和类。当某个调用点重复使用了另一个调用点已经用过的处理方式时,将其做成共享组件上的一个变体或插槽。新的 composer 横幅或通知使用 ComposerBanner 插槽。

大型组件中的处理函数

V8 为一次渲染中创建的每个闭包提供一个共享作用域。被记忆化的处理函数会保留创建它的那次渲染的作用域,而该作用域持有那次渲染中的其他被记忆化的值。当一个组件中的处理函数在不同时间变化时,每个处理函数都会保留一个持有旧处理函数的更早作用域,于是旧渲染及其服务器配置和线程数据,只要组件保持挂载,就会一直留在内存中。React Compiler 的记忆化闭包也是如此。

在 ChatView 和 ChatComposer 这类大型、长期存在的组件中,将处理函数写成普通函数,并且只在它们离开组件的位置用 useStableCallback 包装:props、hook 依赖数组、effect 或 memo 的函数体。组件内部的调用保留普通函数,因此 await 之后恢复的工作仍然作用于它开始时的那次渲染。子组件在渲染期间或 await 之后调用的函数,来自一个基于其输入进行记忆化的模块级工厂,因此其闭包只保留那些输入。

React Compiler

编译器只会在能够证明代码遵守 React 规则时才记忆化组件或 hook。任何它无法证明的情况,例如在渲染期间读取 ref 或从 effect 中调用 setState,都会使它跳过整个函数,而不会产生构建错误。Lint 会将每次跳过报告为 react(...) 警告,并附带说明 "React Compiler skipped optimizing this component or hook"。

apps/web/src 下的每个文件都必须能编译。Lint 将这些规则设为那里的错误,并且 t3code/no-manual-memo 拒绝 useMemo 和 useCallback,因为编译器会让它们变得多余;没有上游规则直接禁止它们。它还拒绝 "use memo"。当一个值在 hook 调用的两侧都到达普通函数调用时,编译器会拒绝提供缓存作用域。"use memo" 辅助函数会强制提供一个,但会隐藏顺序问题。相反,应修复顺序:对值的普通使用放在 hook 之前,或者内联该辅助函数。use* 辅助函数如果不调用任何 hook,会被编译为普通函数且不缓存任何内容,因此派生值辅助函数应自行读取其源 hook,而不是将该值作为参数传入。t3code/no-hookless-use-function 拒绝不调用任何 hook 的 use* 函数。Lint 会漏掉一些跳过情况,尤其是带有 finally 的 try 以及在 try 内 throw,因此 reactCompilerCoverage.test.ts 会编译每个文件,并在任何跳过时失败。异步处理函数很少需要 try:atom 命令会落定为一个结果,因此清理工作可以在 await 之后进行。useStableCallback 仍然标记那些离开大型组件的处理函数,如上所述。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 t3/t3code