你好,我是 Kagol,个人公众号:前端开源星球

TinyVue 中的无渲染组件架构代表了一种精密的关注点分离模式,它将组件逻辑与表现层分离,实现了在多个平台、框架和设计变体间前所未有的代码复用性。该架构作为 TinyVue 跨平台能力的基础层,允许单一逻辑实现同时服务于 PC、移动端和移动优先组件,并支持 Vue 2 和 Vue 3 框架。

核心架构原则

无渲染架构实现了无头组件模式,其中组件逻辑完全与任何渲染关注点解耦。这种方法在业务逻辑、状态管理和 UI 表现之间创建了清晰的分离。无渲染包中的每个组件包含两个主要文件:包含纯无状态逻辑函数的 index.ts,以及使用 Vue 特定响应性和生命周期管理包装这些函数的 vue.ts 文件。

该架构通过 ISharedRenderlessParamHooks 和 ISharedRenderlessParamUtils 类型提供标准化接口,定义了无渲染逻辑与消费框架之间的契约。这个抽象层使相同的无渲染逻辑能够在 Vue 2、Vue 2.7 和 Vue 3 之间无缝工作,由适配器层处理框架特定的差异。

组件结构与组织

每个无渲染组件遵循一致的组织模式,在保持类型安全和开发者体验的同时最大化可复用性。该结构由具有明确职责的两个不同层组成。

逻辑层 (index.ts)

index.ts 文件包含实现组件行为的纯函数,没有任何框架依赖。这些函数接受包含组件状态、props、事件发射器和工具的参数,返回计算值或触发副作用。例如,button 组件的 handleClick 函数管理点击事件,通过 href 属性处理导航,并实现重置计时器机制以防止重复提交。

响应性层 (vue.ts)

vue.ts 文件作为框架适配器,使用 Vue 的响应性系统包装纯逻辑函数。它使用 Vue 的 reactive 和 computed API 管理组件状态,监听 props 变化,并与 onBeforeUnmount 等生命周期钩子集成以进行清理操作。该层暴露标准化 API,供 Vue 组件通过 setup() 函数消费。

类型系统集成

无渲染架构为每个组件包含全面的 TypeScript 类型定义。类型文件定义了组件状态(IButtonState)、props(IButtonProps)和 API 表面(IButtonApi)的接口。这些类型利用 Vue 的 ExtractPropTypes 工具确保 props 定义在整个代码库中类型安全。类型系统还定义了像 ISharedRenderlessParamUtils 这样的共享接口,封装了跨平台工具和上下文。

跨平台复用模式

无渲染架构的真正力量体现在其能够用单一逻辑实现服务多个组件变体。packages/vue 目录中的 Vue 组件消费无渲染逻辑,同时为不同平台提供不同的表现层。

组件变体消费

Vue 组件为每个平台变体组织源文件,使用不同的模板。例如,button 组件包括用于桌面界面的 pc.vue 和用于移动优先设计的 mobile-first.vue。两个变体都从 @opentiny/vue-renderless/button/vue 导入相同的无渲染逻辑,但提供完全不同的标记和样式方法。

模板集成模式

PC 和移动优先模板都遵循相同的集成模式:它们从无渲染包导入 renderless 和 api,定义组件 props,并使用 @opentiny/vue-common 的 setup() 函数初始化无渲染逻辑。返回的 API 对象包含模板可以绑定的状态和方法,实现响应性状态与模板表达式的无缝集成。

通用工具集成

无渲染架构通过 ISharedRenderlessParamUtils 接口提供通用工具,包括表单注入(inject('form', null))、父组件通信(dispatch 和 broadcast 方法)和设计配置访问。这个标准化工具集确保所有组件行为一致,同时消除代码重复。

状态管理与响应性

无渲染架构采用精密的状态管理模式,结合响应性状态和计算属性以高效派生值。

响应性状态初始化

状态对象使用 Vue 的 reactive() 函数创建,包含原始值和计算属性。button 组件的状态包括用于管理重置功能的 timer、可通过 props 和内部逻辑控制的 disabled 标志,以及多个从父组件或设计配置派生值的计算属性。

派生状态的计算属性

计算属性能够在不进行不必要重计算的情况下高效派生复杂状态。button 组件使用计算属性根据 props、父组件状态(如按钮组设置)和表单上下文的组合来确定按钮是否应显示为 plainround 或 disabled。这种方法确保当任何影响因素发生变化时,组件外观会自动更新。

Watch 集成

监听器监控 props 变化并相应更新内部状态。button 组件监听 disabled prop 以同步内部 state.disabled 值,确保外部 prop 变化正确反映在组件的响应性状态中。这种模式在组件接口和内部状态管理之间保持一致性。

复杂组件示例:Input 组件

input 组件展示了无渲染架构处理复杂状态管理和用户交互的能力。凭借近 600 行逻辑代码,input 组件展示了表单验证、textarea 自动调整大小、密码可见性切换和字符计数的高级模式。

动态 Textarea 调整大小

calcTextareaHeight 函数实现了精密的 textarea 自动调整大小逻辑,根据内容、盒模型和行约束计算最佳高度。该函数创建隐藏的 textarea 元素来测量内容尺寸,根据盒模型考虑内边距和边框大小,并基于最小和最大行参数应用高度约束。

组合事件处理

input 组件通过 handleCompositionStarthandleCompositionUpdate 和 handleCompositionEnd 函数正确处理 IME 组合事件(在韩语、日语和中文等亚洲语言中常见)。这些函数管理 isComposing 状态以防止组合过程中的中间文本更新,确保准确的表单验证和值更新。

内存存储集成

input 组件与存储箱工具(useStorageBox)集成,提供基于内存的自动完成功能。这种集成展示了无渲染组件如何与其他工具库组合,同时保持清晰的关注点分离。存储功能使组件能够在会话间记住用户输入,而不需要持久化后端存储。

设计配置集成

无渲染架构通过 designConfig 参数支持设计系统配置,使组件能够适应不同的设计规范而无需代码更改。button 组件访问 designConfig.props.round 以确定未通过 props 显式指定时的默认圆角行为。

全局弹窗管理器配置

无渲染包包含 popup-manager 工具,为弹窗组件行为(如 z-index 堆叠顺序)提供全局配置。开发者可以通过导入弹窗管理器并在应用程序入口文件中设置属性来自定义此配置。

弹窗管理器展示了无渲染组件如何暴露影响组件行为的全局配置工具,而不需要单个组件配置。这种集中化方法确保整个应用程序中所有基于弹窗的组件行为一致。

类型安全与开发者体验

无渲染架构通过全面的 TypeScript 集成优先考虑类型安全。每个组件导出其状态、props 和 API 表面的类型定义,启用自动补全和编译时错误检查。shared.type.ts 中定义的共享类型提供了所有无渲染组件必须实现的契约,确保组件库的一致性。

接口契约

ISharedRenderlessParamUtils 接口定义了无渲染函数可用的完整工具集,包括框架检测('vue3' | 'vue2' | 'vue2.7')、模式检测('pc' | 'mobile' | 'mobile-first')、国际化函数(t)和组件通信方法(dispatchbroadcast)。这个全面的接口确保无渲染组件能够访问所有必要的上下文,而不依赖框架特定的功能。

组件集成流程

下图展示了从无渲染逻辑到多平台变体组件实现的架构流程:

优势与用例

无渲染组件架构提供了几个关键优势,使其对企业组件库特别有价值。

跨框架兼容性

逻辑与表现层的分离使相同的组件逻辑能够支持多个 JavaScript 框架。虽然 TinyVue 目前专注于 Vue 2 和 Vue 3,但该架构可以通过实现新的适配器层扩展到支持 React、Angular 或其他框架,同时复用现有逻辑。

平台变体整合

单一无渲染实现可以服务多个平台变体(PC、移动端、移动优先),大幅减少代码重复和维护开销。这种整合确保跨平台行为一致,同时允许平台特定的表现优化。

测试隔离

index.ts 文件中的纯逻辑函数可以在没有框架依赖的情况下独立测试,实现更简单、更专注的单元测试。框架特定行为可以在 vue.ts 适配器层中单独测试,在纯逻辑和框架集成之间创建清晰的测试边界。

低代码平台集成

无渲染架构为低代码平台集成提供了理想基础。平台构建者可以直接导入无渲染组件并提供匹配其特定设计系统的自定义渲染层,实现设计系统无关组件库的快速开发。

无渲染架构的关注点分离使其特别适合微前端架构,不同团队可以实现不同的表现层,同时通过无渲染包共享共同的业务逻辑。

API 表面标准化

无渲染架构通过标准化模式强制 API 一致性,以暴露组件功能。每个组件导出定义公共 API 表面的 api 数组,包括状态属性和方法名称。

方法暴露

通过 API 表面暴露的方法包括直接函数导出(如 handleClick 和 clearTimer)和基于状态的计算方法。input 组件的 API 包含焦点管理(focusblur)、文本选择(select)、输入处理(handleInputhandleChange)和验证状态管理的广泛功能。

生命周期管理

无渲染架构与 Vue 的生命周期钩子正确集成,确保资源清理和正确初始化。组件使用 onBeforeUnmount 清除计时器和移除事件监听器,防止内存泄漏。

无渲染组件中的生命周期管理集中在 vue.ts 适配器层,确保像清理操作这样的框架特定关注点不会污染 index.ts 中的纯逻辑函数。

高级模式

工具组合

无渲染组件与 @opentiny/utils 等工具包组合,用于 XSS 过滤、防抖和 DOM 操作等常见操作。button 组件使用 xss.filterUrl() 在导航前清理 URL 值,展示了安全意识的开发实践。

父子组件通信

该架构通过 dispatch 和 broadcast 方法支持复杂的父子组件通信模式。组件可以向上派发事件到组件树以查找特定父组件,或向下广播消息到子组件,实现协调行为而无需紧耦合。

迁移与采用

对于熟悉传统单体组件的开发者,迁移到无渲染架构需要理解关注点分离原则并适应三层结构(逻辑、适配器、表现层)。关键见解是业务逻辑不应依赖于表现层关注点,实现跨平台和框架的最大可复用性。

理解无渲染组件架构为探索更广泛的 TinyVue 生态系统提供了坚实基础。

联系我们

GitHub:https://github.com/opentiny/tiny-vue(欢迎 Star ⭐)

官网:https://opentiny.design/tiny-vue

个人博客:kagol.com.cn

小助手微信:opentiny-official

公众号:OpenTiny

Logo

OpenTiny 是企业智能前端开发解决方案,以生成式 UI 和 WebMCP 两大自主核心技术为基础,加速企业应用的智能化改造。我们会在社区定期为大家分享一些前后端的技术文章。

更多推荐