Vue3详解
2402字约8分钟
前端Vue3Vue框架
2024-11-19
一、Vue3 核心特性
1. Composition API
Vue3 引入了 Composition API,提供了一种更灵活的组织组件逻辑的方式。
核心优势:
- 逻辑复用:更好的逻辑复用和组合
- 类型推导:更好的 TypeScript 支持
- 逻辑分离:按功能组织代码,而不是按选项
- 性能优化:更精确的响应式追踪
2. 响应式系统
Vue3 使用 Proxy 替代 Object.defineProperty:
// Vue2 的响应式系统
const data = {
message: 'Hello'
}
Object.defineProperty(data, 'message', {
get() {
return this._message
},
set(value) {
this._message = value
// 触发更新
}
})
// Vue3 的响应式系统
const data = reactive({
message: 'Hello'
})二、Composition API 基础
1. setup 函数
setup 函数是 Composition API 的入口:
<template>
<div>
<h1>{{ count }}</h1>
<button @click="increment">+1</button>
<p>{{ message }}</p>
</div>
</template>
<script>
import { ref, reactive, onMounted } from 'vue'
export default {
name: 'Counter',
setup() {
// 响应式数据
const count = ref(0)
const state = reactive({
message: 'Hello Vue3'
})
// 方法
const increment = () => {
count.value++
}
// 生命周期
onMounted(() => {
console.log('组件已挂载')
})
// 返回模板需要的数据和方法
return {
count,
message: state.message,
increment
}
}
}
</script>2. 响应式 API
ref 和 reactive:
import { ref, reactive, computed, watch } from 'vue'
// ref - 用于基本类型
const count = ref(0)
const name = ref('Vue3')
const isActive = ref(false)
// reactive - 用于对象
const user = reactive({
name: 'John',
age: 25,
address: {
city: 'Beijing',
country: 'China'
}
})
// computed - 计算属性
const doubleCount = computed(() => count.value * 2)
const fullName = computed(() => `${user.firstName} ${user.lastName}`)
// watch - 监听器
watch(count, (newValue, oldValue) => {
console.log(`count changed from ${oldValue} to ${newValue}`)
})
// watchEffect - 自动追踪依赖
watchEffect(() => {
console.log(`Count is: ${count.value}`)
})三、Vue3 生命周期
1. 生命周期钩子
Vue3 的生命周期钩子:
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onErrorCaptured,
onRenderTracked,
onRenderTriggered
} from 'vue'
export default {
setup() {
// 组件挂载前
onBeforeMount(() => {
console.log('组件挂载前')
})
// 组件挂载后
onMounted(() => {
console.log('组件已挂载')
})
// 组件更新前
onBeforeUpdate(() => {
console.log('组件更新前')
})
// 组件更新后
onUpdated(() => {
console.log('组件已更新')
})
// 组件卸载前
onBeforeUnmount(() => {
console.log('组件卸载前')
})
// 组件卸载后
onUnmounted(() => {
console.log('组件已卸载')
})
// 错误捕获
onErrorCaptured((err, instance, info) => {
console.error('捕获到错误:', err)
return false // 阻止错误继续传播
})
// 渲染追踪
onRenderTracked((event) => {
console.log('追踪到响应式依赖:', event)
})
// 渲染触发
onRenderTriggered((event) => {
console.log('触发重新渲染:', event)
})
}
}2. 生命周期对比
Vue2 vs Vue3 生命周期:
// Vue2 选项式 API
export default {
beforeCreate() {
// 实例创建前
},
created() {
// 实例创建后
},
beforeMount() {
// 挂载前
},
mounted() {
// 挂载后
},
beforeUpdate() {
// 更新前
},
updated() {
// 更新后
},
beforeDestroy() {
// 销毁前
},
destroyed() {
// 销毁后
}
}
// Vue3 Composition API
import { onMounted, onUnmounted } from 'vue'
export default {
setup() {
onMounted(() => {
// 挂载后
})
onUnmounted(() => {
// 卸载后
})
}
}四、组件通信
1. Props 和 Emits
父组件向子组件传递数据:
<!-- 父组件 -->
<template>
<ChildComponent
:title="title"
:user="user"
@update-title="handleUpdateTitle"
@user-change="handleUserChange"
/>
</template>
<script>
import { ref, reactive } from 'vue'
import ChildComponent from './ChildComponent.vue'
export default {
components: { ChildComponent },
setup() {
const title = ref('Vue3 Tutorial')
const user = reactive({
name: 'John',
age: 25
})
const handleUpdateTitle = (newTitle) => {
title.value = newTitle
}
const handleUserChange = (newUser) => {
Object.assign(user, newUser)
}
return {
title,
user,
handleUpdateTitle,
handleUserChange
}
}
}
</script>子组件接收和发送事件:
<!-- 子组件 -->
<template>
<div>
<h2>{{ title }}</h2>
<p>{{ user.name }} - {{ user.age }}</p>
<button @click="updateTitle">更新标题</button>
<button @click="updateUser">更新用户</button>
</div>
</template>
<script>
import { defineProps, defineEmits } from 'vue'
export default {
// 定义 props
props: {
title: {
type: String,
required: true
},
user: {
type: Object,
required: true
}
},
// 定义 emits
emits: ['update-title', 'user-change'],
setup(props, { emit }) {
const updateTitle = () => {
emit('update-title', 'New Title')
}
const updateUser = () => {
emit('user-change', {
name: 'Jane',
age: 30
})
}
return {
updateTitle,
updateUser
}
}
}
</script>2. Provide/Inject
跨层级组件通信:
<!-- 祖先组件 -->
<template>
<div>
<h1>{{ theme }}</h1>
<ChildComponent />
</div>
</template>
<script>
import { ref, provide } from 'vue'
import ChildComponent from './ChildComponent.vue'
export default {
components: { ChildComponent },
setup() {
const theme = ref('dark')
const user = ref({
name: 'John',
role: 'admin'
})
// 提供数据给后代组件
provide('theme', theme)
provide('user', user)
provide('updateTheme', (newTheme) => {
theme.value = newTheme
})
return {
theme
}
}
}
</script>后代组件注入数据:
<!-- 后代组件 -->
<template>
<div>
<p>当前主题: {{ theme }}</p>
<p>用户: {{ user.name }}</p>
<button @click="changeTheme">切换主题</button>
</div>
</template>
<script>
import { inject } from 'vue'
export default {
setup() {
// 注入数据
const theme = inject('theme')
const user = inject('user')
const updateTheme = inject('updateTheme')
const changeTheme = () => {
updateTheme(theme.value === 'dark' ? 'light' : 'dark')
}
return {
theme,
user,
changeTheme
}
}
}
</script>五、响应式系统详解
1. ref 和 reactive
ref 的使用:
import { ref, isRef, unref } from 'vue'
// 基本类型使用 ref
const count = ref(0)
const name = ref('Vue3')
const isActive = ref(false)
// 访问和修改值
console.log(count.value) // 0
count.value = 1
// 检查是否为 ref
console.log(isRef(count)) // true
console.log(isRef(0)) // false
// 解包 ref
const unwrappedCount = unref(count) // 等同于 count.valuereactive 的使用:
import { reactive, isReactive, toRefs } from 'vue'
// 对象使用 reactive
const state = reactive({
count: 0,
name: 'Vue3',
user: {
name: 'John',
age: 25
}
})
// 直接修改属性
state.count = 1
state.user.name = 'Jane'
// 检查是否为 reactive 对象
console.log(isReactive(state)) // true
// 解构 reactive 对象
const { count, name } = toRefs(state)
// 现在 count 和 name 是 ref,保持响应式2. computed 和 watch
computed 计算属性:
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
// 只读计算属性
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
// 可写计算属性
const fullNameWritable = computed({
get() {
return `${firstName.value} ${lastName.value}`
},
set(value) {
const names = value.split(' ')
firstName.value = names[0]
lastName.value = names[1] || ''
}
})
console.log(fullName.value) // "John Doe"
fullNameWritable.value = 'Jane Smith'watch 监听器:
import { ref, watch, watchEffect } from 'vue'
const count = ref(0)
const name = ref('Vue3')
// 监听单个 ref
watch(count, (newValue, oldValue) => {
console.log(`count changed: ${oldValue} -> ${newValue}`)
})
// 监听多个源
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log(`count: ${oldCount} -> ${newCount}`)
console.log(`name: ${oldName} -> ${newName}`)
})
// 深度监听对象
const user = ref({
name: 'John',
age: 25
})
watch(user, (newUser, oldUser) => {
console.log('user changed:', newUser)
}, { deep: true })
// watchEffect - 自动追踪依赖
watchEffect(() => {
console.log(`Count: ${count.value}, Name: ${name.value}`)
})六、组件组合
1. 组合函数 (Composables)
创建可复用的逻辑:
// useCounter.js
import { ref, computed } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const doubleCount = computed(() => count.value * 2)
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
const reset = () => {
count.value = initialValue
}
return {
count,
doubleCount,
increment,
decrement,
reset
}
}使用组合函数:
<template>
<div>
<h2>计数器: {{ count }}</h2>
<p>双倍: {{ doubleCount }}</p>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
<button @click="reset">重置</button>
</div>
</template>
<script>
import { useCounter } from './useCounter'
export default {
setup() {
const { count, doubleCount, increment, decrement, reset } = useCounter(10)
return {
count,
doubleCount,
increment,
decrement,
reset
}
}
}
</script>2. 异步组合函数
处理异步逻辑:
// useAsync.js
import { ref, onMounted } from 'vue'
export function useAsync(asyncFn) {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
const execute = async (...args) => {
loading.value = true
error.value = null
try {
data.value = await asyncFn(...args)
} catch (err) {
error.value = err
} finally {
loading.value = false
}
}
return {
data,
loading,
error,
execute
}
}使用异步组合函数:
<template>
<div>
<button @click="fetchData" :disabled="loading">
{{ loading ? '加载中...' : '获取数据' }}
</button>
<div v-if="loading">加载中...</div>
<div v-else-if="error">错误: {{ error.message }}</div>
<div v-else-if="data">
<h3>用户信息:</h3>
<p>姓名: {{ data.name }}</p>
<p>邮箱: {{ data.email }}</p>
</div>
</div>
</template>
<script>
import { useAsync } from './useAsync'
export default {
setup() {
const fetchUser = async (id) => {
const response = await fetch(`/api/users/${id}`)
if (!response.ok) {
throw new Error('获取用户信息失败')
}
return response.json()
}
const { data, loading, error, execute: fetchData } = useAsync(fetchUser)
return {
data,
loading,
error,
fetchData: () => fetchData(1)
}
}
}
</script>七、模板语法增强
1. v-model 增强
Vue3 的 v-model:
<template>
<!-- 基本用法 -->
<input v-model="message" />
<!-- 自定义组件 v-model -->
<CustomInput v-model="title" />
<!-- 多个 v-model -->
<UserForm
v-model:name="user.name"
v-model:email="user.email"
v-model:age="user.age"
/>
<!-- 自定义修饰符 -->
<input v-model.trim="message" />
<input v-model.number="age" />
<input v-model.lazy="message" />
</template>
<script>
import { ref, reactive } from 'vue'
import CustomInput from './CustomInput.vue'
import UserForm from './UserForm.vue'
export default {
components: { CustomInput, UserForm },
setup() {
const message = ref('')
const title = ref('')
const user = reactive({
name: '',
email: '',
age: 0
})
return {
message,
title,
user
}
}
}
</script>2. 自定义组件 v-model
<!-- CustomInput.vue -->
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
<script>
export default {
props: {
modelValue: {
type: String,
required: true
}
},
emits: ['update:modelValue']
}
</script>八、性能优化
1. 响应式优化
shallowRef 和 shallowReactive:
import { ref, shallowRef, reactive, shallowReactive } from 'vue'
// 深层响应式
const deepRef = ref({
nested: {
value: 'deep'
}
})
// 浅层响应式
const shallowRef = shallowRef({
nested: {
value: 'shallow'
}
})
// 深层响应式对象
const deepReactive = reactive({
nested: {
value: 'deep'
}
})
// 浅层响应式对象
const shallowReactive = shallowReactive({
nested: {
value: 'shallow'
}
})2. 组件优化
异步组件:
// 异步组件
const AsyncComponent = defineAsyncComponent({
loader: () => import('./HeavyComponent.vue'),
loadingComponent: LoadingSpinner,
errorComponent: ErrorComponent,
delay: 200,
timeout: 3000
})组件缓存:
<template>
<div>
<keep-alive :include="['UserList', 'UserDetail']">
<component :is="currentComponent" />
</keep-alive>
</div>
</template>九、TypeScript 支持
1. 类型定义
// 组件 Props 类型
interface UserProps {
name: string
age: number
email?: string
}
// 组件 Emits 类型
interface UserEmits {
(e: 'update', user: User): void
(e: 'delete', id: number): void
}
// 组合函数类型
interface UseCounterReturn {
count: Ref<number>
increment: () => void
decrement: () => void
reset: () => void
}
export function useCounter(initialValue = 0): UseCounterReturn {
const count = ref(initialValue)
const increment = () => count.value++
const decrement = () => count.value--
const reset = () => count.value = initialValue
return {
count,
increment,
decrement,
reset
}
}2. 组件类型
<script lang="ts">
import { defineComponent, ref, PropType } from 'vue'
interface User {
id: number
name: string
email: string
}
export default defineComponent({
props: {
user: {
type: Object as PropType<User>,
required: true
},
count: {
type: Number,
default: 0
}
},
emits: {
update: (user: User) => true,
delete: (id: number) => true
},
setup(props, { emit }) {
const localCount = ref(props.count)
const updateUser = (user: User) => {
emit('update', user)
}
const deleteUser = (id: number) => {
emit('delete', id)
}
return {
localCount,
updateUser,
deleteUser
}
}
})
</script>十、最佳实践
1. 代码组织
// 按功能组织代码
export default {
setup() {
// 1. 响应式数据
const count = ref(0)
const user = reactive({
name: '',
email: ''
})
// 2. 计算属性
const doubleCount = computed(() => count.value * 2)
const fullName = computed(() => `${user.firstName} ${user.lastName}`)
// 3. 方法
const increment = () => count.value++
const updateUser = (newUser) => Object.assign(user, newUser)
// 4. 生命周期
onMounted(() => {
console.log('组件已挂载')
})
// 5. 监听器
watch(count, (newValue) => {
console.log('count changed:', newValue)
})
// 6. 返回模板需要的数据
return {
count,
user,
doubleCount,
fullName,
increment,
updateUser
}
}
}2. 性能优化
// 使用 shallowRef 优化大对象
const largeObject = shallowRef({
// 大量数据
})
// 使用 markRaw 标记非响应式对象
import { markRaw } from 'vue'
const staticObject = markRaw({
// 静态数据
})
// 使用 toRef 优化 props 访问
import { toRef } from 'vue'
export default {
props: ['user'],
setup(props) {
// 保持响应式
const userName = toRef(props, 'user')
return {
userName
}
}
}总结
Vue3 通过 Composition API 提供了更灵活、更强大的组件开发方式:
核心优势:
- 逻辑复用:通过组合函数实现更好的逻辑复用
- 类型安全:更好的 TypeScript 支持
- 性能优化:更精确的响应式追踪和更好的性能
- 逻辑组织:按功能组织代码,提高可维护性
- 开发体验:更好的开发工具支持
关键要点:
- 响应式系统:使用 ref 和 reactive 管理状态
- 生命周期:使用 onMounted 等钩子函数
- 组件通信:Props、Emits、Provide/Inject
- 组合函数:创建可复用的逻辑
- 性能优化:合理使用 shallowRef、异步组件等
通过合理使用 Vue3 的特性,可以构建出高效、可维护的现代化前端应用。
