前端组件三层架构:页面/业务/基础组件划分,高内聚低耦合|组件化设计基础篇
【Vue3 TypeScript 业务组件化】中后台前端实战从组件设计原则到真实业务落地掌握可复用业务组件写法避开组件臃肿、耦合、难维护6大高频坑 文章目录一、先说人话为什么你写的组件“看起来能用”却越来越难维护二、什么是“业务组件”1基础组件UI 组件2业务组件Domain Component三、核心原则记住这 5 条就够你避开大坑四、实战案例封装一个可复用“商品卡片业务组件”1需求拆解2组件代码完整示例3父组件如何使用3 个场景五、你最容易踩的 6 个坑高频六、一套能直接落地的业务组件规范建议贴团队 wiki命名规范目录建议设计约束七、进阶一点什么时候拆成“容器组件 展示组件”八、给 7 年前端的一句“校准建议”九、结尾 系列模块导航 组件化设计基础 系列总览同学们好我是 Eugene尤金一名多年中后台前端开发工程师。Eugene 发音 /juːˈdʒiːn/大家怎么顺口怎么叫就好当你能写出规范、可维护的代码后下一个真正的瓶颈就是架构。面对大型项目、复杂业务你是否也会遇到组件越写越乱、重复开发越来越多需求一变全链路改动不知道怎么分层、怎么抽象、怎么设计才能支撑长期迭代想晋升、想带项目却缺少架构思维。这一系列《前端组件化与架构实战》我会继续用大白话 真实业务场景不讲玄学、不啃晦涩源码只教你能落地、能抗复杂项目的架构思路。帮你从「写页面的开发者」真正升级为「能做架构、能带项目、能搞定复杂需求的前端工程师」。一、先说人话为什么你写的组件“看起来能用”却越来越难维护很多项目里组件最后会变成下面这样一个组件里塞了 20 多个props逻辑、UI、接口请求全耦合在一起不同页面只改了 10% 需求却不得不 copy 一份再魔改一个月后自己都不敢动怕改崩这不是你能力差而是缺了“业务组件设计”的基本方法。⬆ 返回目录二、什么是“业务组件”先区分两个概念1基础组件UI 组件只关注样式和基础交互比如Button、Input、Modal。特点通用性高业务语义低。⬆ 返回目录2业务组件Domain Component是把“某个业务场景”抽象成模块比如商品卡片ProductCard订单状态条OrderStatus地址选择器AddressSelector特点有业务语义可复用但不会过度追求全场景通吃。⬆ 返回目录三、核心原则记住这 5 条就够你避开大坑1单一职责一个组件只解决一个业务问题别把“展示 编辑 请求 权限判断 埋点”都塞进一个组件。2输入稳定Props 输出明确Events组件不是黑盒魔法。你要让同事一眼看懂我传什么进去组件会吐什么出来3默认值友好防御式设计新手最常见报错Cannot read properties of undefined。给默认值、做空值兜底是组件“可复用”的基础。4业务逻辑可下沉到 composable重复的业务逻辑放进useXxx组件保持“可读、可测、可维护”。5先满足当前 2~3 个场景不要提前设计宇宙级抽象“过度抽象”比“轻微重复”更可怕。先跑通真实需求再迭代抽象层。⬆ 返回目录四、实战案例封装一个可复用“商品卡片业务组件”目标同一个组件在列表页、活动页、推荐位都能用但展示细节可配置。1需求拆解公共能力展示商品图、标题、价格显示库存状态点击“加入购物车”点击卡片可跳详情场景差异某些场景需要角标如“限时折扣”某些场景不展示库存按钮文案可自定义“立即购买”/“加入购物车”⬆ 返回目录2组件代码完整示例components/ProductCard.vuetemplatearticleclassproduct-cardclickhandleCardClickdivclassthumb-wrapimg:srcsafeProduct.cover:altsafeProduct.titleclassthumb/spanv-ifbadgeTextclassbadge{{ badgeText }}/span/divdivclasscontenth3classtitle{{ safeProduct.title }}/h3pclassprice¥ {{ formatPrice(safeProduct.price) }}/ppv-ifshowStockclassstock:class{ danger: safeProduct.stock 10 }库存{{ safeProduct.stock }}/pbuttonclassaction-btn:disabledsafeProduct.stock 0click.stophandleAddCart{{ buttonText }}/button/div/article/templatescriptsetuplangtsimport{computed}fromvueinterfaceProduct{id:stringtitle:stringcover:stringprice:numberstock:number}constpropswithDefaults(defineProps{product:Product badgeText?:string showStock?:boolean buttonText?:string}(),{badgeText:,showStock:true,buttonText:加入购物车})constemitdefineEmits{(e:card-click,productId:string):void(e:add-cart,payload:{productId:string;quantity:number}):void}()// 防御式兜底避免外部传参不完整导致模板报错constsafeProductcomputedProduct(()({id:props.product?.id??,title:props.product?.title??未知商品,cover:props.product?.cover??https://via.placeholder.com/300x300?textNoImage,price:Number(props.product?.price??0),stock:Number(props.product?.stock??0)}))functionformatPrice(price:number){returnprice.toFixed(2)}functionhandleCardClick(){if(!safeProduct.value.id)returnemit(card-click,safeProduct.value.id)}functionhandleAddCart(){if(!safeProduct.value.id||safeProduct.value.stock0)returnemit(add-cart,{productId:safeProduct.value.id,quantity:1})}/scriptstylescoped.product-card{width:260px;border:1px solid #eee;border-radius:12px;overflow:hidden;cursor:pointer;transition:box-shadow 0.2s ease;background:#fff;}.product-card:hover{box-shadow:0 6px 18pxrgba(0,0,0,0.08);}.thumb-wrap{position:relative;}.thumb{width:100%;height:180px;object-fit:cover;display:block;}.badge{position:absolute;top:10px;left:10px;background:#ff4d4f;color:#fff;font-size:12px;padding:2px 8px;border-radius:999px;}.content{padding:12px;}.title{margin:0 0 8px;font-size:15px;line-height:1.4;}.price{margin:0 0 6px;color:#e60012;font-weight:700;}.stock{margin:0 0 10px;color:#666;font-size:13px;}.stock.danger{color:#fa541c;}.action-btn{width:100%;height:34px;border:none;border-radius:8px;background:#1677ff;color:#fff;}.action-btn:disabled{background:#bfbfbf;cursor:not-allowed;}/style⬆ 返回目录3父组件如何使用3 个场景views/ProductList.vuetemplatesectionclasslist-wrapProductCardv-foritem in products:keyitem.id:productitembadge-text热卖:show-stocktruebutton-text加入购物车card-clickgoDetailadd-cartaddCart//section/templatescriptsetuplangtsimport{ref}fromvueimportProductCardfrom/components/ProductCard.vueconstproductsref([{id:p1,title:机械键盘 K87,cover:https://picsum.photos/300?1,price:299,stock:25},{id:p2,title:无线鼠标 M3,cover:https://picsum.photos/300?2,price:89,stock:8},{id:p3,title:4K 显示器,cover:https://picsum.photos/300?3,price:1899,stock:0}])functiongoDetail(productId:string){console.log(跳转商品详情,productId)// router.push(/product/${productId})}functionaddCart(payload:{productId:string;quantity:number}){console.log(加入购物车,payload)// 调接口 addCartApi(payload)}/scriptstylescoped.list-wrap{display:grid;grid-template-columns:repeat(3,260px);gap:16px;}/style⬆ 返回目录五、你最容易踩的 6 个坑高频把请求写进组件内部导致组件强依赖某个接口难复用。建议父组件请求子组件只发事件。props过多且语义混乱超过 10 个就要警惕考虑拆子组件或合并配置对象。事件命名不清晰click、change太泛优先add-cart、card-click这种业务语义。默认值缺失线上最常见是空数据导致模板报错。把“可配置”做成“可随便改”配置项太多会变成“新项目重写旧组件”。没有约定文档没人知道组件怎么用最后还是复制粘贴。⬆ 返回目录六、一套能直接落地的业务组件规范建议贴团队 wiki命名规范组件名业务域 语义如ProductCard、OrderTimeline事件名动词开头表达动作add-cart、submit-orderprops布尔使用is/has/show前缀如showStock⬆ 返回目录目录建议components/ product/ ProductCard.vue ProductPrice.vue composables/ useCart.ts types/ product.ts⬆ 返回目录设计约束单组件props尽量 8必选props必须写类型可选props必须有默认值对外事件必须在注释/文档标明 payload 结构至少给 1 个完整使用示例⬆ 返回目录七、进阶一点什么时候拆成“容器组件 展示组件”当一个业务组件开始出现这些信号时就该拆了同时处理接口请求、权限判断、复杂状态流转模板越来越长200 行新需求总在“if/else”里叠逻辑拆法容器组件管数据与业务流程请求、状态、权限展示组件只管接收数据并展示 抛出用户行为事件这一步能显著提升可测试性和可维护性。⬆ 返回目录八、给 7 年前端的一句“校准建议”你已经能把需求做出来了下一阶段拼的是可维护性、可协作性、可演进性。业务组件设计不是“追求完美抽象”而是用稳定输入输出建立协作边界用适度抽象减少重复劳动用清晰结构降低未来改动成本⬆ 返回目录九、总结如果你也遇到过“组件越写越重、改一处崩三处”的问题建议从今天开始做三件事每写一个组件先问自己它只解决一个业务问题吗先设计props和events再动手写模板每个业务组件都给一个“最小可运行示例”把这三步坚持一个月你的组件质量会非常明显地提升。 系列模块导航 组件化设计基础持续更新中敬请期待 跟着系列慢慢学把技术功底扎扎实实地打牢 系列总览前端体系化学习完全体基础 → 规范 → 架构 → 大厂面试四套系列、百余篇高质量实战文从入门到进阶一站式补齐前端核心能力前端基础实战系列 《前端基础实战JS/TS与Vue体系化扫盲47 篇完整目录 避坑》前端规范实战系列 《JS/TS/Vue 前端规范实战从写对到写优搞定中后台规范落地打造可维护代码40 篇全目录》前端架构实战系列聚焦工程化、性能优化、可维护架构、中后台体系设计持续更新中前端大厂面试系列覆盖高频考点、手写题、项目深挖、简历与面试技巧规划中每个系列完结后都会整理成一篇完整导航文并附上直达链接方便大家按顺序、体系化学习。全套内容持续更新中敬请期待⬆ 返回目录前端的成长路径很清晰会写代码 → 写规范代码 → 做可扩展架构。每一步都是职业晋升的关键台阶。后续我会持续输出组件化、配置驱动、权限架构、工程化、复杂业务实战干货帮你真正建立架构思维在工作与面试中更有竞争力。觉得有用欢迎点赞 收藏 关注不错过每一篇硬核内容。我是 Eugene与你一起从业务走向架构搞定复杂项目我们下篇干货见