全栈手账 订阅 RSS
Vue

Vue.js 知识总结与面试题

Vue 框架完整梳理:从模板语法、生命周期、组件通信到 Vue Router 与 Vuex 状态管理,并重点讲解 Vue 3 的 Composition API,配代码示例与面试题,适合复习与查阅。

读了 17 分钟 更新于 8 月 5 日

一、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. 模板语法

html
<!-- 插值表达式 --><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. 计算属性和侦听器

javascript
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. 生命周期钩子

javascript
const vm = new Vue({    data() {        return { count: 0 };    },    beforeCreate() {        // 实例创建前,data 和 methods 未初始化    },    created() {        // 实例创建完成,可访问 data 和 methods    },    beforeMount() {        // 挂载前,模板已编译但未挂载到 DOM    },    mounted() {        // 挂载完成,可访问 DOM 元素    },    beforeUpdate() {        // 更新前,数据已更新但视图未更新    },    updated() {        // 更新完成,视图已更新    },    beforeUnmount() {        // 卸载前,清理定时器等    },    unmounted() {        // 卸载完成    }});

2. 生命周期流程图

code
beforeCreate → created → beforeMount → mounted       ↓              ↓              ↓      更新            更新            更新       ↓              ↓              ↓beforeUpdate → updated → beforeUnmount → unmounted

四、组件化开发

1. 创建组件

vue
<!-- 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. 使用组件

vue
<!-- 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)

javascript
// 父组件<Child :message="parentMessage" />// 子组件export default {  props: ['message']};

2. 子传父($emit)

javascript
// 子组件this.$emit('customEvent', data);// 父组件<Child @customEvent="handleEvent" />

3. 兄弟组件通信

javascript
// 使用 eventBusconst eventBus = new Vue();// 组件 A 发送eventBus.$emit('message', data);// 组件 B 接收eventBus.$on('message', (data) => {  console.log(data);});

4. 跨层级通信(provide/inject)

javascript
// 父组件提供数据export default {  provide() {    return {      theme: 'dark'    };  }};// 子组件注入数据export default {  inject: ['theme']};

六、Vue 路由(Vue Router)

1. 基本配置

javascript
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. 路由导航

html
<!-- 声明式导航 --><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. 路由守卫

javascript
// 全局守卫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 核心概念

javascript
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

javascript
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 函数

javascript
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
javascript
this.$nextTick(() => {  // DOM 已更新});

Q19:Vue 中如何监听数组变化?

  • Vue 重写了数组的 7 个方法:push、pop、shift、unshift、splice、sort、reverse
  • 通过这些方法修改数组会触发响应式更新
  • 直接修改数组索引不会触发更新

Q20:Vue 3 的响应式系统相比 Vue 2 有什么改进?

  • 使用 Proxy 代替 Object.defineProperty
  • 支持数组索引和长度变化
  • 支持动态添加/删除对象属性
  • 性能更好

十、最佳实践

  1. 组件划分:按功能划分组件,保持组件单一职责
  2. props 验证:为组件 props 添加类型验证和默认值
  3. 事件命名:使用 kebab-case 命名自定义事件
  4. 避免直接操作 DOM:使用数据驱动视图
  5. 使用计算属性:缓存计算结果,避免重复计算
  6. 路由懒加载:按需加载组件,优化首屏性能

十一、总结

Vue.js 是一个优秀的渐进式框架,掌握其核心概念、组件化开发、路由和状态管理是成为优秀前端开发者的关键。

前端、后端、部署、客服都是我一个人。写这里的东西都是真踩过的坑,不是抄来的教程。