Vue 基础知识(Vue 2 & Vue 3)
一、Vue 概述
1.1 什么是 Vue
Vue 是一套用于构建用户界面的渐进式 JavaScript 框架。
核心特点:
- 响应式数据绑定
- 组件化开发
- 虚拟 DOM
- 指令系统
- 单文件组件(SFC)
1.2 Vue 版本对比
| 特性 | Vue 2 | Vue 3 |
|---|---|---|
| 发布时间 | 2016 年 | 2020 年 |
| 性能 | 较慢 | 更快(约 2 倍) |
| 包大小 | 较大 | 更小(Tree-shaking) |
| Composition API | ❌ | ✅ |
| TypeScript | 支持有限 | 原生支持 |
| 响应式系统 | Object.defineProperty | Proxy |
| 生命周期 | Options API | Options API + Composition API |
二、快速开始
2.1 Vue 2 安装
<!-- CDN 引入 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- 或使用 npm -->
npm install vue@2
// Vue 2 使用
new Vue({
el: '#app',
data: {
message: 'Hello Vue 2'
}
})
2.2 Vue 3 安装
<!-- CDN 引入 -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<!-- 或使用 npm -->
npm install vue@3
// Vue 3 使用
import { createApp } from 'vue'
createApp({
data() {
return {
message: 'Hello Vue 3'
}
}
}).mount('#app')
2.3 单文件组件(SFC)
<template>
<div class="hello">
<h1>{{ message }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data() {
return {
message: 'Hello Vue'
}
}
}
</script>
<style scoped>
.hello {
color: #42b983;
}
</style>
三、模板语法
3.1 插值
Vue 2 & Vue 3(相同):
<template>
<!-- 文本插值 -->
<p>{{ message }}</p>
<!-- 原始 HTML -->
<div v-html="rawHtml"></div>
<!-- 属性绑定 -->
<div :id="dynamicId"></div>
<img :src="imageUrl" />
<!-- 布尔属性 -->
<button :disabled="isDisabled">按钮</button>
</template>
3.2 指令
v-if / v-show:
<template>
<!-- v-if:条件渲染 -->
<div v-if="type === 'A'">A</div>
<div v-else-if="type === 'B'">B</div>
<div v-else>C</div>
<!-- v-show:切换 display -->
<div v-show="isVisible">可见</div>
</template>
v-for:
<template>
<!-- 遍历数组 -->
<li v-for="(item, index) in items" :key="item.id">
{{ index }}: {{ item.name }}
</li>
<!-- 遍历对象 -->
<div v-for="(value, key) in object" :key="key">
{{ key }}: {{ value }}
</div>
</template>
v-on(事件):
<template>
<!-- 方法处理 -->
<button @click="handleClick">点击</button>
<!-- 内联处理 -->
<button @click="count++">+1</button>
<!-- 传参 -->
<button @click="handleClick(item, $event)">点击</button>
<!-- 事件修饰符 -->
<form @submit.prevent="onSubmit"></form>
<div @click.stop="onClick"></div>
<button @click.once="doOnce"></button>
</template>
v-model(双向绑定):
<template>
<!-- 文本 -->
<input v-model="message" />
<!-- 多行文本 -->
<textarea v-model="content"></textarea>
<!-- 复选框 -->
<input type="checkbox" v-model="checked" />
<!-- 单选 -->
<input type="radio" value="one" v-model="picked" />
<!-- 下拉 -->
<select v-model="selected">
<option value="a">A</option>
<option value="b">B</option>
</select>
<!-- 修饰符 -->
<input v-model.lazy="msg" />
<input v-model.number="age" />
<input v-model.trim="name" />
</template>
四、响应式数据
4.1 Vue 2(Options API)
export default {
data() {
return {
message: 'Hello',
count: 0,
user: {
name: 'zhangsan',
age: 25
},
items: [1, 2, 3]
}
},
computed: {
// 计算属性
doubleCount() {
return this.count * 2
}
},
watch: {
// 侦听器
count(newVal, oldVal) {
console.log(`count 从 ${oldVal} 变为 ${newVal}`)
}
},
methods: {
increment() {
this.count++
}
}
}
4.2 Vue 3(Composition API)
import { ref, reactive, computed, watch } from 'vue'
export default {
setup() {
// ref:基本类型
const count = ref(0)
const message = ref('Hello')
// reactive:对象
const user = reactive({
name: 'zhangsan',
age: 25
})
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 侦听器
watch(count, (newVal, oldVal) => {
console.log(`count 从 ${oldVal} 变为 ${newVal}`)
})
// 方法
const increment = () => {
count.value++
}
return {
count,
message,
user,
doubleCount,
increment
}
}
}
4.3 Vue 3(<script setup> 语法糖)
<script setup>
import { ref, reactive, computed, watch } from 'vue'
// ref
const count = ref(0)
const message = ref('Hello')
// reactive
const user = reactive({
name: 'zhangsan',
age: 25
})
// computed
const doubleCount = computed(() => count.value * 2)
// watch
watch(count, (newVal, oldVal) => {
console.log(`count 从 ${oldVal} 变为 ${newVal}`)
})
// methods
const increment = () => {
count.value++
}
</script>
4.4 ref vs reactive
| 特性 | ref | reactive |
|---|---|---|
| 适用类型 | 基本类型 + 对象 | 仅对象/数组 |
| 访问方式 | .value | 直接访问 |
| 重新赋值 | ✅ 可以 | ❌ 会丢失响应性 |
| 解构 | 保持响应性 | 需要 toRefs |
// ref 示例
const count = ref(0)
count.value++ // 正确
// reactive 示例
const state = reactive({ count: 0 })
state.count++ // 正确
// 解构 reactive
const { count } = toRefs(state)
count.value++ // 正确
五、计算属性和侦听器
5.1 计算属性(Computed)
Vue 2:
export default {
data() {
return {
firstName: '张',
lastName: '三'
}
},
computed: {
fullName() {
return this.firstName + this.lastName
},
// 可写计算属性
fullNameRW: {
get() {
return this.firstName + this.lastName
},
set(value) {
const names = value.split(' ')
this.firstName = names[0]
this.lastName = names[1]
}
}
}
}
Vue 3:
import { ref, computed } from 'vue'
const firstName = ref('张')
const lastName = ref('三')
// 只读
const fullName = computed(() => firstName.value + lastName.value)
// 可写
const fullNameRW = computed({
get: () => firstName.value + lastName.value,
set: (val) => {
const names = val.split(' ')
firstName.value = names[0]
lastName.value = names[1]
}
})
5.2 侦听器(Watch)
Vue 2:
export default {
data() {
return {
count: 0,
user: { name: 'zhangsan', age: 25 }
}
},
watch: {
// 基础用法
count(newVal, oldVal) {
console.log(`${oldVal} -> ${newVal}`)
},
// 深度监听
user: {
handler(newVal, oldVal) {
console.log('user changed')
},
deep: true,
immediate: true
},
// 监听对象属性
'user.name'(newVal) {
console.log('name changed:', newVal)
}
}
}
Vue 3:
import { ref, watch, watchEffect } from 'vue'
const count = ref(0)
const user = ref({ name: 'zhangsan', age: 25 })
// 基础用法
watch(count, (newVal, oldVal) => {
console.log(`${oldVal} -> ${newVal}`)
})
// 配置选项
watch(count, callback, {
immediate: true,
deep: true,
flush: 'post'
})
// 监听多个
watch([count, user], ([newCount, newUser], [oldCount, oldUser]) => {
console.log('changed')
})
// watchEffect:自动追踪依赖
watchEffect(() => {
console.log('count:', count.value)
})
六、生命周期
6.1 Vue 2 生命周期
export default {
beforeCreate() {
// 实例初始化之前
},
created() {
// 实例创建完成,数据已初始化
},
beforeMount() {
// 挂载之前
},
mounted() {
// 挂载完成,可访问 DOM
},
beforeUpdate() {
// 数据更新,DOM 更新前
},
updated() {
// DOM 更新后
},
beforeDestroy() {
// 销毁前(Vue 3 改为 beforeUnmount)
},
destroyed() {
// 销毁后(Vue 3 改为 unmounted)
}
}
6.2 Vue 3 生命周期
Options API(与 Vue 2 类似):
export default {
beforeCreate() { },
created() { },
beforeMount() { },
mounted() { },
beforeUpdate() { },
updated() { },
beforeUnmount() { }, // Vue 2 是 beforeDestroy
unmounted() { } // Vue 2 是 destroyed
}
Composition API:
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from 'vue'
export default {
setup() {
onBeforeMount(() => {
console.log('挂载前')
})
onMounted(() => {
console.log('已挂载')
})
onBeforeUpdate(() => {
console.log('更新前')
})
onUpdated(() => {
console.log('已更新')
})
onBeforeUnmount(() => {
console.log('卸载前')
})
onUnmounted(() => {
console.log('已卸载')
})
}
}
生命周期对比:
| Vue 2 | Vue 3 (Options) | Vue 3 (Composition) |
|---|---|---|
| beforeCreate | beforeCreate | setup() |
| created | created | setup() |
| beforeMount | beforeMount | onBeforeMount |
| mounted | mounted | onMounted |
| beforeUpdate | beforeUpdate | onBeforeUpdate |
| updated | updated | onUpdated |
| beforeDestroy | beforeUnmount | onBeforeUnmount |
| destroyed | unmounted | onUnmounted |
七、组件
7.1 组件注册
Vue 2:
// 全局注册
Vue.component('MyComponent', {
// 选项
})
// 局部注册
export default {
components: {
MyComponent
}
}
Vue 3:
// 全局注册
import { createApp } from 'vue'
const app = createApp({})
app.component('MyComponent', {
// 选项
})
// 局部注册(相同)
export default {
components: {
MyComponent
}
}
7.2 Props
Vue 2 & Vue 3(相同):
<!-- 子组件 -->
<script>
export default {
props: ['title', 'count'],
// 或带验证
props: {
title: String,
count: {
type: Number,
required: true,
default: 0
}
}
}
</script>
<!-- 父组件 -->
<template>
<Child title="标题" :count="10" />
</template>
Vue 3(Composition API):
<script setup>
const props = defineProps({
title: String,
count: {
type: Number,
required: true,
default: 0
}
})
</script>
7.3 Emit
Vue 2 & Vue 3(Options API):
<!-- 子组件 -->
<script>
export default {
methods: {
handleClick() {
this.$emit('update', { id: 1, value: 'new' })
}
}
}
</script>
<!-- 父组件 -->
<template>
<Child @update="handleUpdate" />
</template>
Vue 3(Composition API):
<script setup>
const emit = defineEmits(['update', 'delete'])
const handleClick = () => {
emit('update', { id: 1, value: 'new' })
}
</script>
7.4 v-model 组件
Vue 2:
<!-- 子组件 -->
<template>
<input :value="value" @input="$emit('input', $event.target.value)" />
</template>
<script>
export default {
props: ['value'],
model: {
prop: 'value',
event: 'input'
}
}
</script>
Vue 3:
<!-- 子组件 -->
<template>
<input :value="modelValue" @input="updateValue" />
</template>
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const updateValue = (e) => {
emit('update:modelValue', e.target.value)
}
</script>
<!-- 父组件 -->
<CustomInput v-model="text" />
7.5 插槽(Slots)
Vue 2 & Vue 3(相同):
<!-- 子组件 -->
<template>
<!-- 默认插槽 -->
<slot>默认内容</slot>
<!-- 具名插槽 -->
<header><slot name="header"></slot></header>
<footer><slot name="footer"></slot></footer>
<!-- 作用域插槽 -->
<slot name="item" :data="item" :index="index"></slot>
</template>
<!-- 父组件 -->
<template>
<Child>
<template #default>默认内容</template>
<template #header>头部</template>
<template #item="{ data, index }">
{{ index }}: {{ data.name }}
</template>
</Child>
</template>
八、组件通信
8.1 父子通信
// 父传子:Props
// 子传父:$emit
// 父组件
<Child :data="data" @update="handleUpdate" />
// 子组件
props: ['data']
this.$emit('update', newData)
8.2 兄弟组件通信
Vue 2:事件总线
// bus.js
import Vue from 'vue'
export default new Vue()
// 组件 A
import bus from './bus'
bus.$emit('event', data)
// 组件 B
import bus from './bus'
bus.$on('event', (data) => {
console.log(data)
})
Vue 3:mitt 或自定义事件总线
// 使用 mitt
import mitt from 'mitt'
const emitter = mitt()
emitter.emit('event', data)
emitter.on('event', (data) => {
console.log(data)
})
8.3 provide/inject
Vue 2:
// 祖先组件
export default {
provide() {
return {
theme: 'dark'
}
}
}
// 后代组件
export default {
inject: ['theme']
}
Vue 3:
// 祖先组件
import { provide } from 'vue'
export default {
setup() {
provide('theme', 'dark')
}
}
// 后代组件
import { inject } from 'vue'
export default {
setup() {
const theme = inject('theme', 'light') // 默认值
return { theme }
}
}
九、路由(Vue Router)
9.1 配置和使用
路由配置:
// Vue 2
import VueRouter from 'vue-router'
Vue.use(VueRouter)
export default new VueRouter({ mode: 'history', routes })
// Vue 3
import { createRouter, createWebHistory } from 'vue-router'
export default createRouter({ history: createWebHistory(), routes })
// 路由定义
const routes = [
{ path: '/', name: 'Home', component: Home },
{ path: '/user/:id', name: 'User', component: User },
{ path: '/about', name: 'About', component: () => import('@/views/About.vue') }
]
模板中使用:
<template>
<router-link to="/">首页</router-link>
<router-link :to="{ name: 'About' }">关于</router-link>
<router-view />
</template>
编程式导航:
// 基本导航
this.$router.push('/about')
this.$router.push({ name: 'About' })
this.$router.replace('/about') // 替换历史记录
this.$router.go(-1) // 后退
// 路径参数(params)
this.$router.push('/user/123')
this.$router.push({ name: 'User', params: { id: 123 } })
this.$route.params.id // 接收:123
// 查询参数(query)
this.$router.push('/detail?id=123&name=zhangsan')
this.$router.push({ path: '/detail', query: { id: 123, name: 'zhangsan' } })
this.$route.query.id // 接收:123
this.$route.query.name // 接收:'zhangsan'
Vue 3 Composition API:
<script setup>
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
router.push({ name: 'User', params: { id: 123 } })
router.push({ path: '/detail', query: { id: 123 } })
const userId = route.params.id
const queryId = route.query.id
</script>
注意事项:
params必须使用name,不能使用pathquery可以使用path或nameparams在 URL 中不显示(除非在路由配置中定义)query会显示在 URL 中
十、状态管理(Vuex / Pinia)
10.1 Vuex(Vue 2 & Vue 3)
// store/index.js
import Vuex from 'vuex'
import Vue from 'vue' // Vue 2
// import { createStore } from 'vuex' // Vue 3
Vue.use(Vuex) // Vue 2
export default new Vuex.Store({
// state:存储状态数据
state: {
count: 0
},
// mutations:同步修改 state(唯一方式)
mutations: {
increment(state) {
state.count++
}
},
// actions:异步操作,通过 commit 调用 mutations
actions: {
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment') // 调用 mutation
}, 1000)
}
},
// getters:计算属性,类似 computed
getters: {
doubleCount(state) {
return state.count * 2
}
}
})
// 在组件中使用
// this.$store.state.count // 访问 state
// this.$store.commit('increment') // 调用 mutation
// this.$store.dispatch('incrementAsync') // 调用 action
// this.$store.getters.doubleCount // 访问 getter
10.2 Pinia(Vue 3 推荐)
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
// state:状态数据(必须是函数)
state: () => ({
count: 0
}),
// getters:计算属性
getters: {
doubleCount: (state) => state.count * 2
},
// actions:方法(可以是同步或异步)
actions: {
increment() {
this.count++ // 直接修改 state
},
async fetchData() {
// 异步操作
const data = await api.getData()
this.count = data.count
}
}
})
// 在组件中使用
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
// 访问 state
counter.count
// 调用 action
counter.increment()
// 访问 getter
counter.doubleCount
// 重置 store
counter.$reset()
十一、常用指令和 API
11.1 指令
<template>
<!-- v-text -->
<div v-text="message"></div>
<!-- v-html -->
<div v-html="rawHtml"></div>
<!-- v-cloak:防止闪烁 -->
<div v-cloak>{{ message }}</div>
<!-- v-once:只渲染一次 -->
<div v-once>{{ message }}</div>
<!-- v-pre:跳过编译 -->
<div v-pre>{{ message }}</div>
</template>
11.2 实例属性
Vue 2:
this.$data // 数据
this.$props // Props
this.$el // DOM 元素
this.$options // 选项
this.$parent // 父实例
this.$root // 根实例
this.$children // 子实例
this.$refs // 引用
this.$router // 路由
this.$route // 当前路由
this.$store // Vuex store
Vue 3:
// 大部分相同,但 $children 已移除
// 使用 $refs 访问子组件
十二、性能优化
12.1 列表渲染优化
<template>
<!-- 使用 key -->
<div v-for="item in items" :key="item.id">
{{ item.name }}
</div>
<!-- 虚拟滚动(大数据) -->
<virtual-list :data="largeList" />
</template>
12.2 组件懒加载
// 路由懒加载
const Home = () => import('@/views/Home.vue')
// 组件懒加载
const AsyncComponent = () => import('./AsyncComponent.vue')
12.3 计算属性缓存
// ✅ 使用计算属性(有缓存)
computed: {
filteredList() {
return this.list.filter(item => item.active)
}
}
// ❌ 避免在模板中使用方法(无缓存)
methods: {
filterList() {
return this.list.filter(item => item.active)
}
}
12.4 v-show vs v-if
<template>
<!-- v-show:适合频繁切换 -->
<div v-show="isVisible">内容</div>
<!-- v-if:适合条件很少改变 -->
<div v-if="isVisible">内容</div>
</template>
十三、Vue 2 到 Vue 3 迁移
13.1 主要变化
| 变化 | Vue 2 | Vue 3 |
|---|---|---|
| 创建应用 | new Vue() | createApp() |
| 全局 API | Vue.component() | app.component() |
| 过滤器 | ✅ 支持 | ❌ 已移除 |
| 事件 API | $on, $off | ❌ 已移除 |
| v-model | value + input | modelValue + update:modelValue |
| 生命周期 | beforeDestroy | beforeUnmount |
| 响应式 | Object.defineProperty | Proxy |
13.2 迁移步骤
- 升级依赖
npm install vue@3 vue-router@4 vuex@4
- 更新入口文件
// Vue 2
import Vue from 'vue'
new Vue({ ... }).$mount('#app')
// Vue 3
import { createApp } from 'vue'
createApp({ ... }).mount('#app')
- 更新组件语法
- 使用 Composition API(可选)
- 更新生命周期钩子名称
- 移除过滤器,使用计算属性或方法
十四、最佳实践
14.1 组件设计
<!-- ✅ 单一职责 -->
<template>
<UserCard :user="user" />
</template>
<!-- ✅ 可复用 -->
<template>
<Button :type="type" @click="handleClick">
<slot />
</Button>
</template>
14.2 代码组织
<script>
export default {
name: 'ComponentName',
components: {},
props: {},
data() {},
computed: {},
watch: {},
methods: {},
lifecycle hooks...
}
</script>
14.3 命名规范
// 组件名:PascalCase
MyComponent.vue
// Props:camelCase
userName, isVisible
// 事件名:kebab-case
@user-updated, @item-deleted
// 方法名:camelCase
handleClick, fetchData
十五、推荐资源
官方文档:
- Vue 2:v2.cn.vuejs.org/
- Vue 3:cn.vuejs.org/
- Vue Router:router.vuejs.org/
- Pinia:pinia.vuejs.org/
学习资源:
- 《Vue.js 设计与实现》
- Vue Mastery:www.vuemastery.com/
- Vue School:vueschool.io/
十六、总结
Vue 核心要点:
响应式数据 + 组件化 + 指令系统 + 生态工具 = 高效前端开发
版本选择建议:
- 新项目:推荐 Vue 3(性能更好,生态完善)
- 现有项目:Vue 2 稳定,可逐步迁移
核心心法:
Vue 2 和 Vue 3 核心思想相同,主要是 API 差异。 掌握响应式原理和组件化思想是关键。
📝 文档信息
- 作者: 阿鑫
- 更新日期: 2026.1