Vue3新特性Composition API实战指南
作者:天天进步2015
前言
Vue3的发布标志着这个流行前端框架的重大升级。其中最引人注目的变化是引入了Composition API,它为开发者提供了一种全新的组织组件逻辑的方式。本文将深入探讨Vue3的新特性,特别是Composition API的实际应用,帮助开发者掌握这一强大工具。
Vue3核心新特性概览
Vue3相比Vue2有许多显著的改进:
- 性能提升:重写虚拟DOM实现,渲染速度提升1.3~2倍,内存占用减少一半
- Tree-shaking支持:更好的打包优化,减小应用体积
- Composition API:全新的逻辑组织和复用方式
- Teleport组件:可以将内容渲染到DOM树的任何位置
- Fragments:组件可以有多个根节点
- Suspense:处理异步组件的新方式
- TypeScript支持:更完善的类型推断
- 自定义渲染器API:更容易创建自定义渲染器
Composition API基础
Composition API是Vue3最重要的特性之一,它提供了一种新的组织组件逻辑的方式。
基本结构
<script setup> import { ref, computed, onMounted } from 'vue' // 响应式状态 const count = ref(0) // 计算属性 const doubleCount = computed(() => count.value * 2) // 方法 function increment() { count.value++ } // 生命周期钩子 onMounted(() => { console.log('组件已挂载') }) </script> <template> <div> <p>计数: {{ count }}</p> <p>双倍计数: {{ doubleCount }}</p> <button @click="increment">增加</button> </div> </template>
<script setup>语法
Vue3.2引入的<script setup>
是使用Composition API的推荐方式,它具有以下优势:
- 更少的样板代码
- 变量和函数直接在模板中可用
- 更好的运行时性能
- 更好的IDE类型推断
对比Options API与Composition API
Options API(Vue2的主要方式)
export default { data() { return { count: 0 } }, computed: { doubleCount() { return this.count * 2 } }, methods: { increment() { this.count++ } }, mounted() { console.log('组件已挂载') } }
Composition API(Vue3推荐方式)
import { ref, computed, onMounted } from 'vue' export default { setup() { const count = ref(0) const doubleCount = computed(() => count.value * 2) function increment() { count.value++ } onMounted(() => { console.log('组件已挂载') }) return { count, doubleCount, increment } } }
主要区别与优势
- 逻辑组织:Composition API按照逻辑关注点组织代码,而非选项类型
- 逻辑复用:更容易提取和复用逻辑,没有this的困扰
- 类型推断:对TypeScript更友好
- 打包优化:更好的Tree-shaking支持,减小应用体积
响应式系统
Vue3的响应式系统是完全重写的,基于ES6的Proxy:
核心响应式API
- ref:处理基本类型的响应式数据
- reactive:处理对象类型的响应式数据
- computed:创建计算属性
- watch/watchEffect:监听数据变化
import { ref, reactive, computed, watch, watchEffect } from 'vue' // 基本类型的响应式 const count = ref(0) console.log(count.value) // 访问值需要.value // 对象类型的响应式 const state = reactive({ name: '张三', age: 25 }) console.log(state.name) // 直接访问属性 // 计算属性 const doubleCount = computed(() => count.value * 2) // 监听特定数据 watch(count, (newValue, oldValue) => { console.log(`count从${oldValue}变为${newValue}`) }) // 自动追踪依赖 watchEffect(() => { console.log(`count: ${count.value}, name: ${state.name}`) })
ref vs reactive
ref | reactive | |
---|---|---|
适用类型 | 任何类型 | 仅对象类型 |
访问方式 | .value(模板中自动解包) | 直接访问属性 |
解构行为 | 保持响应性 | 失去响应性(需使用toRefs) |
使用场景 | 简单值或需要解构时 | 复杂对象且不需解构时 |
生命周期钩子
Composition API提供了一套新的生命周期钩子:
Options API | Composition API |
---|---|
beforeCreate | setup() |
created | setup() |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
import { onMounted, onBeforeUnmount } from 'vue' export default { setup() { onMounted(() => { console.log('组件已挂载') }) onBeforeUnmount(() => { console.log('组件即将卸载') }) } }
组合式函数(Composables)
Composition API最强大的特性之一是能够轻松创建和使用可复用的组合式函数(Composables)。
创建一个组合式函数
// useCounter.js import { ref, computed } from 'vue' export function useCounter(initialValue = 0) { const count = ref(initialValue) const doubleCount = computed(() => count.value * 2) function increment() { count.value++ } function decrement() { count.value-- } return { count, doubleCount, increment, decrement } }
使用组合式函数
<script setup> import { useCounter } from './composables/useCounter' const { count, doubleCount, increment, decrement } = useCounter(10) </script> <template> <div> <p>计数: {{ count }}</p> <p>双倍计数: {{ doubleCount }}</p> <button @click="increment">增加</button> <button @click="decrement">减少</button> </div> </template>
常用组合式函数示例
1. 使用鼠标位置
// useMouse.js import { ref, onMounted, onUnmounted } from 'vue' export function useMouse() { const x = ref(0) const y = ref(0) function update(event) { x.value = event.pageX y.value = event.pageY } onMounted(() => window.addEventListener('mousemove', update)) onUnmounted(() => window.removeEventListener('mousemove', update)) return { x, y } }
2. 使用本地存储
// useLocalStorage.js import { ref, watch } from 'vue' export function useLocalStorage(key, defaultValue) { const value = ref(JSON.parse(localStorage.getItem(key)) || defaultValue) watch(value, val => { localStorage.setItem(key, JSON.stringify(val)) }) return value }
实战案例:待办事项应用
下面是一个使用Composition API构建的简单待办事项应用:
<script setup> import { ref, computed } from 'vue' import { useLocalStorage } from './composables/useLocalStorage' // 使用本地存储保存待办事项 const todos = useLocalStorage('todos', []) const newTodo = ref('') // 计算属性:未完成的待办事项数量 const remaining = computed(() => { return todos.value.filter(todo => !todo.completed).length }) // 添加待办事项 function addTodo() { if (newTodo.value.trim()) { todos.value.push({ id: Date.now(), text: newTodo.value, completed: false }) newTodo.value = '' } } // 删除待办事项 function removeTodo(id) { todos.value = todos.value.filter(todo => todo.id !== id) } // 切换待办事项完成状态 function toggleTodo(id) { const todo = todos.value.find(todo => todo.id === id) if (todo) { todo.completed = !todo.completed } } </script> <template> <div class="todo-app"> <h1>待办事项列表</h1> <!-- 添加待办事项 --> <div class="add-todo"> <input v-model="newTodo" @keyup.enter="addTodo" placeholder="添加新的待办事项" /> <button @click="addTodo">添加</button> </div> <!-- 待办事项列表 --> <ul class="todo-list"> <li v-for="todo in todos" :key="todo.id" :class="{ completed: todo.completed }"> <input type="checkbox" :checked="todo.completed" @change="toggleTodo(todo.id)" /> <span>{{ todo.text }}</span> <button @click="removeTodo(todo.id)">删除</button> </li> </ul> <!-- 底部状态 --> <div class="todo-footer" v-if="todos.length"> <span>{{ remaining }} 项待完成</span> </div> </div> </template> <style scoped> .todo-app { max-width: 500px; margin: 0 auto; padding: 20px; font-family: Arial, sans-serif; } .add-todo { display: flex; margin-bottom: 20px; } .add-todo input { flex-grow: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px 0 0 4px; } .add-todo button { padding: 8px 16px; background-color: #4CAF50; color: white; border: none; border-radius: 0 4px 4px 0; cursor: pointer; } .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; } .todo-list li span { flex-grow: 1; margin: 0 15px; } .todo-list li button { background-color: #f44336; color: white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; } .todo-footer { margin-top: 20px; color: #666; } </style>
性能优化最佳实践
使用Composition API时,需要注意一些性能优化的最佳实践:
1. 避免不必要的响应式转换
// 不好的做法:创建不需要响应式的大型数据 const data = reactive(hugeData) // 好的做法:仅对需要响应式的数据使用reactive或ref const data = hugeData // 普通对象 const reactiveState = reactive({ count: 0 }) // 只对需要响应式的状态使用reactive
2. 使用shallowRef和shallowReactive
对于大型对象,如果只需要跟踪顶层属性的变化:
import { shallowRef, shallowReactive } from 'vue' // 只有.value的变更是响应式的,而不会深度追踪 const state = shallowRef({ count: 0 }) // 只有顶层属性的变更是响应式的 const user = shallowReactive({ name: '张三', address: { city: '北京' } // 修改city不会触发更新 })
3. 使用v-once和v-memo
<!-- 只渲染一次 --> <div v-once>{{ expensiveComputation() }}</div> <!-- 依赖项变化时才重新渲染 --> <div v-memo="[item.id]">{{ item.name }}</div>
4. 合理使用计算属性缓存
// 不好的做法:在方法中计算 function getFilteredItems() { return items.value.filter(item => item.price > 100) } // 好的做法:使用计算属性 const filteredItems = computed(() => { return items.value.filter(item => item.price > 100) })
总结与展望
Vue3的Composition API为我们提供了一种更灵活、更强大的组织组件逻辑的方式。它解决了Vue2中存在的许多问题,如逻辑复用困难、组件过于臃肿等。虽然学习曲线略陡,但一旦掌握,将极大提升开发效率和代码质量。
随着Vue生态系统的发展,我们可以期待更多基于Composition API的库和工具,以及更多的最佳实践和模式的出现。如果你还在使用Options API,不妨开始尝试Composition API,体验Vue3带来的全新开发体验。
以上就是Vue3新特性Composition API实战指南的详细内容,更多关于Vue3 Composition API指南的资料请关注脚本之家其它相关文章!