Vue 3 是一个现代化的前端框架,在性能、开发体验和功能方面都有显著提升。下面详细介绍 Vue 3 的核心特性,并提供一个实际的示例。

Vue 3 核心特性

1. Composition API

  • 更灵活的逻辑组织和复用

  • 更好的 TypeScript 支持

  • 更清晰的逻辑关注点分离

2. 性能提升

  • 更小的打包体积(Tree-shaking 优化)

  • 更快的渲染速度

  • 更高效的重渲染机制

3. 更好的 TypeScript 支持

  • 完整的 TypeScript 类型定义

  • 更好的开发体验

实际示例:任务管理器

下面是一个使用 Vue 3 实现的任务管理器应用:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vue 3 任务管理器</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    body {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      min-height: 100vh;
      padding: 20px;
      color: #333;
    }
    .container {
      max-width: 800px;
      margin: 0 auto;
    }
    .app-title {
      text-align: center;
      color: white;
      margin-bottom: 30px;
      font-size: 2.5rem;
      text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
    }
    .card {
      background: white;
      border-radius: 12px;
      box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
      padding: 25px;
      margin-bottom: 20px;
    }
    .input-group {
      display: flex;
      margin-bottom: 20px;
    }
    .task-input {
      flex: 1;
      padding: 12px 15px;
      border: 2px solid #e1e5e9;
      border-radius: 8px 0 0 8px;
      font-size: 16px;
      transition: border-color 0.3s;
    }
    .task-input:focus {
      outline: none;
      border-color: #667eea;
    }
    .add-btn {
      background: #667eea;
      color: white;
      border: none;
      padding: 12px 25px;
      border-radius: 0 8px 8px 0;
      cursor: pointer;
      font-size: 16px;
      transition: background 0.3s;
    }
    .add-btn:hover {
      background: #5a6fd8;
    }
    .filter-buttons {
      display: flex;
      gap: 10px;
      margin-bottom: 20px;
    }
    .filter-btn {
      flex: 1;
      padding: 10px;
      background: #f1f3f4;
      border: none;
      border-radius: 6px;
      cursor: pointer;
      transition: all 0.3s;
    }
    .filter-btn.active {
      background: #667eea;
      color: white;
    }
    .task-list {
      list-style: none;
    }
    .task-item {
      display: flex;
      align-items: center;
      padding: 15px;
      border-bottom: 1px solid #eaeaea;
      transition: background 0.2s;
    }
    .task-item:hover {
      background: #f9f9f9;
    }
    .task-item:last-child {
      border-bottom: none;
    }
    .task-checkbox {
      margin-right: 15px;
      transform: scale(1.2);
    }
    .task-text {
      flex: 1;
      font-size: 16px;
    }
    .task-text.completed {
      text-decoration: line-through;
      color: #888;
    }
    .delete-btn {
      background: #ff6b6b;
      color: white;
      border: none;
      padding: 8px 12px;
      border-radius: 6px;
      cursor: pointer;
      transition: background 0.3s;
    }
    .delete-btn:hover {
      background: #ff5252;
    }
    .stats {
      display: flex;
      justify-content: space-between;
      margin-top: 20px;
      padding-top: 15px;
      border-top: 1px solid #eaeaea;
      color: #666;
    }
    .empty-state {
      text-align: center;
      padding: 30px;
      color: #888;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <h1 class="app-title">Vue 3 任务管理器</h1>
      
      <div class="card">
        <div class="input-group">
          <input 
            v-model="newTask" 
            @keyup.enter="addTask"
            class="task-input" 
            placeholder="添加新任务..."
            type="text"
          >
          <button @click="addTask" class="add-btn">添加任务</button>
        </div>
        
        <div class="filter-buttons">
          <button 
            @click="filter = 'all'" 
            :class="['filter-btn', { active: filter === 'all' }]"
          >
            全部
          </button>
          <button 
            @click="filter = 'active'" 
            :class="['filter-btn', { active: filter === 'active' }]"
          >
            待完成
          </button>
          <button 
            @click="filter = 'completed'" 
            :class="['filter-btn', { active: filter === 'completed' }]"
          >
            已完成
          </button>
        </div>
        
        <ul class="task-list">
          <li v-for="task in filteredTasks" :key="task.id" class="task-item">
            <input 
              type="checkbox" 
              v-model="task.completed" 
              class="task-checkbox"
            >
            <span 
              :class="['task-text', { completed: task.completed }]"
            >
              {{ task.text }}
            </span>
            <button @click="removeTask(task.id)" class="delete-btn">删除</button>
          </li>
        </ul>
        
        <div v-if="tasks.length === 0" class="empty-state">
          还没有任务,添加一个开始吧!
        </div>
        
        <div v-if="tasks.length > 0" class="stats">
          <span>总计: {{ tasks.length }} 个任务</span>
          <span>已完成: {{ completedTasksCount }} 个</span>
          <span>待完成: {{ activeTasksCount }} 个</span>
        </div>
      </div>
    </div>
  </div>

  <script>
    const { createApp, ref, computed, onMounted } = Vue;
    
    createApp({
      setup() {
        // 使用ref创建响应式数据
        const newTask = ref('');
        const tasks = ref([]);
        const filter = ref('all');
        
        // 计算属性
        const filteredTasks = computed(() => {
          switch (filter.value) {
            case 'active':
              return tasks.value.filter(task => !task.completed);
            case 'completed':
              return tasks.value.filter(task => task.completed);
            default:
              return tasks.value;
          }
        });
        
        const completedTasksCount = computed(() => {
          return tasks.value.filter(task => task.completed).length;
        });
        
        const activeTasksCount = computed(() => {
          return tasks.value.filter(task => !task.completed).length;
        });
        
        // 方法
        const addTask = () => {
          if (newTask.value.trim() === '') return;
          
          tasks.value.push({
            id: Date.now(),
            text: newTask.value.trim(),
            completed: false
          });
          
          newTask.value = '';
          saveTasks();
        };
        
        const removeTask = (id) => {
          tasks.value = tasks.value.filter(task => task.id !== id);
          saveTasks();
        };
        
        // 本地存储功能
        const saveTasks = () => {
          localStorage.setItem('vue3-tasks', JSON.stringify(tasks.value));
        };
        
        const loadTasks = () => {
          const savedTasks = localStorage.getItem('vue3-tasks');
          if (savedTasks) {
            tasks.value = JSON.parse(savedTasks);
          }
        };
        
        // 生命周期钩子
        onMounted(() => {
          loadTasks();
        });
        
        // 返回模板中需要使用的数据和方法
        return {
          newTask,
          tasks,
          filter,
          filteredTasks,
          completedTasksCount,
          activeTasksCount,
          addTask,
          removeTask
        };
      }
    }).mount('#app');
  </script>
</body>
</html>

Vue 3 核心概念解析

1. 组合式 API (Composition API)

Vue 3 引入了组合式 API,它解决了选项式 API 在复杂组件中逻辑关注点分离的问题。

// 选项式 API (Vue 2)
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  mounted() {
    console.log('组件已挂载')
  }
}

// 组合式 API (Vue 3)
import { ref, onMounted } from 'vue'

export default {
  setup() {
    const count = ref(0)
    
    function increment() {
      count.value++
    }
    
    onMounted(() => {
      console.log('组件已挂载')
    })
    
    return {
      count,
      increment
    }
  }
}

2. 响应式系统

Vue 3 使用 Proxy 重写了响应式系统,提供了更好的性能和完善的响应式能力。

import { ref, reactive, computed, watch } from 'vue'

// ref 用于基本类型
const count = ref(0)

// reactive 用于对象
const state = reactive({
  user: {
    name: '张三',
    age: 25
  }
})

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

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

3. 生命周期钩子

Vue 3 的生命周期钩子在组合式 API 中有对应的函数:

import { onMounted, onUpdated, onUnmounted } from 'vue'

export default {
  setup() {
    onMounted(() => {
      console.log('组件挂载完成')
    })
    
    onUpdated(() => {
      console.log('组件已更新')
    })
    
    onUnmounted(() => {
      console.log('组件已卸载')
    })
  }
}

什么是组件?

组件是 Vue 3 应用的核心构建块,它允许我们将 UI 拆分为独立、可复用的代码片段。每个组件封装了自己的结构、样式和行为,可以像 HTML 元素一样在模板中使用。

组件的基本结构

单文件组件 (SFC)

Vue 组件通常以 .vue 文件形式存在,包含三个部分:

<template>
  <!-- HTML 模板 -->
  <div class="counter">
    <button @click="decrement">-</button>
    <span>{{ count }}</span>
    <button @click="increment">+</button>
  </div>
</template>

<script>
// JavaScript 逻辑
export default {
  name: 'Counter',
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    },
    decrement() {
      this.count--
    }
  }
}
</script>

<style>
/* CSS 样式 */
.counter {
  display: flex;
  gap: 10px;
  align-items: center;
}
</style>

组件的注册

全局注册

import { createApp } from 'vue'
import MyComponent from './MyComponent.vue'

const app = createApp({})
app.component('my-component', MyComponent)

局部注册

import ComponentA from './ComponentA.vue'
import ComponentB from './ComponentB.vue'

export default {
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
}

组件通信

1. Props (父传子)

<!-- 子组件 -->
<template>
  <div>
    <h3>{{ title }}</h3>
    <p>{{ content }}</p>
  </div>
</template>

<script>
export default {
  props: {
    title: {
      type: String,
      required: true
    },
    content: {
      type: String,
      default: '默认内容'
    }
  }
}
</script>

<!-- 父组件使用 -->
<child-component title="文章标题" content="文章内容" />

2. 自定义事件 (子传父)

vue

<!-- 子组件 -->
<template>
  <button @click="notifyParent">通知父组件</button>
</template>

<script>
export default {
  methods: {
    notifyParent() {
      this.$emit('child-event', '来自子组件的消息')
    }
  }
}
</script>

<!-- 父组件 -->
<template>
  <child-component @child-event="handleChildEvent" />
</template>

<script>
export default {
  methods: {
    handleChildEvent(message) {
      console.log(message) // "来自子组件的消息"
    }
  }
}
</script>

3. 使用组合式 API 的组件通信

vue

<script setup>
// 定义 props
const props = defineProps({
  title: String,
  count: Number
})

// 定义事件
const emit = defineEmits(['update:count'])

// 触发事件
const updateCount = (newCount) => {
  emit('update:count', newCount)
}
</script>

组件生命周期

Vue 3 组件有完整的生命周期钩子:

<script>
export default {
  setup() {
    // 组合式 API 生命周期
    onBeforeMount(() => {})
    onMounted(() => {})
    onBeforeUpdate(() => {})
    onUpdated(() => {})
    onBeforeUnmount(() => {})
    onUnmounted(() => {})
  },
  
  // 选项式 API 生命周期
  beforeCreate() {},
  created() {},
  beforeMount() {},
  mounted() {},
  beforeUpdate() {},
  updated() {},
  beforeUnmount() {},
  unmounted() {}
}
</script>

插槽 (Slots)

默认插槽

vue

<!-- 子组件 -->
<template>
  <div class="card">
    <slot>默认内容</slot>
  </div>
</template>

<!-- 父组件使用 -->
<card-component>
  <p>这是插入的内容</p>
</card-component>

具名插槽

vue

<!-- 子组件 -->
<template>
  <div class="layout">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>

<!-- 父组件使用 -->
<layout-component>
  <template v-slot:header>
    <h1>页面标题</h1>
  </template>
  
  <p>主要内容</p>
  
  <template #footer>  <!-- # 是 v-slot 的简写 -->
    <p>页脚内容</p>
  </template>
</layout-component>

作用域插槽

vue

<!-- 子组件 -->
<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      <slot :item="item"></slot>
    </li>
  </ul>
</template>

<!-- 父组件使用 -->
<list-component :items="userList">
  <template v-slot:default="slotProps">
    <span>{{ slotProps.item.name }} - {{ slotProps.item.age }}</span>
  </template>
</list-component>

依赖注入

用于跨层级组件通信:

<!-- 祖先组件 -->
<script>
import { provide } from 'vue'

export default {
  setup() {
    const theme = 'dark'
    provide('theme', theme)
  }
}
</script>

<!-- 后代组件 -->
<script>
import { inject } from 'vue'

export default {
  setup() {
    const theme = inject('theme', 'light') // 第二个参数是默认值
    return { theme }
  }
}
</script>

动态组件

<template>
  <component :is="currentComponent" />
</template>

<script>
import Home from './Home.vue'
import About from './About.vue'
import Contact from './Contact.vue'

export default {
  data() {
    return {
      currentComponent: 'Home'
    }
  },
  components: {
    Home,
    About,
    Contact
  }
}
</script>

异步组件

javascript

// 定义异步组件
const AsyncComponent = defineAsyncComponent(() => 
  import('./AsyncComponent.vue')
)

// 带加载状态和错误处理的异步组件
const AsyncComponentWithOptions = defineAsyncComponent({
  loader: () => import('./AsyncComponent.vue'),
  loadingComponent: LoadingComponent,
  errorComponent: ErrorComponent,
  delay: 200,
  timeout: 3000
})

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐