Vue 面试核心知识点总结
Vue 面试核心知识点总结
生命周期详解
Vue 实例从创建到销毁的完整生命周期过程:
-
BeforeCreate
- 数据观测和事件初始化尚未开始
el
和data
都未初始化- 适合添加loading效果
-
Created
- 完成数据观测、属性和方法运算
data
已初始化,但el
仍未显示- 可在此阶段发起异步请求
- 若无
el
选项,生命周期会暂停直到调用vm.$mount(el)
-
BeforeMount
- 模板编译完成,但尚未挂载到DOM
- 相关
render
函数首次被调用 - 完成了
el
和data
初始化
-
Mounted
- 实例已挂载到DOM
- 可访问DOM元素
- 适合执行DOM操作或初始化第三方库
-
BeforeUpdate
- 数据更新前调用
- 发生在虚拟DOM重新渲染和打补丁之前
- 可获取更新前的DOM状态
-
Updated
- 数据更改导致的虚拟DOM重新渲染完成
- 避免在此钩子中修改状态,可能导致无限更新循环
-
BeforeDestroy
- 实例销毁前调用
- 实例仍完全可用
- 适合清除定时器、取消订阅等清理工作
-
Destroyed
- 实例销毁后调用
- 所有事件监听器和子实例已被移除
- 解除了所有响应式依赖
常用指令
v-model
: 双向数据绑定v-bind
: 动态绑定属性v-show
/v-if
: 条件渲染v-for
: 列表渲染v-on
: 事件绑定v-once
: 一次性渲染v-html
: 原始HTML渲染v-slot
: 插槽内容分发
V-model 实现原理
Vue 的双向绑定是通过以下方式实现的:
<input v-bind:value="something" v-on:input="something = $event.target.value">
核心机制:
- 接受一个
value
属性 - 在有新值时触发
input
事件 - 仅适用于
input
,textarea
,select
元素
修饰符:
.lazy
: 将input
事件改为change
事件触发.trim
: 自动过滤首尾空白字符.number
: 将输入转为数字类型
MVVM 架构模式
Model-View-ViewModel 模式解析:
- Model: 数据模型层,包含业务逻辑和数据操作
- View: UI展示层,负责数据可视化
- ViewModel:
- 连接 Model 和 View 的桥梁
- 处理用户交互
- 监听数据变化并更新视图
优势:
- 双向数据绑定自动同步视图和模型
- 解决了传统MVC模式中厚重的Controller问题
- 更好的可测试性和代码组织
组件通信方式
-
父子组件通信
- 父→子:通过
props
传递数据 - 子→父:通过
$emit
触发事件
- 父→子:通过
-
非父子组件通信
- Vuex: 集中式状态管理
- Event Bus: 事件总线模式
- Provide/Inject: 依赖注入
-
高级通信
- Slot: 内容分发
- $attrs/$listeners: 跨级传递
- $parent/$children: 直接访问组件实例
路由模式对比
特性 | Hash模式 | History模式 |
---|---|---|
URL显示 | 带#号 | 普通URL |
实现原理 | window.location.hash | HTML5 History API |
服务器配置 | 无需特殊配置 | 需要后端支持 |
SEO友好度 | 较差 | 较好 |
兼容性 | 所有浏览器 | 需要HTML5支持 |
Vuex 核心概念
graph TD
A[View] -->|Dispatch| B(Actions)
B -->|Commit| C[Mutations]
C -->|Mutate| D[State]
D -->|Render| A
- State: 单一状态树,存储应用级状态
- Getters: 计算属性,用于派生状态
- Mutations: 同步修改状态(唯一途径)
- Actions: 提交 mutations,可包含异步操作
- Modules: 模块化组织复杂状态
性能优化技巧
- Keep-alive: 缓存不活跃组件
- Key属性: 高效更新虚拟DOM
- 懒加载: 异步组件和路由
- 函数式组件: 无状态组件优化
- 生产模式: 移除警告和开发工具
组件传参方式详解
路由传参
- 动态路由匹配
// router.js
{
path: '/user/:id',
component: User
}
// 获取参数
this.$route.params.id
- Query传参
// 传递
this.$router.push({ path: '/user', query: { id: 123 } })
// 获取
this.$route.query.id
- Props解耦
// router.js
{
path: '/user/:id',
component: User,
props: true
}
// 组件中直接使用props接收
props: ['id']
组件间传参
- 父子组件
// 父组件
<child :msg="message" @update="handleUpdate"/>
// 子组件
props: ['msg']
this.$emit('update', newValue)
- 非父子组件
// Event Bus实现
// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()
// 组件A
EventBus.$emit('eventName', data)
// 组件B
EventBus.$on('eventName', data => {})
- Vuex状态管理
// 获取状态
this.$store.state.count
// 提交mutation
this.$store.commit('increment')
// 分发action
this.$store.dispatch('incrementAsync')
计算属性 vs 侦听器
computed(计算属性)
特点:
- 基于依赖缓存
- 只有依赖变化才会重新计算
- 必须有返回值
- 适合同步计算
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
},
// 完整写法
reversedMessage: {
get() { /*...*/ },
set(newValue) { /*...*/ }
}
}
watch(侦听器)
特点:
- 无缓存性
- 可执行异步操作
- 适合数据变化时执行副作用
watch: {
// 简单写法
question(newVal, oldVal) {
// 执行异步操作
},
// 完整写法
someObject: {
handler(newVal, oldVal) {},
deep: true, // 深度监听
immediate: true // 立即执行
}
}
路由跳转方式对比
方式 | 特点 | 使用场景 |
---|---|---|
router.push() |
添加新记录,可回退 | 常规导航 |
router.replace() |
替换当前记录,不可回退 | 登录后跳转等不需要回退的场景 |
router.go(n) |
在历史记录中前进或后退 | 实现前进后退功能 |
location.href |
刷新页面,丢失状态 | 需要完全刷新页面的场景 |
高级特性
动态组件
<component :is="currentComponent"></component>
异步组件
components: {
AsyncComponent: () => import('./AsyncComponent.vue')
}
自定义指令
Vue.directive('focus', {
inserted: function (el) {
el.focus()
}
})
过滤器
filters: {
capitalize: function (value) {
if (!value) return ''
return value.toString().charAt(0).toUpperCase() + value.slice(1)
}
}
性能优化实践
- 组件懒加载
const User = () => import('./User.vue')
- 路由懒加载
{
path: '/dashboard',
component: () => import('./Dashboard.vue')
}
- 函数式组件
Vue.component('functional-button', {
functional: true,
render(h, context) {
return h('button', context.data, context.children)
}
})
- 合理使用v-if和v-show
v-if
:切换开销大,适合不频繁切换的场景v-show
:初始渲染开销大,适合频繁切换的场景
常见问题解决方案
跨域问题
- 开发环境:配置webpack devServer代理
- 生产环境:Nginx反向代理或后端设置CORS
样式隔离
- Scoped CSS
<style scoped>
/* 样式只作用于当前组件 */
</style>
- CSS Modules
<style module>
/* 生成唯一类名 */
</style>
错误处理
- 全局错误捕获
Vue.config.errorHandler = function (err, vm, info) {
// 处理错误
}
- 异步错误捕获
async created() {
try {
await this.fetchData()
} catch (error) {
// 处理错误
}
}
最佳实践建议
- 组件命名:使用PascalCase(如
MyComponent
) - Prop定义:尽量详细指定类型和默认值
props: {
msg: {
type: String,
required: true,
validator: value => value.length > 0
}
}
-
状态管理原则:
- 组件私有状态放在data中
- 组件间共享状态提升到父组件
- 全局共享状态使用Vuex
-
代码组织:
- 单一职责组件
- 按功能组织文件结构
- 合理拆分大型组件