动态组件 vs 插槽.md
# 动态组件 vs 插槽 > 动态组件文档:[Vue 官方文档 - Dynamic Components](https://cn.vuejs.org/guide/essentials/component-basics.html?utm_source=chatgpt.com#dynamic-components) 如下图所示,壳组件(Layout Component)组织了布局与左侧的公共目录部分,而右侧区域是不同的内容,需通过组件注入加载,此时有两种方案:动态组件与插槽  在 Vue 中,组件拆分往往并不是完全独立的模块,更多情况下是“公共结构 + 内容注入”的组合模式。某些组件只负责整体布局、状态管理与公共逻辑,而真正的业务内容则通过插槽或动态组件注入到指定区域中 这种设计方式在后台管理系统、文档系统、详情页、配置页等场景中十分常见,因为页面结构通常高度一致,但中间内容区域却存在明显差异 ## 动态组件 动态组件适合以下场景:右侧内容已经被封装成独立组件,只需要根据不同页面动态切换即可 如下结构: ```txt | |——TOCBase.vue // 壳组件,layout component,只负责布局与左侧公共目录部分 |——TOC // 文件夹 |——EditTOC.vue // 右侧编辑组件 |——DetailTOC.vue // 右侧详情组件 ``` 此时,`TOCBase.vue` 负责: - 页面布局 - 左侧目录 - 滚动联动 - 激活状态 - 公共逻辑 而右侧内容区域则完全交由业务组件负责 由于右侧已经封装为完整组件,因此此时使用动态组件更加适合 新增页: ```vue ``` 详情页: ```vue ``` 通过参数直接传递右侧组件,即可实现不同内容区域的动态切换 这种模式本质上类似: ```txt Layout + 页面内容注入 ``` 壳组件只负责结构,而不关心具体业务内容 ### 动态组件实现 ```vue ``` 在 Vue 中,`:is`(即 `v-bind:is`)主要用于动态组件,它允许根据绑定的响应式变量,在多个组件之间动态切换 例如: ```vue ``` 此时: ```js currentComponent.value = DetailTOC ``` 页面会自动切换为: ```vue ``` 这种方式最大的优势在于: - 解耦业务与布局 - 提高组件复用性 - 页面切换更加灵活 - 更适合中大型项目 尤其在后台系统中,一个统一的布局组件往往会配合多个动态业务组件共同使用 ## 插槽 与动态组件相比,插槽更加适合“局部内容注入” 如果动态组件更像: ```txt 注入一个完整模块 ``` 那么插槽更像: ```txt 向指定区域填充内容 ``` 例如: ```vue 左侧目录 ``` 父组件: ```vue 这里是内容区域 ``` 最终: ```vue ``` 会被替换为: ```vue 这里是内容区域 ``` ### 具名插槽 当页面存在多个可注入区域时,通常会使用具名插槽 例如: ```vue ``` 父组件: ```vue 顶部区域 内容区域 ``` 这种方式相比动态组件更加灵活,因为它允许父组件直接控制局部结构,而不需要额外拆分完整组件 ## 动态组件与插槽的区别 动态组件更适合: - 已封装好的组件 - 独立业务模块 - 页面级组件切换 - 后台管理系统 - 多页面复用布局 插槽更适合: - 局部内容注入 - 灵活结构组合 - 小型内容块 - UI 组件封装 - 通用容器组件 可以理解为: ```txt 动态组件:注入组件 插槽:注入内容 ``` ## Layout Component 的核心思想 无论是动态组件还是插槽,本质上都体现了同一种设计思想: ```txt 结构与内容分离 ``` 布局组件只负责: - 页面结构 - 公共逻辑 - 状态管理 - UI 框架 业务内容则由外部注入 这种模式能够显著提升: - 组件复用率 - 项目可维护性 - 模块解耦能力 - 页面扩展能力 在 Vue 中,大量优秀组件库与后台框架都采用了类似思想,例如: - 表格组件 - Dialog 组件 - Drawer 组件 - Layout 布局组件 - Tabs 组件 它们往往只负责公共结构,而真正的内容则由开发者通过插槽或动态组件注入完成 ## 扩展:动态组件与插槽并不是对立关系,而是经常配合使用 在实际项目开发中,动态组件与插槽并不是二选一的关系。很多场景下,两者往往会同时出现,共同完成布局组件(Layout Component)的设计。 例如,一个典型的双栏布局组件,曾在后台系统中遇到: ```txt ┌──────────┬───────────────────────┐ │ │ │ │ tabs │ │ │ (切换) │ right │ │──────────│ (右侧内容区) │ │ │ │ │ left │ │ │(左侧内容)│ │ │ │ │ └──────────┴───────────────────────┘ ``` 该布局被多个页面复用,其中: - `left`:左侧功能区域 - `right`:右侧业务区域 - `tabs`:可选区域,用于切换左侧内容 此时会发现: - `left` 和 `right` 本质上属于完整业务模块 - `tabs` 只是一个局部控制区域 - `tabs` 与具体页面状态强相关 因此三者并不适合采用同一种注入方式。 ### left 与 right 适合动态组件 对于左右区域而言,它们通常已经被封装为独立业务组件。 例如: ```txt components │ ├── LeftUser.vue ├── LeftRole.vue ├── LeftPermission.vue │ ├── RightAdd.vue ├── RightEdit.vue └── RightDetail.vue ``` 不同页面只需要动态切换对应组件即可: ```vue ``` Layout 内部: ```vue ``` 这种方式非常符合动态组件的设计目标: ```txt 根据不同页面动态切换完整业务模块 ``` Layout 不需要关心具体渲染哪个组件,只负责提供渲染位置。 ### tabs 更适合插槽 相比之下,tabs 的情况有所不同。 很多时候 tabs 只是一个简单的切换器,例如: > Segmented 组件是 ant-design-vue 中的一个分段控制器组件 ```vue ``` 它本身并不是一个独立业务模块,而是当前页面状态的一部分。 例如: ```js const activeTab = ref('user') const currentLeft = computed(() => { return activeTab.value === 'user' ? LeftUser : LeftRole }) ``` 这里的核心状态: ```js activeTab ``` 属于页面本身,而不是 Layout。 如果将 tabs 再单独封装为: ```vue ``` 那么组件之间就会产生额外通信: ```txt Tabs ↓ 通知父组件 ↓ 修改 activeTab ↓ 切换 Left 组件 ``` 原本简单的状态流转变得更加复杂(❌)。 因此,对于这种仅负责局部控制的内容,更适合使用插槽进行注入。 ### Layout 实现:具名插槽 + 动态组件 Layout 只提供一个具名插槽: ```vue ``` Layout 只负责: - 布局结构 - 左右区域渲染 - tabs 插槽占位 并不参与具体业务逻辑。 ### 页面使用示例 新增页面:`add.vue` ```vue ``` 整个状态流转过程如下: ```txt Page │ ├── activeTab │ ├── Segmented │ ├── currentLeft │ └── Layout ├── tabs slot ├── left component └── right component ``` 其中: ```txt Page 负责状态管理 Layout 负责页面结构 Segmented 负责切换状态 动态组件 负责业务内容 ``` 各自职责清晰,互不耦合。 ### 为什么这种设计更合理 从职责角度分析: 动态组件解决的是: ```txt 渲染什么组件 ``` 的问题。 插槽解决的是: ```txt 把内容放到哪里 ``` 的问题。 在当前场景下: ```txt left/right ↓ 完整业务模块 ↓ 动态组件 tabs ↓ 控制组件切换功能 ↓ 具名插槽:将tabs的权限归还给所需使用的页面,不额外控制 ``` 两种机制分别解决不同层面的问题。
以上内容来自博客订阅源,版权归原作者。关注作者后可以在 关注列表 第一时间查看作者文章。

登录后,加入这场讨论。
登录参与