1. 什么是 Vue 组件
Vue 组件本质上是一个 可复用的 UI 和逻辑单元,它把模板、数据、行为、样式等封装在一起,用来构建页面。
组件化的好处
- 提高复用性
- 提高可维护性
- 提高开发效率
- 方便拆分复杂页面
2. 常见组件通信方式
props:父传子emit:子传父v-model:父子之间核心状态同步provide / inject:跨层级通信ref + defineExpose:父组件主动调用子组件方法Pinia / Vuex:全局状态共享slot:内容分发
3. props 高频回答
props 是什么
props 是父组件向子组件传递数据的方式。
为什么是单向数据流
因为数据只能从父组件流向子组件,子组件只能使用,不能直接修改。
为什么不能直接修改 props
因为 props 的所有权在父组件。如果子组件直接修改:
- 会违背单向数据流
- 父组件重新渲染时可能会覆盖掉子组件修改
- 容易造成状态混乱
子组件想改怎么办
- 把
props拷贝成内部状态再改 - 通过
emit通知父组件修改
4. emit 高频回答
emit 的本质是:
子组件触发一个自定义事件,把数据或变更意图传给父组件,父组件监听后决定是否修改状态。
它不是子组件直接改父组件数据,而是 通知父组件去改。
5. v-model 高频回答
v-model 本质上是 props + emit 的语法糖。
Vue2
valueinput
Vue3
modelValueupdate:modelValue
适用场景
当组件有一个核心状态需要和父组件保持同步时,适合用 v-model。
例如:
- 输入框值
- 开关状态
- 弹窗显示隐藏
6. 插槽 slot 高频回答
什么是插槽
插槽是 Vue 提供的 内容分发机制,子组件通过 slot 预留位置,父组件在使用时把内容插入进去。
插槽的作用
- 提高组件灵活性
- 结构复用,内容可定制
具名插槽
给插槽起名字,用来区分多个插槽位置。
作用域插槽
子组件把内部数据作为 slot props 传给父组件,父组件接收这些数据后决定怎么渲染。
一句话总结:
作用域插槽不是父组件直接访问子组件内部数据,而是子组件主动把数据传给父组件。
7. provide / inject 高频回答
provide / inject 是 Vue 提供的跨层级通信方式。
适用场景
- 祖先组件给深层后代传值
- 避免一层层 props 透传
- 表单组件、主题组件上下文共享
优点
- 减少中间层传值
- 适合深层嵌套组件通信
缺点
- 依赖关系不够显式
- 不如 props 清晰
是否响应式
provide / inject 本身不是自动响应式机制,是否响应式取决于提供的数据本身是不是响应式对象,比如 ref 或 reactive。
8. keep-alive 高频回答
keep-alive 是 Vue 的内置组件,用来缓存动态组件或路由组件实例,避免组件切换时被重复销毁和创建。
作用
- 保留组件状态
- 减少重复挂载
- 提升切换性能
场景
- tab 切换
- 动态组件切换
- 路由返回后保留页面状态
多出的生命周期
activateddeactivated
使用不当的问题
- 内存占用增加
- 旧状态残留
- 副作用未清理
9. 异步组件 高频回答
异步组件是指组件不会在初始化时立即加载,而是在真正渲染时才加载。
作用
- 减少首屏资源体积
- 优化首屏性能
Vue3 写法
1const AsyncComp = defineAsyncComponent(() => import("./AsyncComp.vue"));和路由懒加载的区别
- 异步组件:组件级别懒加载
- 路由懒加载:页面级别懒加载
10. 动态组件 高频回答
动态组件指的是:
在同一个挂载位置,根据条件动态切换不同组件进行渲染。
实现方式
1<component :is="currentComponent" />和 v-if / v-show 的区别
- 动态组件:切换不同组件
v-if:控制是否创建和销毁v-show:控制显示隐藏,不销毁
常搭配什么使用
通常搭配 keep-alive 使用,因为动态组件切换时默认会销毁实例,而 keep-alive 可以缓存组件状态。
11. 生命周期执行顺序 高频回答
挂载时
- 父
created - 子
created - 子
mounted - 父
mounted
总结:
先创建父,再创建子;先挂载子,再挂载父。
更新时
- 父
beforeUpdate - 子
beforeUpdate - 子
updated - 父
updated
总结:
父先开始更新,子先更新完成,父最后
updated。
12. $attrs 和 inheritAttrs 高频回答
$attrs 是什么
$attrs 表示父组件传进来、但没有被当前组件通过 props 或 emits 显式声明接收的属性集合。
inheritAttrs: false 的作用
阻止这些属性默认挂到组件根元素上,从而让开发者手动决定把它们绑定到哪个元素。
为什么封装组件经常用到
因为封装组件时,不可能把所有原生属性都写成 props,使用 $attrs 可以把额外属性继续透传给内部元素或子组件,提高灵活性。
13. ref + defineExpose 高频回答
ref 在 DOM 上和组件上的区别
- 用在 DOM 上:拿到真实 DOM 节点
- 用在组件上:拿到组件实例
为什么配合 defineExpose
因为 <script setup> 中内部变量和方法默认不会暴露给父组件,所以需要通过 defineExpose 显式暴露。
适用场景
- 打开/关闭弹窗
- 表单校验
- 输入框聚焦
为什么不能滥用
因为会增加父子组件耦合,不适合作为常规数据通信方案。
14. 组件设计思想 高频回答
为什么要高内聚、低耦合
因为这样可以让组件职责更单一、边界更清晰,提高复用性、可维护性和扩展性。
一个好的 Vue 组件应该具备哪些特点
- 职责单一
- 接口清晰
- 可复用
- 易维护
- 易扩展
- 状态边界明确
15. Dialog 封装题速背
常见 props
modelValuetitlewidthshowClosecloseOnClickModalcloseOnPressEscape
常见事件
update:modelValueopencloseconfirmcancel
常见插槽
header- 默认插槽
footer
显示隐藏怎么设计
一般采用 v-model:
1<Dialog v-model="visible" />本质等价于:
1<Dialog :modelValue="visible" @update:modelValue="visible = $event" />子组件通过 props 接收 modelValue,通过 emit("update:modelValue", false) 通知父组件关闭。
16. data、props、computed、watch 怎么分
props
适合父组件传入、当前组件只使用不拥有的数据。
内部状态
适合只服务于当前组件实现的局部状态,比如:
- 聚焦状态
- 展开收起
- loading
computed
适合做 派生数据,根据已有状态计算新值,有缓存。
watch
适合做 副作用逻辑,比如:
- 发请求
- 做校验
- 同步存储
- 操作 DOM
为什么要分清谁拥有数据
因为这样可以让数据流更清晰,避免多个地方同时维护同一份状态,减少混乱。
17. props + emit、v-model、ref 的适用场景
props + emit
适合最常规的父子通信。
v-model
适合组件某个核心状态需要和父组件保持同步的场景。
ref
适合命令式调用子组件方法,不适合做常规数据传递。
18. 最后总背诵
Vue 组件本质上是可复用的 UI 和逻辑单元。常见通信方式有 props、emit、v-model、provide/inject、ref + defineExpose、Pinia,以及 slot。props 适合父传子,emit 适合子传父,v-model 适合组件核心状态同步,provide/inject 适合跨层级通信,ref 适合命令式调用子组件方法。组件设计时要注意高内聚、低耦合,尽量做到职责单一、接口清晰、状态边界明确,这样组件才更容易复用、维护和扩展。