Skip to content

Vue 3 基础教程

欢迎来到 Vue 3 基础教程!本教程将带您从零开始学习 Vue 3 的核心概念和用法。

什么是 Vue 3?

Vue 3 是一个用于构建用户界面的渐进式 JavaScript 框架。相比 Vue 2,Vue 3 带来了:

  • 🚀 更好的性能 - 更小的包体积,更快的渲染速度
  • 🔧 Composition API - 更灵活的逻辑复用方式
  • 📦 更好的 TypeScript 支持 - 原生 TypeScript 支持
  • 🌳 Tree-shaking 友好 - 按需引入,减少包体积

环境准备

安装 Node.js

确保您的系统已安装 Node.js 16+ 版本:

bash
node --version
npm --version

创建 Vue 3 项目

使用 Vite 创建新项目(推荐):

bash
npm create vue@latest my-vue-app
cd my-vue-app
npm install
npm run dev

或使用 Vue CLI:

bash
npm install -g @vue/cli
vue create my-vue-app
cd my-vue-app
npm run serve

基础语法

模板语法

Vue 使用基于 HTML 的模板语法:

vue
<template>
  <div>
    <!-- 文本插值 -->
    <h1>{{ message }}</h1>
    
    <!-- 属性绑定 -->
    <img :src="imageUrl" :alt="imageAlt">
    
    <!-- 事件监听 -->
    <button @click="handleClick">点击我</button>
    
    <!-- 条件渲染 -->
    <p v-if="isVisible">这段文字是可见的</p>
    
    <!-- 列表渲染 -->
    <ul>
      <li v-for="item in items" :key="item.id">
        {{ item.name }}
      </li>
    </ul>
  </div>
</template>

响应式数据

使用 refreactive 创建响应式数据:

vue
<script setup>
import { ref, reactive } from 'vue'

// 基本类型使用 ref
const count = ref(0)
const message = ref('Hello Vue 3!')

// 对象类型使用 reactive
const user = reactive({
  name: '张三',
  age: 25,
  email: 'zhangsan@example.com'
})

// 修改数据
const increment = () => {
  count.value++
}

const updateUser = () => {
  user.name = '李四'
  user.age = 30
}
</script>

组件基础

单文件组件

Vue 组件通常写在 .vue 文件中:

vue
<!-- UserCard.vue -->
<template>
  <div class="user-card">
    <img :src="user.avatar" :alt="user.name" class="avatar">
    <div class="info">
      <h3>{{ user.name }}</h3>
      <p>{{ user.email }}</p>
      <button @click="$emit('follow', user.id)">关注</button>
    </div>
  </div>
</template>

<script setup>
// 定义 props
const props = defineProps({
  user: {
    type: Object,
    required: true
  }
})

// 定义 emits
const emit = defineEmits(['follow'])
</script>

<style scoped>
.user-card {
  display: flex;
  padding: 16px;
  border: 1px solid #ddd;
  border-radius: 8px;
  margin-bottom: 16px;
}

.avatar {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  margin-right: 16px;
}

.info h3 {
  margin: 0 0 8px 0;
  color: #333;
}

.info p {
  margin: 0 0 12px 0;
  color: #666;
}
</style>

使用组件

vue
<template>
  <div>
    <h1>用户列表</h1>
    <UserCard 
      v-for="user in users" 
      :key="user.id"
      :user="user"
      @follow="handleFollow"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import UserCard from './components/UserCard.vue'

const users = ref([
  {
    id: 1,
    name: '张三',
    email: 'zhangsan@example.com',
    avatar: '/avatars/zhangsan.svg'
  },
  {
    id: 2,
    name: '李四',
    email: 'lisi@example.com',
    avatar: '/avatars/lisi.svg'
  }
])

const handleFollow = (userId) => {
  console.log(`关注用户: ${userId}`)
}
</script>

Composition API

基本用法

Composition API 提供了更灵活的逻辑组织方式:

vue
<script setup>
import { ref, computed, watch, onMounted } from 'vue'

// 响应式状态
const count = ref(0)
const name = ref('')

// 计算属性
const doubleCount = computed(() => count.value * 2)

// 侦听器
watch(count, (newValue, oldValue) => {
  console.log(`count 从 ${oldValue} 变为 ${newValue}`)
})

// 生命周期钩子
onMounted(() => {
  console.log('组件已挂载')
})

// 方法
const increment = () => {
  count.value++
}
</script>

组合式函数

创建可复用的逻辑:

javascript
// 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
  }
}

使用组合式函数:

vue
<script setup>
import { useCounter } from '@/composables/useCounter'

const { count, doubleCount, increment, decrement, reset } = useCounter(10)
</script>

<template>
  <div>
    <p>计数: {{ count }}</p>
    <p>双倍: {{ doubleCount }}</p>
    <button @click="increment">+1</button>
    <button @click="decrement">-1</button>
    <button @click="reset">重置</button>
  </div>
</template>

状态管理

使用 Pinia

安装 Pinia:

bash
npm install pinia

创建 store:

javascript
// stores/user.js
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    currentUser: null,
    users: []
  }),
  
  getters: {
    isLoggedIn: (state) => !!state.currentUser,
    userCount: (state) => state.users.length
  },
  
  actions: {
    async login(credentials) {
      try {
        const response = await api.login(credentials)
        this.currentUser = response.data.user
      } catch (error) {
        throw error
      }
    },
    
    logout() {
      this.currentUser = null
    },
    
    async fetchUsers() {
      try {
        const response = await api.getUsers()
        this.users = response.data
      } catch (error) {
        console.error('获取用户列表失败:', error)
      }
    }
  }
})

在组件中使用:

vue
<script setup>
import { useUserStore } from '@/stores/user'

const userStore = useUserStore()

// 访问状态
console.log(userStore.currentUser)
console.log(userStore.isLoggedIn)

// 调用 actions
const handleLogin = async () => {
  try {
    await userStore.login({ email, password })
    router.push('/dashboard')
  } catch (error) {
    console.error('登录失败:', error)
  }
}
</script>

路由管理

使用 Vue Router

安装 Vue Router:

bash
npm install vue-router@4

配置路由:

javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
import User from '@/views/User.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: About
  },
  {
    path: '/user/:id',
    name: 'User',
    component: User,
    props: true
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

在组件中使用:

vue
<script setup>
import { useRouter, useRoute } from 'vue-router'

const router = useRouter()
const route = useRoute()

// 编程式导航
const goToUser = (userId) => {
  router.push(`/user/${userId}`)
}

// 获取路由参数
console.log(route.params.id)
</script>

<template>
  <div>
    <!-- 声明式导航 -->
    <router-link to="/">首页</router-link>
    <router-link to="/about">关于</router-link>
    
    <!-- 路由出口 -->
    <router-view />
  </div>
</template>

实战项目

让我们创建一个简单的待办事项应用:

vue
<!-- TodoApp.vue -->
<template>
  <div class="todo-app">
    <h1>待办事项</h1>
    
    <!-- 添加新任务 -->
    <form @submit.prevent="addTodo">
      <input 
        v-model="newTodo" 
        placeholder="输入新任务..."
        required
      >
      <button type="submit">添加</button>
    </form>
    
    <!-- 任务列表 -->
    <ul class="todo-list">
      <li 
        v-for="todo in filteredTodos" 
        :key="todo.id"
        :class="{ completed: todo.completed }"
      >
        <input 
          type="checkbox" 
          v-model="todo.completed"
        >
        <span>{{ todo.text }}</span>
        <button @click="removeTodo(todo.id)">删除</button>
      </li>
    </ul>
    
    <!-- 过滤器 -->
    <div class="filters">
      <button 
        v-for="filter in filters" 
        :key="filter"
        :class="{ active: currentFilter === filter }"
        @click="currentFilter = filter"
      >
        {{ filter }}
      </button>
    </div>
    
    <!-- 统计信息 -->
    <p>
      总计: {{ todos.length }} | 
      已完成: {{ completedCount }} | 
      未完成: {{ remainingCount }}
    </p>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'

// 状态
const newTodo = ref('')
const todos = ref([])
const currentFilter = ref('全部')
const filters = ['全部', '未完成', '已完成']

// 计算属性
const completedCount = computed(() => 
  todos.value.filter(todo => todo.completed).length
)

const remainingCount = computed(() => 
  todos.value.filter(todo => !todo.completed).length
)

const filteredTodos = computed(() => {
  switch (currentFilter.value) {
    case '未完成':
      return todos.value.filter(todo => !todo.completed)
    case '已完成':
      return todos.value.filter(todo => todo.completed)
    default:
      return todos.value
  }
})

// 方法
const addTodo = () => {
  if (newTodo.value.trim()) {
    todos.value.push({
      id: Date.now(),
      text: newTodo.value.trim(),
      completed: false
    })
    newTodo.value = ''
  }
}

const removeTodo = (id) => {
  const index = todos.value.findIndex(todo => todo.id === id)
  if (index > -1) {
    todos.value.splice(index, 1)
  }
}
</script>

<style scoped>
.todo-app {
  max-width: 600px;
  margin: 0 auto;
  padding: 20px;
}

.todo-list {
  list-style: none;
  padding: 0;
}

.todo-list li {
  display: flex;
  align-items: center;
  padding: 10px;
  border-bottom: 1px solid #eee;
}

.todo-list li.completed span {
  text-decoration: line-through;
  color: #999;
}

.filters button {
  margin-right: 10px;
  padding: 5px 10px;
  border: 1px solid #ddd;
  background: white;
  cursor: pointer;
}

.filters button.active {
  background: #007bff;
  color: white;
}
</style>

下一步学习

恭喜您完成了 Vue 3 基础教程!接下来可以学习:

  1. Vue 3 进阶特性
  2. Vue 生态系统
  3. Vue 3 + TypeScript
  4. Vue 3 性能优化

参考资源


继续您的 Vue 3 学习之旅吧!🚀

vitepress开发指南