Vue 3新特性与生态:前端框架的未来
Vue 3就像Vue 2的"超级升级版",带来了更强大的功能和更好的开发体验。如果说Vue 2是一辆跑车,那么Vue 3就是一辆配备涡轮增压引擎的超跑!
1. Vue 3核心特性:组合式API的革命
组合式API(Composition API):逻辑复用的新方式
组合式API就像乐高的积木,让你可以更灵活地组织和复用代码逻辑:
vue
<!-- Vue 2 选项式API -->
<script>
export default {
data() {
return {
count: 0,
posts: []
}
},
methods: {
increment() {
this.count++
},
async fetchPosts() {
// 获取文章列表
}
},
computed: {
doubleCount() {
return this.count * 2
}
},
mounted() {
this.fetchPosts()
}
}
</script>vue
<!-- Vue 3 组合式API -->
<script setup>
import { ref, computed, onMounted } from 'vue'
// 响应式数据
const count = ref(0)
const posts = ref([])
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 方法
const increment = () => {
count.value++
}
const fetchPosts = async () => {
// 获取文章列表
posts.value = await api.getPosts()
}
// 生命周期钩子
onMounted(() => {
fetchPosts()
})
</script>响应式API详解
ref:基础类型的响应式
vue
<script setup>
import { ref, watch } from 'vue'
// 创建响应式基础类型
const count = ref(0)
const name = ref('Vue')
const isLoading = ref(false)
// 修改值需要通过.value
const increment = () => {
count.value++
}
// 监听ref变化
watch(count, (newVal, oldVal) => {
console.log(`计数从 ${oldVal} 变为 ${newVal}`)
})
// 监听多个ref
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log('值发生了变化')
})
</script>reactive:对象类型的响应式
vue
<script setup>
import { reactive, watch } from 'vue'
// 创建响应式对象
const state = reactive({
user: {
name: '张三',
age: 25
},
posts: [],
loading: false
})
// 直接修改属性
const updateUser = () => {
state.user.name = '李四'
state.user.age = 30
}
// 监听对象属性变化
watch(
() => state.user.name,
(newName, oldName) => {
console.log(`用户名从 ${oldName} 变为 ${newName}`)
}
)
// 深度监听整个对象
watch(
state,
(newState, oldState) => {
console.log('状态发生变化')
},
{ deep: true }
)
</script>computed和watch的高级用法
vue
<script setup>
import { ref, computed, watch, watchEffect } from 'vue'
const firstName = ref('张')
const lastName = ref('三')
const age = ref(25)
// 计算属性
const fullName = computed({
get: () => `${firstName.value}${lastName.value}`,
set: (value) => {
[firstName.value, lastName.value] = value.split(' ')
}
})
// watch - 明确指定依赖
watch(
[firstName, lastName],
([newFirst, newLast], [oldFirst, oldLast]) => {
console.log(`姓名从 ${oldFirst}${oldLast} 变为 ${newFirst}${newLast}`)
}
)
// watchEffect - 自动追踪依赖
watchEffect(() => {
console.log(`全名: ${fullName.value}, 年龄: ${age.value}`)
// 当fullName或age变化时自动执行
})
// 停止监听
const stopWatcher = watchEffect(() => {
// ...
})
// 停止监听
stopWatcher()
</script>依赖注入:provide/inject
vue
<!-- 父组件 App.vue -->
<script setup>
import { provide, ref } from 'vue'
const theme = ref('light')
const user = ref({ name: '张三', role: 'admin' })
// 提供数据给子孙组件
provide('theme', theme)
provide('user', user)
provide('updateTheme', (newTheme) => {
theme.value = newTheme
})
</script>vue
<!-- 子孙组件 -->
<script setup>
import { inject } from 'vue'
// 注入数据
const theme = inject('theme')
const user = inject('user')
const updateTheme = inject('updateTheme')
// 带默认值的注入
const config = inject('config', { apiUrl: 'https://api.example.com' })
</script>2. Vue 3高级特性:更强大的功能
自定义组合函数(Composables)
组合函数就像可复用的"代码积木",让你可以轻松分享逻辑:
javascript
// composables/useCounter.js
import { ref, computed } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const increment = () => count.value++
const decrement = () => count.value--
const reset = () => count.value = initialValue
const doubleCount = computed(() => count.value * 2)
return {
count,
increment,
decrement,
reset,
doubleCount
}
}javascript
// composables/useApi.js
import { ref, reactive } from 'vue'
export function useApi() {
const loading = ref(false)
const error = ref(null)
const data = ref(null)
const request = async (apiCall) => {
loading.value = true
error.value = null
try {
data.value = await apiCall()
return data.value
} catch (err) {
error.value = err
throw err
} finally {
loading.value = false
}
}
return {
loading,
error,
data,
request
}
}vue
<!-- 在组件中使用组合函数 -->
<script setup>
import { useCounter, useApi } from '@/composables'
// 使用计数器组合函数
const { count, increment, decrement, doubleCount } = useCounter(10)
// 使用API组合函数
const { loading, error, data, request } = useApi()
const fetchUsers = async () => {
await request(() => api.getUsers())
}
</script>Teleport:传送门组件
Teleport就像"传送门",可以将组件内容渲染到DOM的任何位置:
vue
<template>
<div>
<h1>我的应用</h1>
<!-- 传送到body -->
<teleport to="body">
<div class="modal" v-if="showModal">
<div class="modal-content">
<h2>模态框</h2>
<p>这是模态框内容</p>
<button @click="showModal = false">关闭</button>
</div>
</div>
</teleport>
<!-- 传送到指定元素 -->
<teleport to="#popup-container">
<div class="popup" v-if="showPopup">
<p>这是一个弹出框</p>
</div>
</teleport>
<button @click="showModal = true">打开模态框</button>
<button @click="showPopup = true">打开弹出框</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const showModal = ref(false)
const showPopup = ref(false)
</script>
<style>
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background: white;
padding: 20px;
border-radius: 8px;
}
.popup {
position: fixed;
top: 20px;
right: 20px;
background: #409eff;
color: white;
padding: 10px;
border-radius: 4px;
}
</style>Suspense:异步组件的等待状态
Suspense可以优雅地处理异步组件的加载状态:
vue
<!-- AsyncComponent.vue -->
<script setup>
// 模拟异步加载数据
const userData = await fetch('/api/user')
</script>
<template>
<div>
<h2>用户信息</h2>
<p>姓名: {{ userData.name }}</p>
<p>邮箱: {{ userData.email }}</p>
</div>
</template>vue
<!-- 父组件 -->
<template>
<div>
<Suspense>
<!-- 异步组件 -->
<template #default>
<AsyncComponent />
</template>
<!-- 加载状态 -->
<template #fallback>
<div class="loading">
<p>加载中...</p>
</div>
</template>
</Suspense>
</div>
</template>3. 生态系统工具:开发者的得力助手
Vite:新一代构建工具
Vite就像"火箭燃料",让开发环境启动速度飞快:
javascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
// 服务器配置
server: {
port: 3000,
open: true, // 自动打开浏览器
proxy: {
// 代理API请求
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
// 构建配置
build: {
outDir: 'dist',
assetsDir: 'assets',
rollupOptions: {
// 代码分割
output: {
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia']
}
}
}
},
// 路径别名
resolve: {
alias: {
'@': '/src'
}
}
})UI组件库推荐
Element Plus(Vue 3官方推荐)
bash
# 安装
npm install element-plus
# 全局引入
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
app.use(ElementPlus)
# 按需引入
import { ElButton, ElInput } from 'element-plus'vue
<template>
<div>
<el-button type="primary" @click="handleClick">主要按钮</el-button>
<el-input v-model="inputValue" placeholder="请输入内容" />
<el-table :data="tableData">
<el-table-column prop="name" label="姓名" />
<el-table-column prop="age" label="年龄" />
</el-table>
</div>
</template>Naive UI(高质量组件库)
bash
# 安装
npm install naive-ui
# 使用
import { NButton, NInput, NDataTable } from 'naive-ui'4. 服务端交互:与后端的完美配合
Axios集成与封装
javascript
// utils/request.js
import axios from 'axios'
// 创建axios实例
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 5000
})
// 请求拦截器
service.interceptors.request.use(
config => {
// 添加token
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
const { code, data, message } = response.data
if (code === 200) {
return data
} else {
// 处理业务错误
console.error(message)
return Promise.reject(new Error(message))
}
},
error => {
// 处理网络错误
console.error('网络错误:', error)
return Promise.reject(error)
}
)
export default servicejavascript
// api/user.js
import request from '@/utils/request'
export function getUserList(params) {
return request({
url: '/users',
method: 'get',
params
})
}
export function createUser(data) {
return request({
url: '/users',
method: 'post',
data
})
}
export function updateUser(id, data) {
return request({
url: `/users/${id}`,
method: 'put',
data
})
}在组合函数中使用异步数据
javascript
// composables/useUser.js
import { ref, reactive } from 'vue'
import { getUserList, createUser } from '@/api/user'
export function useUser() {
const users = ref([])
const loading = ref(false)
const error = ref(null)
const fetchUsers = async (params) => {
loading.value = true
error.value = null
try {
users.value = await getUserList(params)
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}
const addUser = async (userData) => {
try {
const newUser = await createUser(userData)
users.value.push(newUser)
return newUser
} catch (err) {
error.value = err.message
throw err
}
}
return {
users,
loading,
error,
fetchUsers,
addUser
}
}vue
<!-- 在组件中使用 -->
<script setup>
import { onMounted } from 'vue'
import { useUser } from '@/composables/useUser'
const { users, loading, error, fetchUsers, addUser } = useUser()
onMounted(() => {
fetchUsers({ page: 1, size: 10 })
})
const handleAddUser = async () => {
try {
await addUser({ name: '新用户', email: 'new@example.com' })
console.log('用户添加成功')
} catch (err) {
console.error('添加用户失败:', err)
}
}
</script>5. 实践项目:Vue 3实战应用
用组合式API重构项目
vue
<!-- 重构前:选项式API -->
<script>
export default {
data() {
return {
searchQuery: '',
users: [],
loading: false,
selectedUser: null
}
},
computed: {
filteredUsers() {
return this.users.filter(user =>
user.name.includes(this.searchQuery)
)
}
},
methods: {
async fetchUsers() {
this.loading = true
try {
this.users = await api.getUsers()
} finally {
this.loading = false
}
},
selectUser(user) {
this.selectedUser = user
}
},
mounted() {
this.fetchUsers()
}
}
</script>vue
<!-- 重构后:组合式API -->
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useUser } from '@/composables/useUser'
// 使用组合函数
const { users, loading, fetchUsers } = useUser()
// 本地状态
const searchQuery = ref('')
const selectedUser = ref(null)
// 计算属性
const filteredUsers = computed(() => {
return users.value.filter(user =>
user.name.includes(searchQuery.value)
)
})
// 方法
const selectUser = (user) => {
selectedUser.value = user
}
// 生命周期
onMounted(() => {
fetchUsers()
})
</script>完整的后台管理系统
vue
<!-- AdminLayout.vue -->
<template>
<div class="admin-layout">
<!-- 侧边栏 -->
<aside class="sidebar">
<nav>
<router-link to="/dashboard">仪表盘</router-link>
<router-link to="/users">用户管理</router-link>
<router-link to="/products">商品管理</router-link>
<router-link to="/orders">订单管理</router-link>
</nav>
</aside>
<!-- 主内容区 -->
<main class="main-content">
<header class="header">
<h1>{{ $route.meta.title }}</h1>
<div class="user-info">
<span>欢迎, {{ userStore.userInfo?.name }}</span>
<button @click="handleLogout">退出</button>
</div>
</header>
<div class="content">
<router-view />
</div>
</main>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { useUserStore } from '@/stores/user'
const router = useRouter()
const userStore = useUserStore()
const handleLogout = () => {
userStore.logout()
router.push('/login')
}
</script>javascript
// stores/user.js (Pinia)
import { defineStore } from 'pinia'
import { login as apiLogin, getUserInfo } from '@/api/auth'
export const useUserStore = defineStore('user', {
state: () => ({
token: localStorage.getItem('token') || null,
userInfo: null
}),
getters: {
isLoggedIn: (state) => !!state.token
},
actions: {
async login(credentials) {
try {
const { token } = await apiLogin(credentials)
this.token = token
localStorage.setItem('token', token)
await this.fetchUserInfo()
return true
} catch (error) {
throw error
}
},
async fetchUserInfo() {
if (this.token) {
this.userInfo = await getUserInfo()
}
},
logout() {
this.token = null
this.userInfo = null
localStorage.removeItem('token')
}
}
})数据可视化dashboard
vue
<!-- Dashboard.vue -->
<template>
<div class="dashboard">
<div class="stats-cards">
<div class="card">
<h3>总用户数</h3>
<p class="number">{{ stats.totalUsers }}</p>
<p class="trend positive">↑ 12% 本月</p>
</div>
<div class="card">
<h3>总销售额</h3>
<p class="number">¥{{ formatCurrency(stats.totalSales) }}</p>
<p class="trend positive">↑ 8% 本月</p>
</div>
<div class="card">
<h3>订单数</h3>
<p class="number">{{ stats.totalOrders }}</p>
<p class="trend negative">↓ 2% 本月</p>
</div>
</div>
<div class="charts">
<div class="chart-container">
<h3>月度销售趋势</h3>
<LineChart :data="salesData" />
</div>
<div class="chart-container">
<h3>用户地区分布</h3>
<PieChart :data="regionData" />
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useDashboard } from '@/composables/useDashboard'
import LineChart from '@/components/charts/LineChart.vue'
import PieChart from '@/components/charts/PieChart.vue'
const { stats, salesData, regionData, fetchDashboardData } = useDashboard()
onMounted(() => {
fetchDashboardData()
})
const formatCurrency = (amount) => {
return new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
}).format(amount)
}
</script>总结
本章节介绍了Vue 3的新特性和生态系统:
- 组合式API的核心概念和使用方法
- Vue 3的高级特性(Teleport、Suspense等)
- 生态系统工具(Vite、UI组件库)
- 服务端交互的最佳实践
- 实际项目中的应用示例
Vue 3的组合式API让代码组织更加灵活,逻辑复用更加简单。通过自定义组合函数,你可以构建出高度可复用的代码库。同时,Vue 3的生态系统也更加完善,为开发者提供了丰富的工具选择。
在下一章节中,我们将学习Vue的工程化实践和性能优化技巧,让你的应用更加专业和高效。
记住,Vue 3是Vue框架的未来,掌握这些新特性将让你在前端开发领域保持竞争力。多实践这些新特性,你的Vue开发技能将达到新的高度!