Vue3 Composition API 设计模式:从组合函数到状态机

背景与问题界定 随着 Vue3 的全面普及,Composition API 已经成为中大型项目的主要开发范式。然而,许多团队在使用过程中暴露出两个典型问题:一是组合函数的跨组件复用缺乏统一规范,导致同一业务逻辑在不同组件中出现三四种不同的封装风格;二是复杂交互场景下的状态管理陷入面条式代码——多个 ref 和 reactive 散布在 setup 中,状态转换路径隐晦难测,尤其是在多步骤表单、长连接握手和异步任务编排等场景中尤为突出。这些问题本质上是 Composition API 提供的灵活性带来了设计约束的缺失,当团队规模超过 5 人时,维护成本呈指数级上升。我们需要一套可落地、可审阅的设计模式来解决这些工程痛点。 目标拆解与工程约束 组合函数的可组合性与语义清晰:每个组合函数应保持单一职责,函数名体现用途,返回值使用 readonly 和 toRefs 控制可变性暴露,禁止在函数内部意外修改外部作用域。 状态转换的可追溯性与不可变快照:复杂状态机场景必须采用显式状态定义,禁止隐式地在多个 ref 之间通过 watch 联动产生状态迁移,所有合法转换路径需声明在状态表中。 与 Pinia/Vuex 的职责边界:组合函数负责局部 UI 逻辑和设备 API 封装,全局跨组件数据流向 Pinia store 管理,避免在组合函数中引用全局 store 造成紧耦合。 单元测试友好:组合函数应纯化副作用,API 请求、定时器和 DOM 事件通过可注入的适配器封装,确保 mount 时无需真正初始化浏览器环境即可完成逻辑验证。 方案设计 核心思路是将 Composition API 的使用提升到设计模式层面,不再把 setup 看作一个可以随意书写命令式逻辑的地方。我们定义了三层模式体系:基础组合函数(Primitive Composables)、业务组合函数(Business Composables) 和 有限状态机(State Machine)。 基础组合函数负责单一浏览器 API 或通用工具的封装,例如 useLocalStorage、useMediaQuery。这类函数具有纯函数式的接口——接收配置型参数,返回响应式状态和操作方法,不做任何业务依赖。业务组合函数则编排一个完整的业务领域逻辑,比如 useOrderFlow,它内部组合多个基础组合函数,暴露统一的 state、actions 和 effects。 当业务组合函数的内部状态转换超过 3 个分支时,强制采用有限状态机模式。借助 @vueuse/core 的 useMachine 或自行实现转换表,将每个状态(idle / loading / success / error)和允许的切换路径显式声明。例如一个异步提交流程的状态机: ...

2026年8月1日 · 1 分钟 · BvBeJ

C++ 反射实现:从 0 到依赖注入容器

背景 C++ 一直缺少反射(Reflection),虽然 C++20 引入了 std::reflect,但目前编译器支持还很有限。 实际项目中,我们经常需要: 根据字符串创建对象 自动注入依赖 序列化/反序列化 这篇文章聊聊怎么在 C++ 里实现一套轻量反射系统。 反射的核心:类型注册 反射的本质是在运行时动态查询类型信息。实现思路很简单——用全局注册表。 1. 基础类型注册表 #include <functional> #include <unordered_map> #include <string> #include <memory> class TypeRegistry { public: template<typename T> static void registerType(const std::string& name) { creators()[name] = []() -> std::any { return std::make_any<T>(); }; } static std::any create(const std::string& name) { auto it = creators().find(name); if (it != creators().end()) { return it->second(); } throw std::runtime_error("Unknown type: " + name); } private: static auto& creators() { static std::unordered_map<std::string, std::function<std::any()>> map; return map; } }; 2. 宏简化注册 #define REGISTER_TYPE(T) \ namespace { \ struct Registrar##T { \ Registrar##T() { \ TypeRegistry::registerType<T>(#T); \ } \ }; \ static Registrar##T registrar_##T; \ } 3. 使用 struct User { std::string name; int age; }; REGISTER_TYPE(User) int main() { auto any_user = TypeRegistry::create("User"); auto& user = std::any_cast<User&>(any_user); user.name = "BvBeJ"; user.age = 28; } 进阶:带构造函数参数 上面的实现只能调用默认构造函数。实际场景往往需要传参: ...

2026年4月6日 · 2 分钟 · BvBeJ