Vue2面试核心问题

目录


基础概念

1. Vue的核心特性是什么?

参考答案:

特性 说明
响应式数据绑定 数据变化自动更新视图
组件系统 组件化开发,可复用
指令系统 v-if/v-for/v-bind/v-on等
虚拟DOM 高效的DOMdiff算法
单文件组件 .vue文件包含模板、逻辑、样式

2. Vue2和Vue3的区别?

参考答案:

对比项 Vue2 Vue3
响应式原理 Object.defineProperty Proxy
组件根节点 必须一个根元素 支持多根节点(Fragment)
API风格 Options API Composition API + Options API
生命周期 beforeCreate/created等 onBeforeMount/onMounted等
TypeScript partial support 完整支持
打包体积 ~20KB ~10KB
性能 一般 优化后更好

3. MVVM模式是什么?Vue遵循了吗?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌─────────────────────────────────────────────────────────────┐
│ MVVM 模式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ View │ <--> │ViewModel│ <--> │ Model │ │
│ │ 视图 │ │ 视图模型 │ │ 模型 │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │
│ │ 模板语法 │ 数据绑定 │ 数据定义 │
│ │ │ │ │
│ └─────────────────┴──────────────────┘ │
│ │
│ Model: data/props/computed │
│ View: template │
│ ViewModel: methods/watch │
└─────────────────────────────────────────────────────────────┘

Vue实例作为ViewModel,连接View和Model,实现双向数据绑定。

4. Vue实例有哪些属性?

参考答案:

属性 说明
$data 组件实例观察的数据对象
$props 当前组件接收的props
$el Vue实例使用的根DOM元素
$options 当前Vue实例的初始化选项
$parent 父实例
$root 当前组件树的根实例
$children 当前实例的直接子组件
$refs DOM元素和组件实例的引用
$slots 插槽内容
$scopedSlots 作用域插槽
$attrs 非props属性

组件相关

5. 组件通信方式有哪些?

参考答案:

方式 适用场景 实现方式
props/$emit 父子通信 props向下,$emit向上
$refs 父访问子 this.$refs.child.method()
$children 父访问子 this.$children[0].method()
$parent 子访问父 this.$parent.method()
eventBus 兄弟/跨级通信 emit/on
provide/inject 跨级通信 provide/inject
Vuex/Pinia 全局状态 store

6. props有哪些配置项?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
props: {
// 基础类型
name: String,

// 多个类型
value: [String, Number],

// 必填
required: {
type: String,
required: true
},

// 默认值
age: {
type: Number,
default: 18
},

// 对象/数组默认值
items: {
type: Array,
default() {
return []
}
},

// 自定义验证
level: {
validator(value) {
return [1, 2, 3].includes(value)
}
}
}

7. v-model的原理?

参考答案:

1
2
3
4
5
<!-- 原生v-model -->
<input v-model="message">

<!-- 等价于 -->
<input :value="message" @input="message = $event.target.value">

对于组件:

1
2
3
4
5
<!-- 组件v-model -->
<custom-input v-model="message">

<!-- 等价于 -->
<custom-input :value="message" @input="message = $event">

8. slot的作用域插槽原理?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- Child.vue -->
<template>
<slot :item="item" :index="index"></slot>
</template>

<!-- Parent.vue -->
<template>
<child>
<template #default="{ item, index }">
<div>{{ index }}: {{ item.name }}</div>
</template>
</child>
</template>

子组件通过slot将数据传递给父组件,父组件通过解构获取数据。

9. 动态组件是什么?如何实现?

参考答案:

1
2
3
4
5
6
7
<!-- 使用component + is -->
<component :is="currentComponent"></component>

<!-- keep-alive缓存 -->
<keep-alive include="Home,About">
<component :is="currentComponent"></component>
</keep-alive>

响应式原理

10. Vue2响应式原理是什么?

参考答案:

Vue2使用Object.defineProperty实现响应式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 源码简化
function defineReactive(obj, key, val) {
const dep = new Dep()

Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
// 依赖收集
if (Dep.target) {
dep.depend()
}
return val
},
set(newValue) {
if (val === newValue) return
val = newValue
// 通知更新
dep.notify()
}
})
}

11. Object.defineProperty的缺点?

参考答案:

缺点 说明
无法监听新增属性 需要Vue.set
无法监听删除属性 需要Vue.delete
无法监听数组索引 需要Vue.set或splice
需要递归 深层对象需要递归处理
无法监听Map/Set 不支持新的数据结构

12. $nextTick的原理?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 原理:异步队列 + promise/mutationObserver/setTimeout
this.$nextTick(() => {
// DOM已更新
})

// 源码简化
function nextTick(cb) {
callbacks.push(cb)

if (!pending) {
pending = true
Promise.resolve().then(flushCallbacks)
}
}

function flushCallbacks() {
callbacks.forEach(cb => cb())
pending = false
}

13. 数组响应式如何实现?

参考答案:

Vue2对数组方法进行了拦截:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 变异方法 - 会触发视图更新
const methods = [
'push', 'pop', 'shift', 'unshift',
'splice', 'sort', 'reverse'
]

methods.forEach(method => {
original[method] = function(...args) {
const result = original.apply(this, args)
dep.notify()
return result
}
})

直接通过索引修改数组不会触发更新:

1
2
3
4
5
6
7
// 不响应
this.items[0] = newValue

// 响应
this.$set(this.items, 0, newValue)
// 或
this.items.splice(0, 1, newValue)

14. Watch和Computed的区别?

参考答案:

特性 Watch Computed
用途 监听数据变化执行操作 依赖数据计算新值
返回值 不必须有 必须有返回值
缓存 有(基于响应式依赖)
执行时机 被动触发 主动求值
适用场景 异步操作、副操作 同步计算

路由相关

15. Vue Router有哪几种模式?

参考答案:

模式 原理 URL表现 服务器配置
hash location.hash http://xxx/#/home 不需要
history history.pushState http://xxx/home 需要配置
abstract 内存 无感知 N/A
1
2
3
4
5
// history模式需要服务器配置
// nginx配置
location / {
try_files $uri $uri/ /index.html;
}

16. 导航守卫有哪些?

参考答案:

守卫 说明
beforeEach 全局前置守卫
beforeResolve 全局解析守卫
afterEach 全局后置守卫
beforeEnter 路由独享守卫
beforeRouteEnter 组件内守卫 - 进入前
beforeRouteUpdate 组件内守卫 - 路由变化
beforeRouteLeave 组件内守卫 - 离开前

17. 路由懒加载如何实现?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 方式1: 箭头函数 + import
{
path: '/home',
component: () => import('./Home.vue')
}

// 方式2: webpack的require
{
path: '/home',
component: resolve => require(['./Home.vue'], resolve)
}

// 方式3: 命名chunk
{
path: '/home',
component: () => import(/* webpackChunkName: "home" */ './Home.vue')
}

状态管理

18. Vuex的核心概念?

参考答案:

概念 说明
state 状态定义
getters 状态计算属性
mutations 同步修改状态
actions 异步操作
modules 模块化管理

19. Vuexmutation和action的区别?

参考答案:

特性 mutation action
同步/异步 同步 异步
修改state 直接修改 通过mutation修改
事务调试 可追踪 异步难以追踪
参数 state + payload context + payload

20. Vuex数据流程?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌─────────────────────────────────────────────────────────────┐
│ Vuex 数据流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Component │
│ │ │
│ │ dispatch('action') │
│ ▼ │
│ Action ───────────────────────────────────────────────┐ │
│ │ │ │
│ │ async operation │ │
│ ▼ │ │
│ Mutation ◄──────────────────────────────────────────────┘ │
│ │ │
│ │ commit('mutation') │
│ ▼ │
│ State │
│ │ │
│ │ reactive │
│ ▼ │
│ Component │
│ │
└─────────────────────────────────────────────────────────────┘

性能优化

21. Vue性能优化方法?

参考答案:

优化点 方法
代码分割 路由懒加载、组件懒加载
减少重排重绘 使用transform/opacity
列表key 使用唯一id而非index
合理使用响应式 不需要响应式的用Object.freeze
keep-alive 缓存不活跃组件
v-show vs v-if 频繁切换用v-show
事件销毁 及时清理定时器/监听
函数式组件 无状态用functional

22. 为什么列表需要key?

参考答案:

  1. 快速定位: key帮助Vue追踪每个节点,实现就地复用
  2. 保持状态: 列表顺序变化时,key确保组件状态正确
  3. 提高性能: 减少DOM操作,提高渲染性能
1
2
3
4
5
<!-- 错误示例 -->
<li v-for="(item, index) in items" :key="index">

<!-- 正确示例 -->
<li v-for="item in items" :key="item.id">

23. v-if和v-show的区别?

参考答案:

特性 v-if v-show
原理 动态添加/删除DOM 切换display
初始渲染 条件为false不渲染 条件为false渲染但隐藏
切换开销 高(DOM操作) 低(CSS切换)
适用场景 运行时很少改变 频繁切换

24. keep-alive的原理?

参考答案:

  1. 缓存机制: 使用ache对象存储已缓存的组件实例
  2. 生命周期: 提供activated/deactivated钩子
  3. 最大缓存: 通过max属性限制缓存数量
1
2
3
<keep-alive :max="10" :include="['Home', 'About']">
<component :is="currentComponent"></component>
</keep-alive>

实际应用

25. 如何实现一个权限控制按钮?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. 定义权限指令
Vue.directive('permission', {
inserted(el, binding) {
const { value } = binding
const userPermissions = store.state.user.permissions || []

if (!userPermissions.includes(value)) {
el.parentNode.removeChild(el)
}
}
})

// 2. 使用
<button v-permission="'user:delete'">删除</button>

26. 如何实现一个表单验证?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 验证规则
const rules = {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
{ min: 2, max: 10, message: '名称长度2-10', trigger: 'blur' }
],
email: [
{ type: 'email', message: '邮箱格式错误', trigger: 'blur' }
]
}

// 使用
<el-form :model="form" :rules="rules">
<el-form-item prop="name">
<el-input v-model="form.name"></el-input>
</el-form-item>
</el-form>

27. 如何封装一个请求?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// request.js
import axios from 'axios'
import { Message } from 'element-ui'

const service = axios.create({
baseURL: process.env.VUE_APP_API,
timeout: 10000
})

service.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})

service.interceptors.response.use(
response => {
const { code, data, message } = response.data
if (code !== 0) {
Message.error(message)
return Promise.reject(response.data)
}
return data
},
error => {
if (error.response.status === 401) {
// 登录过期
}
return Promise.reject(error)
}
)

export default service

28. 如何排查Vue应用性能问题?

参考答案:

  1. Vue DevTools: 使用Timeline查看组件渲染性能
  2. Chrome DevTools: Performance面板分析
  3. Vue Performance: vue-addons性能检测
  4. Vue.config.performance: 开启性能追踪

总结

Vue2核心知识图谱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌─────────────────────────────────────────────────────────────┐
│ Vue2 核心知识图谱 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 基础 │
│ ├── 模板语法 (插值/指令/事件) │
│ ├── 计算属性和侦听器 │
│ └── 过滤器 │
│ │
│ 组件 │
│ ├── 组件通信 (props/$emit/$refs/eventBus) │
│ ├── 插槽 (默认/具名/作用域) │
│ ├── 动态组件 (component + keep-alive) │
│ └── 生命周期 │
│ │
│ 原理 │
│ ├── 响应式原理 (Object.defineProperty) │
│ ├── 虚拟DOM (Snabbdom/VDOM diff) │
│ └── 异步更新 ($nextTick) │
│ │
│ 生态 │
│ ├── Vue Router (导航守卫/懒加载) │
│ └── Vuex (state/getters/mutations/actions) │
│ │
└─────────────────────────────────────────────────────────────┘

相关链接