标准答案

  1. 先区分展示组件、容器组件和业务组件,避免一个组件同时负责请求、权限、布局、弹窗和表单提交。
  2. 组件 props 应表达业务语义,而不是暴露内部 DOM、样式细节或页面级状态结构。
  3. 副作用和数据请求可以放在页面层、自定义 Hook 或数据层,组件通过数据和回调协作。
  4. 插槽式能力可以用 children、render props 或组合组件表达,不要靠大量布尔 props 堆分支。
  5. 组件要保留扩展点,例如空状态、操作区、权限控制和错误态,但不要为了所有未来场景过度抽象。

题目解析

这道题关注组件边界。低耦合不是没有业务含义,而是不要让组件直接依赖具体页面流程。

好的业务组件通常可以在相近场景复用,但不追求跨所有业务线通用。

判断封装是否合适,可以看它换接口、换页面、换权限来源时是否需要大改内部实现。

代码示例

下面的用户选择器只关心 value、options 和 onChange,不直接知道页面如何请求数据。

TSX
type UserSelectProps = {
  value?: string
  options: Array<{ id: string; name: string }>
  disabled?: boolean
  onChange: (userId: string) => void
}

function UserSelect({ value, options, disabled, onChange }: UserSelectProps) {
  return (
    <select value={value} disabled={disabled} onChange={event => onChange(event.target.value)}>
      {options.map(user => (
        <option key={user.id} value={user.id}>{user.name}</option>
      ))}
    </select>
  )
}

常见误区

  • 组件内部直接请求固定接口,导致同类业务页面很难复用。
  • 为了通用性加入几十个布尔 props,组件分支比页面还复杂。
  • 把权限、路由、埋点和弹窗流程都塞进一个展示组件里。

作者信息