当前位置: 首页 > news >正文

Vue3 Composition API

一、setup 配置详解

1.1 Vue2 中的 $attrs 和 $slots 回顾

在 Vue2 中,父组件通过标签属性向子组件传递数据时,通常需要在子组件中使用 props 接收。但即使不声明接收,这些属性也会存在于子组件实例的 $attrs 中。

示例对比:

javascript

// Vue2 子组件
export default {props: ['receivedProp'], // 声明接收的属性created() {console.log(this.$attrs) // 未声明接收的属性会出现在这里console.log(this.$slots) // 父组件传递的插槽内容}
}

1.2 Vue3 setup 的两个关键点

执行时机
  • setup 在 beforeCreate 之前执行

  • 此时组件实例尚未创建,this 为 undefined

参数解析

setup 接收两个参数:

  1. props:包含组件外部传递且内部声明接收的属性

  2. context:上下文对象,包含:

    • attrs:未在 props 中声明的属性(相当于 Vue2 的 $attrs

    • slots:接收的插槽内容(相当于 Vue2 的 $slots

    • emit:触发自定义事件的函数(相当于 Vue2 的 $emit

完整示例:

javascript

import { defineComponent } from 'vue'export default defineComponent({props: ['title'],emits: ['change'], // 必须声明自定义事件setup(props, { attrs, slots, emit }) {console.log(props.title) // 访问声明的 propconsole.log(attrs.customAttr) // 访问未声明的属性const handleClick = () => {emit('change', 'new value') // 触发事件}return {handleClick}}
})

二、响应式进阶:计算属性与监视

2.1 computed 函数

Vue3 的 computed 用法与 Vue2 类似,但更灵活:

javascript

import { ref, computed } from 'vue'setup() {const firstName = ref('张')const lastName = ref('三')// 只读计算属性const fullName = computed(() => `${firstName.value} ${lastName.value}`)// 可写计算属性const editableName = computed({get: () => `${firstName.value} ${lastName.value}`,set: (newValue) => {const [first, last] = newValue.split(' ')firstName.value = firstlastName.value = last}})return { fullName, editableName }
}

2.2 watch 函数详解

Vue3 的 watch 功能强大但有一些注意事项:

javascript

import { ref, reactive, watch } from 'vue'setup() {const count = ref(0)const state = reactive({user: {name: 'Alice',age: 25}})// 情况1:监视 refwatch(count, (newVal, oldVal) => {console.log(`count变化: ${oldVal} -> ${newVal}`)})// 情况2:监视多个 refwatch([count, anotherRef], ([newCount, newAnother], [oldCount, oldAnother]) => {// 处理变化})// 情况3:监视 reactive 对象(注意 oldValue 问题)watch(() => state.user,(newUser, oldUser) => {// oldUser 可能与 newUser 相同!},{ deep: true } // 虽然默认开启,但显式声明更清晰)// 情况4:监视 reactive 对象的特定属性watch(() => state.user.age,(newAge, oldAge) => {// 这里能正确获取 oldAge})
}

2.3 watchEffect 智能监听

watchEffect 自动追踪回调中的响应式依赖:

javascript

import { ref, watchEffect } from 'vue'setup() {const count = ref(0)const message = ref('')watchEffect(() => {console.log(`count: ${count.value}, message: ${message.value}`)// 会自动追踪 count 和 message 的变化})// 实际应用:自动取消之前的请求const searchQuery = ref('')watchEffect(async (onCleanup) => {const query = searchQuery.valueconst controller = new AbortController()onCleanup(() => controller.abort()) // 清除副作用if (query) {const results = await fetchResults(query, {signal: controller.signal})// 处理结果}})
}

三、生命周期全解析

3.1 Vue2 与 Vue3 生命周期对比

Vue2 生命周期Vue3 生命周期 (Options API)Vue3 Composition API
beforeCreatebeforeCreatesetup()
createdcreatedsetup()
beforeMountbeforeMountonBeforeMount
mountedmountedonMounted
beforeUpdatebeforeUpdateonBeforeUpdate
updatedupdatedonUpdated
beforeDestroybeforeUnmountonBeforeUnmount
destroyedunmountedonUnmounted

3.2 组合式 API 生命周期使用

javascript

import { onMounted, onUnmounted } from 'vue'setup() {// 鼠标位置跟踪示例const x = ref(0)const y = ref(0)const updatePosition = (e) => {x.value = e.pageXy.value = e.pageY}onMounted(() => {window.addEventListener('mousemove', updatePosition)})onUnmounted(() => {window.removeEventListener('mousemove', updatePosition)})return { x, y }
}

四、自定义 Hook 实践

自定义 Hook 是 Vue3 代码复用的利器:

4.1 鼠标位置跟踪 Hook

javascript

// hooks/useMousePosition.js
import { ref, onMounted, onUnmounted } from 'vue'export function useMousePosition() {const x = ref(0)const y = ref(0)const updatePosition = (e) => {x.value = e.pageXy.value = e.pageY}onMounted(() => {window.addEventListener('mousemove', updatePosition)})onUnmounted(() => {window.removeEventListener('mousemove', updatePosition)})return { x, y }
}// 在组件中使用
import { useMousePosition } from './hooks/useMousePosition'setup() {const { x, y } = useMousePosition()return { x, y }
}

4.2 数据请求 Hook

javascript

// hooks/useFetch.js
import { ref, isRef, unref, watchEffect } from 'vue'export function useFetch(url) {const data = ref(null)const error = ref(null)const loading = ref(false)const fetchData = async () => {loading.value = truetry {const response = await fetch(unref(url))data.value = await response.json()error.value = null} catch (err) {error.value = err.message} finally {loading.value = false}}if (isRef(url)) {watchEffect(fetchData)} else {fetchData()}return { data, error, loading, retry: fetchData }
}

五、toRef 与 toRefs 深度解析

5.1 toRef 使用场景

javascript

import { reactive, toRef } from 'vue'setup() {const state = reactive({name: 'Alice',age: 25})// 保持响应式连接const nameRef = toRef(state, 'name')setTimeout(() => {state.name = 'Bob' // nameRef 也会更新}, 1000)return {nameRef // 可以在模板中直接使用}
}

5.2 toRefs 解构响应式对象

javascript

import { reactive, toRefs } from 'vue'setup() {const state = reactive({name: 'Alice',age: 25,address: {city: 'Beijing'}})// 解构后仍保持响应式const { name, age } = toRefs(state)// 嵌套对象需要单独处理const { city } = toRefs(state.address)return {name,age,city}
}

六、Vue3 新组件实战

6.1 Fragment 片段组件

Vue3 不再需要根标签:

vue

<template><header>...</header><main>...</main><footer>...</footer>
</template>

6.2 Teleport 传送门

将组件渲染到 DOM 中的其他位置:

vue

<template><button @click="showModal = true">打开弹窗</button><Teleport to="body"><div v-if="showModal" class="modal"><div class="modal-content"><h2>标题</h2><p>内容...</p><button @click="showModal = false">关闭</button></div></div></Teleport>
</template><script>
import { ref } from 'vue'
export default {setup() {const showModal = ref(false)return { showModal }}
}
</script>

6.3 Suspense 异步组件

优雅处理异步组件加载状态:

vue

<template><Suspense><template #default><AsyncComponent /></template><template #fallback><div class="loading-spinner">加载中...</div></template></Suspense>
</template><script>
import { defineAsyncComponent } from 'vue'const AsyncComponent = defineAsyncComponent(() =>import('./components/AsyncComponent.vue')
)export default {components: {AsyncComponent}
}
</script>

七、Composition API 优势总结

  1. 更好的代码组织:相关功能代码集中在一起

  2. 更好的逻辑复用:通过自定义 Hook 实现

  3. 更好的类型推断:对 TypeScript 支持更友好

  4. 更小的生产包:Tree-shaking 友好

对比示例:

javascript

// Options API 方式
export default {data() {return {count: 0,searchQuery: ''}},computed: {filteredList() {// 基于 searchQuery 过滤列表}},methods: {increment() {this.count++}},mounted() {// 初始化代码}
}// Composition API 方式
import { ref, computed, onMounted } from 'vue'export default {setup() {const count = ref(0)const searchQuery = ref('')const increment = () => {count.value++}const filteredList = computed(() => {// 过滤逻辑})onMounted(() => {// 初始化代码})return {count,searchQuery,increment,filteredList}}
}

结语

Vue3 的 Composition API 为开发者提供了更灵活、更强大的代码组织方式。通过本文的详细解析和丰富示例,相信你已经掌握了其核心概念和使用技巧。在实际开发中,建议从简单功能开始逐步尝试组合式 API,体验其带来的开发效率提升和代码可维护性优势。

http://www.dtcms.com/a/306047.html

相关文章:

  • 独立站如何吃掉平台蛋糕?DTC模式下的成本重构与利润跃升
  • 八种AI记忆术,重构智能体的“大脑”
  • Unity_XR控制手部动画
  • RFID 系统行业前沿洞察:技术跃迁与生态重构
  • 国内好用的智能三防手机,适合户外、工业、公共安全等场景
  • 深入剖析Three.js中的关键帧动画
  • 闸机控制系统从设计到实现全解析 第 2 篇:数据库设计与 SqlSugar 集成方案
  • 笔记本电脑磁盘维护指南:WIN11系统磁盘维护完全手册
  • 不止 “听懂”,更能 “感知”!移远通信全新AI 音频模组 重新定义智能家居“听觉”逻辑
  • 自定心深凹槽参数检测装置及检测方法 - 激光频率梳 3D 轮廓检测
  • 视觉语言模型在视觉任务上的研究综述
  • 3D空间中的变换矩阵
  • 微软OpenAI展开深入谈判
  • Elasticsearch 文档操作管理:从增删改查到批量操作与数据类型
  • USB电源原理图学习笔记
  • 易基因:cfDNA甲基化突破性液体活检方法cfMeDIP-seq临床验证研究 助力头颈癌早期MRD检测|Ann Oncol/IF65
  • 【达梦数据库】参数实践积累
  • PyTorch API
  • HPC超算、集群计算
  • 基于Java对于PostgreSQL多层嵌套JSON 字段判重
  • 18.编译优化
  • SQL167 连续签到领金币
  • MySQL 9 Group Replication维护
  • 达梦数据库(DM Database)角色管理详解|了解DM预定义的各种角色,掌握角色创建、角色的分配和回收
  • C++:STL中list的使用和模拟实现
  • Keepalived 实战
  • 《C++二叉搜索树原理剖析:从原理到高效实现教学》
  • 如何利用 Redis 的原子操作(INCR, DECR)实现分布式计数器?
  • Java 控制台用户登录系统(支持角色权限与自定义异常处理)
  • 生成模型实战 | GLOW详解与实现