Vue 基础理论 & API 使用
本文主要记录
Vue的基础理论、核心概念与常用 API 使用方法,面向面试和日常开发参考。
原文地址
Vue 简介
Vue 是一个渐进式 JavaScript 框架,由尤雨溪于 2014 年创建。Vue 核心库聚焦于视图层,易于学习和集成,同时能够驱动复杂的单页应用程序(SPA)开发。
核心特点:
- 响应式数据绑定 (MVVM 模式)
- 组件化开发
- 虚拟 DOM
- 指令系统
- 渐进式架构
安装与项目创建
Vite(推荐)
bash
# 创建 Vue3 项目
npm create vue@latest
# 或使用 Vite 直接创建
npm create vite@latest my-vue-app -- --template vue
Vue CLI
bash
npm install -g @vue/cli
vue create my-project
基础指令
v-model 双向绑定
v-model 是 Vue 中用于表单输入和数据双向绑定的核心指令,本质是 v-bind + v-on 的语法糖。
基本用法:
html
<input v-model="message">
<p>{{ message }}</p>
修饰符:
| 修饰符 | 说明 |
|---|---|
.lazy |
在 change 事件时更新,而非 input |
.number |
自动转换为数值 |
.trim |
去除首尾空白 |
自定义 v-model(Vue 3.4+):
javascript
// 子组件
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
// 父组件
<MyInput v-model:title="title" />
v-if / v-show 条件渲染
| 特性 | v-if | v-show |
|---|---|---|
| DOM 操作 | 创建/销毁 | display: none |
| 初始渲染 | 惰性 | 立即渲染 |
| 切换性能 | 低 | 高 |
| 适用场景 | 很少切换 | 频繁切换 |
html
<div v-if="type === 'A'">A</div>
<div v-else-if="type === 'B'">B</div>
<div v-else>C</div>
v-for 列表渲染
html
<li v-for="(item, index) in items" :key="item.id">
{{ index }} - {{ item.name }}
</li>
注意事项:
- 必须使用
:key绑定唯一标识 - 不建议使用数组索引作为 key
- Vue2 中 v-for 优先级高于 v-if,Vue3 中相反
v-bind / v-on 属性与事件
html
<!-- 绑定属性 -->
<img :src="url">
<!-- 绑定多个属性 -->
<img v-bind="attrs">
<!-- 事件监听 -->
<button @click="handleClick">Click</button>
<!-- 事件修饰符 -->
<button @click.stop="handle">阻止冒泡</button>
<button @click.prevent="handle">阻止默认行为</button>
组件选项
data
组件的响应式数据源,必须返回纯对象:
javascript
export default {
data() {
return {
count: 0,
user: { name: 'Vue' }
}
}
}
props
父子组件通信的重要方式,支持类型校验 和默认值:
javascript
export default {
props: {
// 基础类型
title: String,
// 多个类型
age: [Number, String],
// 带默认值
size: {
type: String,
default: 'medium'
},
// 必需
id: {
type: Number,
required: true
},
// 自定义校验
score: {
validator: (value) => value >= 0 && value <= 100
}
}
}
Vue3 组合式 API:
javascript
const props = defineProps({
title: String,
count: { type: Number, default: 0 }
})
computed 计算属性
缓存计算结果,只在依赖变化时重新计算:
javascript
export default {
data() { return { count: 1 } },
computed: {
// 只读
doubled() { return this.count * 2 },
// 可写
plusOne: {
get() { return this.count + 1 },
set(val) { this.count = val - 1 }
}
}
}
methods 方法
处理业务逻辑,每次渲染都会重新创建:
javascript
export default {
methods: {
handleClick() { /* ... */ }
}
}
watch 监听器
监听数据变化并执行回调:
javascript
export default {
data() { return { count: 0 } },
watch: {
count(newVal, oldVal) {
console.log(`变化: ${oldVal} → ${newVal}`)
},
// 深度监听
'obj.data': {
handler() { /* ... */ },
deep: true
},
// 立即执行
name: {
handler() { /* ... */ },
immediate: true
}
}
}
Composition API
Vue3 引入的组合式 API,提供了更灵活的逻辑组织方式。
ref / reactive 响应式
javascript
import { ref, reactive } from 'vue'
// ref - 原始类型
const count = ref(0)
count.value++
// reactive - 对象
const state = reactive({
user: { name: 'Vue' }
})
state.user.name = 'Vue3'
区别:
| 特性 | ref | reactive | | ----- -| ----- | ---------- | | 适用类型 | 任意类型 | 对象/数组 | | 访问方式 | .value | 直接属性 | | 重新赋值 | 响应式 | 替换整个对象 |
toRefs / toRef
将 reactive 对象解构为独立的 ref:
javascript
import { reactive, toRefs } from 'vue'
const state = reactive({ name: 'Vue', age: 25 })
const { name, age } = toRefs(state)
// 或创建单个 ref
const nameRef = toRef(state, 'name')
computed() 计算属性
javascript
import { ref, computed } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
watch / watchEffect
javascript
import { ref, watch, watchEffect } from 'vue'
// watch - 显式监听
watch(count, (newVal, oldVal) => { /* ... */ })
watch(() => state.name, (newVal) => { /* ... */ })
// watchEffect - 自动收集依赖
watchEffect(() => {
console.log(count.value) // 自动追踪
})
执行时机控制:
watch:默认同步执行watchEffect:默认 pre(在组件更新前)watchPostEffect:在组件更新后执行watchSyncEffect:同步执行
生命周期钩子
javascript
import {
onMounted,
onUpdated,
onUnmounted
} from 'vue'
export default {
setup() {
onMounted(() => { console.log('mounted') })
onUpdated(() => { console.log('updated') })
onUnmounted(() => { console.log('unmounted') })
}
}
组件通信
Props / $emit
javascript
// 父组件
<Child :count="count" @update="handleUpdate" />
// 子组件
const props = defineProps({ count: Number })
const emit = defineEmits(['update'])
emit('update', props.count + 1)
Provide / Inject
祖先向后代跨级传值:
javascript
// 祖先组件
provide('key', 'value')
// 后代组件
const value = inject('key')
响应式:
javascript
// 祖先
const count = ref(0)
provide('count', count)
// 后代 - 修改会影响所有后代
const count = inject('count')
<math xmlns="http://www.w3.org/1998/Math/MathML"> a t t r s / attrs / </math>attrs/listeners
透传属性和事件:
html
<!-- 透传所有 -->
<Child v-bind="$attrs" v-on="$listeners" />
Pinia 状态管理
Vue3 推荐的状态管理方案:
javascript
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
doubled: (state) => state.count * 2
},
actions: {
increment() { this.count++ }
}
})
内置组件
Transition
为元素添加过渡动画:
html
<Transition name="fade">
<div v-if="show">Content</div>
</Transition>
css
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
过渡类名:
v-enter-from/v-leave-from:起始状态v-enter-active/v-leave-active:过渡中v-enter-to/v-leave-to:结束状态
KeepAlive
缓存组件实例:
html
<KeepAlive include="A,B" exclude="C">
<component :is="current" />
</KeepAlive>
生命周期:
activated:激活时deactivated:停用时
Teleport
渲染到指定 DOM 位置:
html
<Teleport to="#modal-root">
<div class="modal">Content</div>
</Teleport>
Suspense
处理异步组件(实验性):
html
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
Loading...
</template>
</Suspense>
生命周期
Options API
| 阶段 | 钩子 | 说明 |
|---|---|---|
| 初始化 | beforeCreate | 实例创建前 |
| 初始化 | created | 数据观测完成 |
| 挂载 | beforeMount | 模板编译完成 |
| 挂载 | mounted | DOM 挂载完成 |
| 更新 | beforeUpdate | 数据变化,DOM 未更新 |
| 更新 | updated | DOM 更新完成 |
| 销毁 | beforeUnmount | 实例销毁前 |
| 销毁 | unmounted | 实例已销毁 |
父子组件执行顺序
挂载:父 created → 子 created → 子 mounted → 父 mounted
更新:父 beforeUpdate → 子 beforeUpdate → 子 updated → 父 updated
销毁:父 beforeUnmount → 子 beforeUnmount → 子 unmounted → 父 unmounted
常用技巧
动态类名
html
<div :class="{ active: isActive, 'text-center': isCenter }">
<div :class="[activeClass, errorClass]">
条件类名
html
<div :class="[isActive && 'active']">
动态绑定 style
html
<div :style="{ color: textColor, fontSize: fontSize + 'px' }">
函数式组件
javascript
export default {
functional: true,
props: { msg: String },
render(h, context) {
return h('div', context.props.msg)
}
}
异步组件
javascript
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent({
loader: () => import('./Async.vue'),
loadingComponent: Loading,
errorComponent: Error,
delay: 200,
timeout: 3000
})
面试常见问题
1. v-model 的原理?
本质是 v-bind:value + @input 的语法糖,监听 input 事件并更新数据。
2. v-for 中 key 的作用?
帮助 Vue 识别节点身份,实现高效的 DOM 复用。推荐使用数据唯一 ID,避免使用数组索引。
3. computed 和 watch 的区别?
- computed:计算属性,依赖变化自动计算,缓存结果
- watch:监听器,监听数据变化,执行异步或复杂逻辑
4. Vue2 和 Vue3 的区别?
- 响应式:Object.defineProperty → Proxy
- API:Options API → Composition API
- 多根节点:不支持 → 支持
- 生命周期:beforeDestroy → beforeUnmount
5. 组件通信方式有哪些?
- props / $emit:父子
- provide / inject:祖先-后代
- <math xmlns="http://www.w3.org/1998/Math/MathML"> a t t r s / attrs / </math>attrs/listeners:透传
- 事件总线:兄弟/任意
- Pinia/Vuex:全局状态
6. Vue 的响应式原理?
通过 Proxy/Object.defineProperty 劫持数据访问,在 getter 中收集依赖 ,setter 中触发更新。
总结
Vue 以其简洁的 API 和渐进式的设计理念,成为前端开发的主流框架。掌握 Vue 的基础理论、常用 API 以及组件通信方式,是 Vue 开发者的必备技能。Vue3 的 Composition API 提供了更现代化的开发范式,建议在实际项目中优先使用。