Vue3 组件重构实战:从“一坨代码”到高内聚组件,顺便干掉烦人的布局对齐 Bug!

最近在维护个人博客项目时,遇到一个非常典型的场景:

我的“个人主页”和“博客详情页”左侧都有一个长得一模一样的“作者信息面板”(包含头像、关注/粉丝数、私信按钮等)。

最开始,这些代码全部揉在具体的页面文件里。随着功能增加,代码越来越臃肿。于是我决定进行一波重构,把左侧面板抽离成一个独立的 UserInfo.vue 组件。

在这个过程中,我不仅体会到了组件高内聚的爽快感,还和 CSS 布局对齐的“幽灵 Bug”狠狠切磋了一把。

今天把过程记录下来,希望能帮到遇到同样问题的小伙伴。

进阶思考:到底什么是真正的“高内聚”?

刚开始抽离 UserInfo.vue 时,遇到一个关于“私信”功能的问题。点击私信按钮,需要弹出一个 <Chat> 聊天窗口。

最初的习惯性思维是:在子组件 UserInfo.vue 里用 emit 抛出一个事件,让外部的父页面去监听这个事件,然后控制 <Chat> 组件的显示和隐藏。

但这带来了一个问题:不管我在哪里复用 UserInfo 组件,父页面都必须额外引入一次 <Chat> 组件,并写一堆控制状态的代码。这太累赘了!

最终破局点:直接把 <Chat> 组件塞进 UserInfo.vue 里!

既然“私信”是用户信息的强相关功能,那就让 UserInfo 自己消化掉。

  • 在 UserInfo.vue 内部引入 Chat.vue。
  • 在内部维护 chatVisible 状态。
  • 在内部处理“不能和自己聊天”等校验逻辑。

这样一改,UserInfo.vue 就变成了一个真正的即插即用组件。父页面只需要 <UserInfo :user="authorInfo" /> 一行代码,所有用户信息展示、关注、点赞、私信聊天功能全包了,外部代码变得异常清爽!

踩坑实录:复用组件时的“对齐幽灵”

组件抽离得很完美,但在实际复用到“个人主页”和“博客详情页”时,UI 彻底乱套了:

  1. 博客详情页: 左侧信息栏和右侧文章完全对不齐,左侧莫名其妙多出一截空白。
  2. 个人主页: 修复了详情页后,主页的左侧又和中间的工具栏对不齐了。

排查了半天,发现罪魁祸首是我在抽离组件时,把特定的布局占位(Spacer)和悬浮(Sticky)样式一起写进了子组件里

原先的页面为了配合顶部的工具栏,写了一个 <div class="align-spacer"></div>(高度 36px)来强行占位,并且用了 position: sticky; top: 20px;。当把这些带着强烈“上下文环境”的代码封装进通用组件后,它到了不需要工具栏的详情页,自然就往下掉了一大截。

破局之道:把布局控制权交还给父组件

经过这次踩坑,我总结出一个复用组件的铁律:

通用组件内部只管业务逻辑和自身 UI,绝对不要写死对外的 margin、定位(absolute/fixed/sticky)或空 div 占位!

最终的解决方案如下:

1. 净化子组件 (UserInfo.vue):
删掉内部所有用于顶部占位的 <div class="align-spacer"></div>,删掉 sticky 相关的 CSS。让它变成一个高度完全由内容撑开的纯粹组件。

2. 详情页的极致对齐:
在博客详情页的 Flex 容器上,加上 align-items: flex-start;

防止子元素在纵向上被默认拉伸,确保左右两栏像刀切一样在顶部完美平齐。

css

/* 博客详情页的父容器 */
.main-body {
  display: flex;
  align-items: flex-start; /* 核心:确保顶部对齐 */
  gap: 24px;
}

3. 个人主页的精准下推:
在个人主页中,给 UserInfo 组件套一层专门的 Wrapper,由这个 Wrapper 来负责该页面的特有布局(吸顶和下推)。

html

<div class="person-left-wrapper">
  <UserInfo :user="user" />
</div>

css

/* 个人主页专属 Wrapper */
.person-left-wrapper {
  position: sticky;
  top: 20px;
  width: 280px; 
  flex-shrink: 0; 
  margin-top: 36px; /* 精确下推 36px,与中间工具栏完美对齐 */
}

总结

这次重构虽然踩了几个坑,但收获颇丰:

  1. 业务逻辑: 能让子组件内部闭环的逻辑(比如包含弹窗、聊天),就不要抛给父组件,这才是真正的解耦与高内聚。
  2. CSS 布局: 抽离公共组件时,一定要“剥离环境”。定位、外边距占位是父级容器该操心的事,子组件只负责把自己画好就行。

敲代码就是一个不断折腾、推翻自己再重构的过程。搞定了这波重构,看着清爽的代码和对齐完美的 UI,强迫症表示非常满足!