Skip to content

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 service
javascript
// 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开发技能将达到新的高度!