跳到正文

我所理解的代码风格最佳实践(2):Vue 组件与 composable 的职责边界

我所理解的代码风格最佳实践

  1. JavaScript 代码怎样从调用点读下去
  2. Vue 组件与 composable 的职责边界(当前)
  3. Nuxt 页面、目录与自动导入
  4. 哪些项目约定值得写成 Lint

个人资料卡要显示昵称和头像。昵称可以来自路由用户的资料,也可能要用调用方提供的备用文案。以后如果加备注名,最先需要确认的是这张卡在显示谁,以及备注属于哪一份数据。把这些问题说清楚以后,才好决定哪些值放在组件里,哪些交给 composable。

从资料卡的调用点开始

const { avatarSrc, displayName } = useProfileIdentity({
  fallbackName,
  userProfile,
});

这个调用里,userProfile 是当前展示的用户资料,fallbackName 是资料缺失时的文案。调用方持有这两个响应式输入。composable 根据它们派生名字和头像,资料请求、缓存刷新仍由提供资料的一方负责。

调用处的字段名已经说明备用文案的用途。资料卡可以先只解构 displayName,将来再按需解构 avatarSrc 或 displayNameIsRemarked。给参数对象增加可选字段、给结果对象增加属性,通常可以沿用已有的解构调用。已有字段一旦变成必填或换了含义,就得回头检查调用方。

如果返回的是普通对象,里面的字段各自是 Ref,解构拿到的仍然是引用。传递响应式对象里的普通属性时,直接取值可能只拿到当时的快照。需要持续跟随源值时,我会按契约传 ref 或 getter。这两个决定分开看:对象形状照顾 API 演进,ref 或 getter 照顾更新路径。Vue 的 composable 返回值约定

对于稳定的全局依赖,可以由 composable 自己读取。例如统一的备注缓存由 useUserDisplayName() 获取,返回的 resolveDisplayName(user) 再接受每次要展示的用户。独立的底层函数若只有一个稳定输入和一个结果,位置参数与单值返回也很自然。

谁能改这个状态

资料卡的展开状态可以留在资料卡内部。若父组件确实需要协调几个卡片的展开行为,再把选择权提升给父组件,通过 props 表达输入,通过事件报告用户操作。昵称是资料与备用文案的派生值,直接计算即可。若编辑器支持未保存草稿,草稿有独立的生命周期,才值得成为另一份可写状态。

当前登录账号和当前路由用户尤其容易混在一起。主态下两者恰好相同,访问别人主页时就分开了。Header 要显示当前账号,资料卡要显示路由用户。编辑本人资料之后,账号资料需要刷新,路由页则只在它展示同一个用户时才需要联动。把所有入口接到一个含义模糊的 useMeProfile 上,读者很难从调用点判断究竟刷新谁。完整案例保留了身份与资料的拆分、慢响应处理和测试。一次 Profile 全局状态重构

把公开入口和实现放在能找到的地方

useProfileIdentity 只有一个职责时,可以留在一个文件里。它长出只供自己使用的备注解析或资料归一化逻辑后,可以将这些 helper 放进同一个模块目录,通过入口只公开主函数。其他模块继续从入口调用,内部 helper 的拆分和改名由这个模块自己决定。

全局数据源可以给组件提供数据与动作,依赖方向也要从调用处看得出来。一个全局账号 source 如果反过来导入资料卡目录里的 helper,改资料卡就可能连带改变账号逻辑。先看 helper 实际为谁服务,再决定留在组件、移到业务数据层,还是提成真正通用的工具。

需要组件实例或 Nuxt app 上下文的能力,我习惯在 composable 初始化时取得,再让事件回调通过闭包使用。这样从入口就能看到它依赖什么,回调触发时也有明确的上下文来源。框架对某些异步位置有专门转换,具体规则和例外可以单独查阅。用 ESLint 检查 composable 的公开面、依赖方向与调用时机

资料卡的根节点怎么暴露展开状态、样式差异留在哪里,是同一个归属判断在 CSS 里的应用。组件状态和就近差异样式