深入探索Vue 3组合式API

深入探索Vue 3组合式API

  • 深入探索Vue 3组合式API
    • 一、组合式API诞生背景
      • 1.1 Options API的局限性
      • 1.2 设计目标
      • 二、核心概念解析
        • 2.1 setup() 函数:组合式API的基石
        • 2.2 响应式系统:重新定义数据驱动
        • 2.3 生命周期:全新的接入方式
        • 2.4 响应式原理探秘
        • 2.5 组合式API中的上下文处理
    • 三、核心API深度解析
      • 3.1 响应式工具
      • 3.2 计算属性
      • 3.3 副作用管理
    • 四、逻辑复用模式
      • 4.1 自定义组合函数
      • 4.2 异步状态管理
    • 五、最佳实践
      • 5.1 代码组织模式
      • 5.2 TypeScript集成
    • 六、与Options API对比
      • 6.1 逻辑组织对比

深入探索Vue 3组合式API

一、组合式API诞生背景

1.1 Options API的局限性

  • 代码组织碎片化
  • 逻辑复用困难(mixins缺陷)
  • 类型支持不足

1.2 设计目标

  • 更好的逻辑复用
  • 更灵活的组织方式
  • 更好的类型推导
  • 渐进式采用策略

二、核心概念解析

2.1 setup() 函数:组合式API的基石

(1)函数特性与执行时机

export default {setup(props, context) {// 在beforeCreate之前执行// 无法访问this// 返回对象将暴露给模板和其他选项}
}
  • 执行顺序:在组件实例创建之前同步执行,位于beforeCreatecreated生命周期之间
  • 参数解析
    • props:响应式的props对象,结构会失去响应性
    • context:包含attrs、slots、emit的非响应式对象
  • 返回值
    • 返回对象属性将合并到模板渲染上下文
    • 可返回渲染函数直接控制视图输出

(2)props处理规范

import { defineComponent } from 'vue'interface Props {title: stringcount?: number
}export default defineComponent({props: {title: String,count: {type: Number,default: 0}},setup(props: Props) {// 使用watch监听props变化watch(() => props.count, (newVal) => {console.log('count changed:', newVal)})}
})
  • 类型安全:配合TypeScript实现严格类型校验
  • 不可解构:直接解构props会导致响应性丢失,需使用toRefs
  • 默认值处理:当父组件未传值时自动应用默认值

(3)上下文对象解析

setup(props, { attrs, slots, emit, expose }) {// 访问非响应式属性:console.log(attrs.class)// 检查插槽内容:const hasFooter = slots.footer// 事件触发:emit('submit', payload)// 暴露公共属性:expose({ publicMethod })
}
  • attrs:包含未在props中声明的属性
  • slots:访问通过插槽分发的内容(v-slot语法)
  • emit:替代this.$emit的事件触发方式
  • expose:控制组件实例对外暴露的公共方法

(4)响应式状态管理

const state = reactive({user: {name: 'Alice',posts: []},loading: false
})// 嵌套对象自动响应化
watchEffect(() => {console.log('User name changed:', state.user.name)
})
  • 深层响应性:reactive会递归转换对象属性
  • 数组处理:支持数组索引修改和length变更检测
  • 自动解包:在模板中访问ref无需.value,但JS环境中需要

2.2 响应式系统:重新定义数据驱动

(1)响应式核心原理

const targetMap = new WeakMap()function track(target, key) {// 收集依赖
}function trigger(target, key) {// 通知更新
}const handler = {get(target, key, receiver) {track(target, key)return Reflect.get(...arguments)},set(target, key, value, receiver) {const result = Reflect.set(...arguments)trigger(target, key)return result}
}
  • Proxy代理:基于ES6 Proxy实现属性访问拦截
  • 依赖追踪:通过WeakMap建立目标对象->属性->依赖的映射关系
  • 批量更新:Vue的调度机制确保多次状态变更合并为单次更新

(2)ref的进阶用法

// DOM元素引用
const inputRef = ref<HTMLInputElement | null>(null)// 组件挂载后访问
onMounted(() => {inputRef.value?.focus()
})// 模板引用
<template><input ref="inputRef">
</template>// 复杂类型处理
const state = ref({user: {name: 'Bob'}
})// 自动解包
state.value.user.name = 'Charlie'
  • 模板引用:替代this.$refs的声明方式
  • 类型标注:在TypeScript中明确指定引用类型
  • 对象嵌套:ref可以包裹复杂对象结构

(3)reactive的边界情况

// 响应式丢失场景
const state = reactive({ x: 0 })
let { x } = state // 值拷贝,失去响应性// 正确解构方式
const { x } = toRefs(state)// 数组处理特例
const list = reactive([1, 2, 3])
list = reactive([4,5,6]) // 错误!需要修改现有引用
list.push(4) // 正确方式// 使用readonly保护
const protectedState = readonly(state)
protectedState.x++ // 控制台警告
  • 引用替换限制:必须保持对象引用不变
  • 数组变异方法:push/pop等标准方法可触发更新
  • 只读保护:防止意外修改共享状态

(4)响应式工具函数对比

方法作用典型场景
toRef为源响应式对象属性创建refprops解构时保持响应性
toRefs转换整个响应式对象为普通对象从组合函数返回响应式状态
isProxy检查是否为代理对象调试响应式系统
isReactive检查reactive创建的代理类型判断
isReadonly检查只读代理安全校验
markRaw标记对象永不转为响应式性能优化/集成第三方库
shallowRef创建浅层ref大型对象性能优化
triggerRef手动触发shallowRef更新强制刷新界面

2.3 生命周期:全新的接入方式

(1)完整生命周期映射表

Options APIComposition API触发时机
beforeCreate-被setup替代
created-被setup替代
beforeMountonBeforeMountDOM挂载开始前
mountedonMountedDOM挂载完成后
beforeUpdateonBeforeUpdate响应式数据变更导致更新前
updatedonUpdated虚拟DOM重新渲染后
beforeUnmountonBeforeUnmount组件卸载前(vue2的beforeDestroy别名)
unmountedonUnmounted组件卸载后(vue2的destroyed别名)
errorCapturedonErrorCaptured捕获后代组件错误时
renderTrackedonRenderTracked响应式依赖被追踪时(开发模式)
renderTriggeredonRenderTriggered响应式依赖触发更新时(开发模式)

(2)组合式生命周期示例

import { onMounted,onUpdated,onUnmounted 
} from 'vue'export default {setup() {// 同步调用保证正确注册onMounted(async () => {const data = await fetchData()// 异步操作不会阻塞生命周期})// 支持多次注册相同钩子onMounted(() => console.log('第一个mounted回调'))onMounted(() => console.log('第二个mounted回调'))// 清理副作用示例const timer = ref()onMounted(() => {timer.value = setInterval(() => {/* 定时任务 */}, 1000)})onUnmounted(() => {clearInterval(timer.value)})}
}

(3)生命周期使用原则

  1. 同步注册:必须在setup同步调用生命周期钩子
    // 错误示例
    setTimeout(() => {onMounted(() => {}) // 将不会执行
    }, 100)
    
  2. 执行顺序:按照注册顺序同步执行
  3. 异步操作:钩子函数本身可以包含异步代码,但不会延迟生命周期进度
  4. 组件树顺序:父组件onBeforeMount先于子组件onBeforeMount

(4)调试钩子实践

onRenderTracked((event) => {console.log('依赖追踪:', event)
})onRenderTriggered((event) => {console.log('依赖触发更新:', event)
})
  • 开发模式专用:帮助分析组件渲染行为
  • 事件对象:包含target(响应式对象)、key(触发属性)、type(操作类型)等信息
  • 性能优化:识别不必要的渲染触发源

2.4 响应式原理探秘

(1)依赖收集流程

// 伪代码实现
function reactive(obj) {return new Proxy(obj, {get(target, key) {track(target, key)return Reflect.get(target, key)},set(target, key, value) {const result = Reflect.set(target, key, value)trigger(target, key)return result}})
}// 副作用函数注册
let activeEffect
class ReactiveEffect {constructor(fn) {this.fn = fn}run() {activeEffect = thisreturn this.fn()}
}function watchEffect(fn) {const effect = new ReactiveEffect(fn)effect.run()
}

(2)响应式类型对比

refreactiveshallowRef
创建方式ref(value)reactive(object)shallowRef(value)
访问方式.value直接访问属性.value
嵌套响应自动展开递归响应非递归
类型支持基础类型/对象仅对象任意类型
模板自动解包支持不需要支持
适用场景独立基本值、模板引用复杂对象结构大型对象性能优化

(3)响应式转换规则

const raw = {}
const observed = reactive(raw)console.log(observed === raw) // false
console.log(reactive(observed) === observed) // trueconst refVal = ref(0)
console.log(ref(refVal) === refVal) // true
  • 代理唯一性:对同一原始对象多次调用reactive返回相同代理
  • ref保护机制:如果传入ref给ref构造函数,直接返回原ref
  • 原始对象保护:Vue不会代理Vue实例或代理对象

2.5 组合式API中的上下文处理

(1)跨层级访问示例

// 祖先组件
import { provide } from 'vue'setup() {const theme = ref('dark')provide('theme', theme)
}// 后代组件
import { inject } from 'vue'setup() {const theme = inject('theme', 'light') // 默认值return { theme }
}

(2)模板引用转发

// 子组件
import { defineExpose } from 'vue'setup() {const publicMethod = () => { /* ... */ }defineExpose({ publicMethod })
}// 父组件
const childRef = ref()onMounted(() => {childRef.value.publicMethod()
})

三、核心API深度解析

3.1 响应式工具

// 解构响应式对象
import { reactive, toRefs } from 'vue'const state = reactive({x: 0,y: 0
})const { x, y } = toRefs(state)

3.2 计算属性

const doubleCount = computed(() => count.value * 2)

3.3 副作用管理

// watchEffect自动追踪依赖
const stop = watchEffect(() => {console.log(`count变化: ${count.value}`)
})// 精确控制的watch
watch(count, (newVal, oldVal) => {// 执行特定操作
})

四、逻辑复用模式

4.1 自定义组合函数

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

4.2 异步状态管理

// useFetch.js
import { ref } from 'vue'export function useFetch(url) {const data = ref(null)const error = ref(null)const loading = ref(false)async function fetchData() {try {loading.value = trueconst response = await fetch(url)data.value = await response.json()} catch (err) {error.value = err} finally {loading.value = false}}return {data,error,loading,fetchData}
}

五、最佳实践

5.1 代码组织模式

export default {setup() {// 数据逻辑const { x, y } = useMouse()// 业务逻辑const { data, fetch } = useFetch('/api')// 其他逻辑const count = useCounter()return { x, y, data, fetch, count }}
}

5.2 TypeScript集成

interface User {id: numbername: string
}const users = ref<User[]>([])

六、与Options API对比

6.1 逻辑组织对比

// Options API
export default {data() {return { count: 0 }},methods: {increment() { this.count++ }},mounted() {console.log('挂载完成')}
}// Composition API
export default {setup() {const count = ref(0)const increment = () => count.value++onMounted(() => console.log('挂载完成'))return { count, increment }}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/70124.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微调llama3问题解决-RuntimeError: CUDA unknown error - this may be due to an incorrectly set up environment

问题说明之一 具体问题如下&#xff1a; RuntimeError: CUDA unknown error - this may be due to an incorrectly set up environment, e.g. changing env variable CUDA_VISIBLE_DEVICES after program start. Setting the available devices to be zero.我使用的这套是根据…

【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-Chapter1-什么是 JavaScript

一、什么是 JavaScript 虽然 JavaScript 和 ECMAScript&#xff08;发音为“ek-ma-script”&#xff09; 基本上是同义词&#xff0c;但 JavaScript 远远不限于 ECMA-262 所定义的那样。没错&#xff0c;完整的 JavaScript 实现包含以下几个部分。 核心&#xff08;ECMAScript&…

2. 【.NET Aspire 从入门到实战】--理论入门与环境搭建--.NET Aspire 概览

在当今快速发展的软件开发领域&#xff0c;构建高效、可靠且易于维护的云原生应用程序已成为开发者和企业的核心需求。.NET Aspire 作为一款专为云原生应用设计的开发框架&#xff0c;旨在简化分布式系统的构建和管理&#xff0c;提供了一整套工具、模板和集成包&#xff0c;帮…

49【服务器介绍】

服务器和你的电脑可以说是一模一样的&#xff0c;只不过用途不一样&#xff0c;叫法就不一样了 物理服务器和云服务器的区别 整台设备眼睛能够看得到的&#xff0c;我们一般称之为物理服务器。所以物理服务器是比较贵的&#xff0c;不是每一个开发者都能够消费得起的。 …

Redis代金卷(优惠卷)秒杀案例-单应用版

优惠卷表:优惠卷基本信息,优惠金额,使用规则 包含普通优惠卷和特价优惠卷(秒杀卷) 优惠卷的库存表:优惠卷的库存,开始抢购时间,结束抢购时间.只有特价优惠卷(秒杀卷)才需要填写这些信息 优惠卷订单表 卷的表里已经有一条普通优惠卷记录 下面首先新增一条秒杀优惠卷记录 { &quo…

Notepad++消除生成bak文件

设置(T) ⇒ 首选项... ⇒ 备份 ⇒ 勾选 "禁用" 勾选禁用 就不会再生成bak文件了 notepad怎么修改字符集编码格式为gbk 如图所示

DeepSeek蒸馏模型:轻量化AI的演进与突破

目录 引言 一、知识蒸馏的技术逻辑与DeepSeek的实践 1.1 知识蒸馏的核心思想 1.2 DeepSeek的蒸馏架构设计 二、DeepSeek蒸馏模型的性能优势 2.1 效率与成本的革命性提升 2.2 性能保留的突破 2.3 场景适应性的扩展 三、应用场景与落地实践 3.1 智能客服系统的升级 3.2…

物联网领域的MQTT协议,优势和应用场景

MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;作为轻量级发布/订阅协议&#xff0c;凭借其低带宽消耗、低功耗与高扩展性&#xff0c;已成为物联网通信的事实标准。其核心优势包括&#xff1a;基于TCP/IP的异步通信机制、支持QoS&#xff08;服务质量&…

基于“蘑菇书”的强化学习知识点(五):条件期望

条件期望 摘要一、条件期望的定义二、条件期望的关键性质三、条件期望的直观理解四、条件期望的应用场景五、简单例子离散情况连续情况 摘要 本系列知识点讲解基于蘑菇书EasyRL中的内容进行详细的疑难点分析&#xff01;具体内容请阅读蘑菇书EasyRL&#xff01; 对应蘑菇书Eas…

Node.js与嵌入式开发:打破界限的创新结合

文章目录 一、Node.js的本质与核心优势1.1 什么是Node.js&#xff1f;1.2 嵌入式开发的范式转变 二、Node.js与嵌入式结合的四大技术路径2.1 硬件交互层2.2 物联网协议栈2.3 边缘计算架构2.4 轻量化运行时方案 三、实战案例&#xff1a;智能农业监测系统3.1 硬件配置3.2 软件架…

Shell 中的 Globbing:原理、使用方法与实现解析(中英双语)

Shell 中的 Globbing&#xff1a;原理、使用方法与实现解析 在 Unix Shell&#xff08;如 Bash、Zsh&#xff09;中&#xff0c;globbing 是指 文件名模式匹配&#xff08;filename pattern matching&#xff09;&#xff0c;它允许用户使用特殊的通配符&#xff08;wildcards…

7 与mint库对象互转宏(macros.rs)

macros.rs代码定义了一个Rust宏mint_vec&#xff0c;它用于在启用mint特性时&#xff0c;为特定的向量类型实现与mint库中对应类型的相互转换。mint库是一个提供基本数学类型&#xff08;如点、向量、矩阵等&#xff09;的Rust库&#xff0c;旨在与多个图形和数学库兼容。这个宏…

P3078[USACO13MAR] Poker Hands S

P3078[USACO13MAR] Poker Hands S https://www.luogu.com.cn/problem/P3078 前言 学习差分后写的第一道题&#xff0c;直接给我干懵逼&#xff0c;题解都看不懂……吃了个晚饭后开窍写出来了&#xff0c;遂成此篇。 题目 翻译版本 Bessie 和她的朋友们正在玩一种独特的扑克游…

【物联网】ARM核常用指令(详解):数据传送、计算、位运算、比较、跳转、内存访问、CPSR/SPSR

文章目录 指令格式&#xff08;重点&#xff09;1. 立即数2. 寄存器位移 一、数据传送指令1. MOV指令2. MVN指令3. LDR指令 二、数据计算指令1. ADD指令1. SUB指令1. MUL指令 三、位运算指令1. AND指令2. ORR指令3. EOR指令4. BIC指令 四、比较指令五、跳转指令1. B/BL指令2. l…

Redis基础(二)——通用命令与五大基本数据类型

目录 一、Redis数据结构基本介绍 二、Redis通用命令 1.查看通用命令 2.KEYS&#xff1a;查看符合模板的所有key 3.DEL&#xff1a;删除指定的Key 4.lEXISTS&#xff1a;判断key是否存在 5.lEXPIRE&#xff1a;给一个key设置有效期&#xff0c;有效期到期时该key会被自…

Ajax:重塑Web交互体验的人性化探索

在数字化时代&#xff0c;网页的交互性和响应速度已成为衡量用户体验的关键指标。Ajax&#xff08;Asynchronous JavaScript and XML&#xff09;&#xff0c;作为前端与后端沟通的桥梁&#xff0c;凭借其异步通信的能力&#xff0c;极大地提升了网页的动态性和用户友好度&…

ComfyUI工作流 参考图像生成人像手办(SDXL版)

文章目录 参考图像生成人像手办SD模型Node节点工作流程效果展示开发与应用参考图像生成人像手办 此工作流旨在实现将图像生成高精度的3D手办风格效果,通过深度学习技术完成从图像处理、模型加载、提示词优化到图像生成和超分辨率处理的一系列操作。整个流程以SDXL模型为核心,…

c语言 程序计算圆的面积(Program to find area of a circle)

给定圆的半径&#xff0c;求该圆的面积。 可以使用以下公式简单地计算圆的面积。 其中 r 是圆的半径&#xff0c;它可能是浮点数&#xff0c;因为饼图的值为 3.14 方法&#xff1a;使用给定的半径&#xff0c;使用上述公式找到面积&#xff1a;&#xff08;pi * r * r&#…

解析PHP文件路径相关常量

PHP文件路径相关常量包括以下几个常量&#xff1a; __FILE__&#xff1a;表示当前文件的绝对路径&#xff0c;包括文件名。 __DIR__&#xff1a;表示当前文件所在的目录的绝对路径&#xff0c;不包括文件名。 dirname(__FILE__)&#xff1a;等同于__DIR__&#xff0c;表示当前…

Rust错误处理:从灭火器到核按钮的生存指南

开篇&#xff1a;错误处理的生存哲学 在Rust的平行宇宙里&#xff0c;错误分为两种人格&#xff1a; panic! → 核按钮&#x1f4a3;&#xff08;不可恢复&#xff0c;全系统警报&#xff09;Result → 灭火器&#x1f9ef;&#xff08;可控制&#xff0c;局部处理&#xff0…