去年审查一个后台系统,看到一个页面组件嵌套了十一层,props 从最顶层 drilling 到最底层,中间每层都要透传七八个参数。

改一个字段名,要改六七个文件。更隐蔽的是,React 每次渲染都要遍历这整棵树,低端设备上明显卡顿。

问题根源不是嵌套本身,是嵌套方式错了。

我们习惯把组件当成"容器"来用:页面组件包着布局组件,布局组件包着区块组件,区块组件包着卡片组件,卡片组件里才是真实内容。每层都接收 props,筛选一遍,再传给下一层。props 像接力棒一样层层传递,中间任何一层掉链子就断了。

这种模式的问题在于耦合。父组件知道子组件需要什么数据,子组件又依赖父组件的传参结构。一改需求,整条链都要动。而且 React 的渲染机制是递归遍历组件树,树越深,遍历成本越高,即使用了 memo,浅比较也要跑遍每一层。

解决办法是组合模式,不是继承,不是 HOC,是像搭积木一样拼组件。

React 官方文档里其实一直推荐这个思路,但很多人没当回事。核心思想是:父组件只负责搭架子,具体内容通过 children 或者 render prop 插进去。中间层不传递数据,只提供位置和样式。

jsx
复制
// 以前:层层传递
<Page user={user} orders={orders} settings={settings}>
<Layout user={user}>

<Sidebar user={user} settings={settings} />
<Main orders={orders} />

</Layout>
</Page>

// 现在:组合拼插
<Page>
<Layout

sidebar={<Sidebar />}
main={<Main />}

/>
</Page>
Sidebar 和 Main 各自通过 Context 或者数据获取库拿需要的数据,不依赖父组件传参。Page 和 Layout 只关心"放在哪",不关心"放什么"。

具体怎么落地?

把页面拆成"布局组件"和"内容组件"两类。布局组件负责结构,比如两栏、三栏、固定头部。内容组件负责业务,比如用户卡片、订单列表。布局组件通过 props 接收内容组件,而不是接收数据再往下传。

数据获取下沉到叶子节点。能用服务端组件的直接在叶子节点 async/await,客户端的用 SWR 或 TanStack Query。父组件不碰数据,只负责组合。
中间层用 React Context 做依赖注入,但只注入"能力"不注入"数据"。比如注入一个主题对象或者国际化函数,而不是注入用户详情。

我的观点:

组件嵌套深不是问题,层层传递才是。组合模式把"怎么摆"和"摆什么"解耦,树变平了,渲染路径变短了,改需求时影响的范围也变小了。这不是新思路,React 团队从 2018 年就在推,但直到被嵌套地狱折磨过,才真正理解它的价值。

你的项目里组件最多嵌套过几层?有没有被 props drilling 逼疯过?