Vue.js 知识总结与面试题
Vue 框架完整梳理:从模板语法、生命周期、组件通信到 Vue Router 与 Vuex 状态管理,并重点讲解 Vue 3 的 Composition API,配代码示例与面试题,适合复习与查阅。
一、Vue 基础概念
1. 什么是 Vue.js?
- 渐进式 JavaScript 框架
- 用于构建用户界面
- 核心库只关注视图层
- 易于上手,便于与第三方库集成
2. Vue 的特点
- 响应式数据绑定:自动更新视图
- 组件化开发:代码复用
- 虚拟 DOM:高效渲染
- 指令系统:简化 DOM 操作
- 单文件组件:HTML、CSS、JS 一体化
3. Vue 版本区别
| 特性 | Vue 2 | Vue 3 |
|---|---|---|
| 响应式 | Object.defineProperty | Proxy |
| 组合式 API | - | Composition API |
| 模板语法 | 相同 | 相同 |
| 性能 | 较好 | 更好 |
| TypeScript 支持 | 一般 | 很好 |
二、Vue 基础语法
1. 模板语法
<!-- 插值表达式 --><span>{{ message }}</span><!-- 指令 --><div v-if="show">条件渲染</div><ul> <li v-for="item in list" :key="item.id">{{ item.name }}</li></ul><!-- 绑定属性 --><img :src="imageUrl" :alt="imageAlt"><!-- 事件绑定 --><button @click="handleClick">点击</button><!-- 双向绑定 --><input v-model="username" placeholder="用户名">2. 计算属性和侦听器
const vm = new Vue({ data() { return { firstName: 'John', lastName: 'Doe' }; }, computed: { fullName() { return `${this.firstName} ${this.lastName}`; } }, watch: { firstName(newVal, oldVal) { console.log(`名字从 ${oldVal} 变为 ${newVal}`); } }});三、Vue 生命周期
1. 生命周期钩子
const vm = new Vue({ data() { return { count: 0 }; }, beforeCreate() { // 实例创建前,data 和 methods 未初始化 }, created() { // 实例创建完成,可访问 data 和 methods }, beforeMount() { // 挂载前,模板已编译但未挂载到 DOM }, mounted() { // 挂载完成,可访问 DOM 元素 }, beforeUpdate() { // 更新前,数据已更新但视图未更新 }, updated() { // 更新完成,视图已更新 }, beforeUnmount() { // 卸载前,清理定时器等 }, unmounted() { // 卸载完成 }});2. 生命周期流程图
beforeCreate → created → beforeMount → mounted ↓ ↓ ↓ 更新 更新 更新 ↓ ↓ ↓beforeUpdate → updated → beforeUnmount → unmounted四、组件化开发
1. 创建组件
<!-- MyComponent.vue --><template> <div class="my-component"> <h3>{{ title }}</h3> <p>{{ content }}</p> <button @click="handleClick">点击</button> </div></template><script>export default { name: 'MyComponent', props: { title: { type: String, required: true }, content: { type: String, default: '' } }, methods: { handleClick() { this.$emit('click', 'Hello from component'); } }};</script><style scoped>.my-component { padding: 20px; border: 1px solid #ccc;}</style>2. 使用组件
<!-- Parent.vue --><template> <div> <MyComponent title="标题" content="内容" @click="handleChildClick" /> </div></template><script>import MyComponent from './MyComponent.vue';export default { components: { MyComponent }, methods: { handleChildClick(message) { console.log(message); } }};</script>五、组件通信
1. 父传子(props)
// 父组件<Child :message="parentMessage" />// 子组件export default { props: ['message']};2. 子传父($emit)
// 子组件this.$emit('customEvent', data);// 父组件<Child @customEvent="handleEvent" />3. 兄弟组件通信
// 使用 eventBusconst eventBus = new Vue();// 组件 A 发送eventBus.$emit('message', data);// 组件 B 接收eventBus.$on('message', (data) => { console.log(data);});4. 跨层级通信(provide/inject)
// 父组件提供数据export default { provide() { return { theme: 'dark' }; }};// 子组件注入数据export default { inject: ['theme']};六、Vue 路由(Vue Router)
1. 基本配置
import Vue from 'vue';import VueRouter from 'vue-router';import Home from './views/Home.vue';import About from './views/About.vue';Vue.use(VueRouter);const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', component: About }, { path: '/user/:id', name: 'User', component: () => import('./views/User.vue') // 懒加载 }];const router = new VueRouter({ mode: 'history', routes});export default router;2. 路由导航
<!-- 声明式导航 --><router-link to="/">首页</router-link><router-link :to="{ name: 'User', params: { id: 123 } }">用户</router-link><!-- 编程式导航 --><script>export default { methods: { goHome() { this.$router.push('/'); }, goUser(id) { this.$router.push({ name: 'User', params: { id } }); } }};</script>3. 路由守卫
// 全局守卫router.beforeEach((to, from, next) => { if (to.meta.requiresAuth && !isLoggedIn) { next('/login'); } else { next(); }});// 路由独享守卫const routes = [ { path: '/admin', component: Admin, beforeEnter: (to, from, next) => { // 检查权限 } }];// 组件内守卫export default { beforeRouteEnter(to, from, next) { // 在渲染组件前调用 }, beforeRouteUpdate(to, from, next) { // 当前路由改变但组件复用 }, beforeRouteLeave(to, from, next) { // 离开当前路由 }};七、状态管理(Vuex)
1. Vuex 核心概念
import Vue from 'vue';import Vuex from 'vuex';Vue.use(Vuex);const store = new Vuex.Store({ state: { count: 0, user: null }, mutations: { increment(state) { state.count++; }, setUser(state, user) { state.user = user; } }, actions: { async fetchUser({ commit }) { const user = await fetch('/api/user'); commit('setUser', user); } }, getters: { doubleCount(state) { return state.count * 2; } }, modules: { // 模块化 }});export default store;2. 在组件中使用 Vuex
export default { computed: { count() { return this.$store.state.count; }, doubleCount() { return this.$store.getters.doubleCount; } }, methods: { increment() { this.$store.commit('increment'); }, fetchUser() { this.$store.dispatch('fetchUser'); } }};八、Vue 3 Composition API
1. setup 函数
import { ref, reactive, computed, watch, onMounted } from 'vue';export default { setup() { // 响应式数据 const count = ref(0); const user = reactive({ name: 'Alice', age: 25 }); // 计算属性 const doubleCount = computed(() => count.value * 2); // 方法 const increment = () => { count.value++; }; // 侦听器 watch(count, (newVal, oldVal) => { console.log(`count changed from ${oldVal} to ${newVal}`); }); // 生命周期钩子 onMounted(() => { console.log('Component mounted'); }); return { count, user, doubleCount, increment }; }};2. ref 和 reactive 的区别
| 特性 | ref | reactive |
|---|---|---|
| 适用类型 | 基本类型 | 对象类型 |
| 访问方式 | .value |
直接访问 |
| 响应式深度 | 自动深层 | 自动深层 |
九、常见面试题
基础概念
Q1:Vue 的响应式原理是什么?
- Vue 2 使用
Object.defineProperty劫持对象属性 - Vue 3 使用
Proxy代理对象 - 数据变化时触发依赖更新,自动更新视图
Q2:Vue 和 React 的区别?
- Vue 使用模板语法,React 使用 JSX
- Vue 响应式自动更新,React 需要 setState
- Vue 更易上手,React 更灵活
Q3:Vue 的生命周期钩子有哪些?
- 创建阶段:beforeCreate、created
- 挂载阶段:beforeMount、mounted
- 更新阶段:beforeUpdate、updated
- 卸载阶段:beforeUnmount、unmounted
组件通信
Q4:Vue 组件间通信有哪些方式?
- 父传子:props
- 子传父:$emit
- 兄弟通信:eventBus、Vuex
- 跨层级:provide/inject、Vuex
Q5:v-if 和 v-show 的区别?
- v-if:条件不满足时元素不存在
- v-show:条件不满足时元素隐藏(display: none)
- v-if 适合条件很少改变的场景,v-show 适合频繁切换
路由
Q6:Vue Router 有哪些导航方式?
- 声明式:
<router-link> - 编程式:
this.$router.push()、replace()、go()
Q7:路由守卫有哪些类型?
- 全局守卫:beforeEach、afterEach
- 路由独享守卫:beforeEnter
- 组件内守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
Vuex
Q8:Vuex 的核心概念有哪些?
- state:状态
- mutations:同步修改状态
- actions:异步操作
- getters:派生状态
- modules:模块化
Q9:为什么 mutations 必须是同步的?
- mutations 是状态变更的唯一方式
- 同步操作便于追踪状态变化
- devtools 需要记录每一次状态变更
Vue 3
Q10:Vue 3 的 Composition API 有什么优势?
- 更好的代码组织
- 更好的逻辑复用
- 更好的类型推断
- 更小的打包体积
Q11:ref 和 reactive 的区别?
- ref 用于基本类型,通过
.value访问 - reactive 用于对象类型,直接访问属性
进阶问题
Q12:什么是虚拟 DOM?
- 用 JavaScript 对象描述 DOM 结构
- 通过 diff 算法比较新旧虚拟 DOM
- 最小化实际 DOM 操作,提高性能
Q13:Vue 的 diff 算法是怎样的?
- 同层比较,不跨层级
- 先比较节点类型,类型不同直接替换
- 使用 key 优化列表渲染
Q14:如何优化 Vue 应用性能?
- 使用 v-show 代替 v-if(频繁切换)
- 使用 computed 缓存计算结果
- 使用 v-for 时加 key
- 懒加载组件
- 使用 keep-alive 缓存组件
Q15:Vue 中如何处理异步请求?
- 在 created 或 mounted 钩子中发起请求
- 使用 async/await 处理异步
- 使用 Vuex actions 统一管理异步操作
Q16:什么是 mixin?
- 可复用的组件选项对象
- 用于提取公共逻辑
- Vue 3 推荐使用 Composition API 代替
Q17:Vue 中如何实现双向绑定?
- 使用 v-model 指令
- 本质是 :value 和 @input 的语法糖
Q18:Vue 的 nextTick 是什么?
- 在下次 DOM 更新循环结束后执行回调
- 用于在更新后访问 DOM
this.$nextTick(() => { // DOM 已更新});Q19:Vue 中如何监听数组变化?
- Vue 重写了数组的 7 个方法:push、pop、shift、unshift、splice、sort、reverse
- 通过这些方法修改数组会触发响应式更新
- 直接修改数组索引不会触发更新
Q20:Vue 3 的响应式系统相比 Vue 2 有什么改进?
- 使用 Proxy 代替 Object.defineProperty
- 支持数组索引和长度变化
- 支持动态添加/删除对象属性
- 性能更好
十、最佳实践
- 组件划分:按功能划分组件,保持组件单一职责
- props 验证:为组件 props 添加类型验证和默认值
- 事件命名:使用 kebab-case 命名自定义事件
- 避免直接操作 DOM:使用数据驱动视图
- 使用计算属性:缓存计算结果,避免重复计算
- 路由懒加载:按需加载组件,优化首屏性能
十一、总结
Vue.js 是一个优秀的渐进式框架,掌握其核心概念、组件化开发、路由和状态管理是成为优秀前端开发者的关键。