深入探索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.rhkb.cn/news/12308.html

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

相关文章

【玩转 Postman 接口测试与开发2_016】第13章:在 Postman 中实现契约测试(Contract Testing)与 API 接口验证(上)

《API Testing and Development with Postman》最新第二版封面 文章目录 第十三章 契约测试与 API 接口验证1 契约测试的概念2 契约测试的工作原理3 契约测试的分类4 DeepSeek 给出的契约测试相关背景5 契约测试在 Postman 中的创建方法6 API 实例的基本用法7 API 实例的类型实…

java-(Oracle)-Oracle,plsqldev,Sql语法,Oracle函数

卸载好注册表,然后安装11g 每次在执行orderby的时候相当于是做了全排序,思考全排序的效率 会比较耗费系统的资源,因此选择在业务不太繁忙的时候进行 --给表添加注释 comment on table emp is 雇员表 --给列添加注释; comment on column emp.empno is 雇员工号;select empno,en…

尚硅谷课程【笔记】——大数据之Shell【一】

课程视频&#xff1a;【【尚硅谷】Shell脚本从入门到实战】 一、Shell概述 为什么要学习Shell&#xff1f; 1&#xff09;需要看懂运维人员的Shell程序 2&#xff09;偶尔编写一些简单的Shell程序来管理集群、提高开发效率 什么是Shell&#xff1f; 1&#xff09;Shell是一…

pytorch实现长短期记忆网络 (LSTM)

人工智能例子汇总&#xff1a;AI常见的算法和例子-CSDN博客 LSTM 通过 记忆单元&#xff08;cell&#xff09; 和 三个门控机制&#xff08;遗忘门、输入门、输出门&#xff09;来控制信息流&#xff1a; 记忆单元&#xff08;Cell State&#xff09; 负责存储长期信息&…

CDDIS从2025年2月开始数据迁移

CDDIS 将从 2025 年 2 月开始将我们的网站从 cddis.nasa.gov 迁移到 earthdata.nasa.gov&#xff0c;并于 2025 年 6 月结束。 期间可能对GAMIT联网数据下载造成影响。

【Redis】主从模式,哨兵,集群

主从复制 单点问题&#xff1a; 在分布式系统中&#xff0c;如果某个服务器程序&#xff0c;只有一个节点&#xff08;也就是一个物理服务器&#xff09;来部署这个服务器程序的话&#xff0c;那么可能会出现以下问题&#xff1a; 1.可用性问题&#xff1a;如果这个机器挂了…

华为云kubernetes部署deepseek r1、ollama和open-webui(已踩过坑)

1 概述 ollama是一个管理大模型的一个中间层&#xff0c;通过它你可以下载并管理deepseek R1、llama3等大模型。 open-webui是一个web界面&#xff08;界面设计受到chatgpt启发&#xff09;&#xff0c;可以集成ollama API、 OpenAI的 API。 用常见的web应用架构来类比&#x…

在Mac mini M4上部署DeepSeek R1本地大模型

在Mac mini M4上部署DeepSeek R1本地大模型 安装ollama 本地部署&#xff0c;我们可以通过Ollama来进行安装 Ollama 官方版&#xff1a;【点击前往】 Web UI 控制端【点击安装】 如何在MacOS上更换Ollama的模型位置 默认安装时&#xff0c;OLLAMA_MODELS 位置在"~/.o…

CSS 背景与边框:从基础到高级应用

CSS 背景与边框&#xff1a;从基础到高级应用 1. CSS 背景样式1.1 背景颜色示例代码&#xff1a;设置背景颜色 1.2 背景图像示例代码&#xff1a;设置背景图像 1.3 控制背景平铺行为示例代码&#xff1a;控制背景平铺 1.4 调整背景图像大小示例代码&#xff1a;调整背景图像大小…

数据思维错题知识点整理(复习)

小的知识点整理 目前常见的数据采集方案有什么。 埋点、可视化埋点、无埋点&#xff08;无埋点并不是字面意思不埋点&#xff0c;其实也是一种埋点&#xff0c;只是让开发人员完全无感知&#xff0c;直接嵌入sdk&#xff0c;然后每个元素都能查看他们的情况&#xff0c;后续开…

PyQt4学习笔记2】QMainWindow

目录 一、创建 QMainWindow 组件 1. 创建工具栏 2. 创建停靠窗口 3. 设置状态栏 4. 设置中央窗口部件 二、QMainWindow 的主要方法 1. addToolBar() 2. addDockWidget() 3. setStatusBar() 4. setCentralWidget() 5. menuBar() 6. saveState() 和 restoreState() 三、QMainWind…

Linux:文件系统(软硬链接)

目录 inode ext2文件系统 Block Group 超级块&#xff08;Super Block&#xff09; GDT&#xff08;Group Descriptor Table&#xff09; 块位图&#xff08;Block Bitmap&#xff09; inode位图&#xff08;Inode Bitmap&#xff09; i节点表&#xff08;inode Tabl…

ubuntu22.40安装及配置静态ip解决重启后配置失效

遇到这种错误&#xff0c;断网安装即可&#xff01; 在Ubuntu中配置静态IP地址的步骤如下。根据你使用的Ubuntu版本&#xff08;如 Netplan 或传统的 ifupdown&#xff09;&#xff0c;配置方法有所不同。以下是基于 Netplan 的配置方法&#xff08;适用于Ubuntu 17.10及更高版…

手写MVVM框架-实现简单的数据代理

MVVM框架最显著的特点就是虚拟dom和响应式的数据、我们以Vue为例&#xff0c;分别实现data、computed、created、methods以及虚拟dom。 这一章我们先实现简单的响应式&#xff0c;修改数据之后在控制台打印。 我们将该框架命名为MiniVue。 首先我们需要创建MiniVue的类(src/co…

ESLint

ESLint ESLint 是一个针对 JS 的代码风格检查工具&#xff0c;当不满足其要求的风格时&#xff0c;会给予警告或错误。 官网&#xff1a;https://eslint.org/ 中文网&#xff1a;https://eslint.nodejs.cn/ 安装使用 在你的项目中安装 ESLint 包&#xff1a; npm install -…

kaggle视频行为分析1st and Future - Player Contact Detection

这次比赛的目标是检测美式橄榄球NFL比赛中球员经历的外部接触。您将使用视频和球员追踪数据来识别发生接触的时刻&#xff0c;以帮助提高球员的安全。两种接触&#xff0c;一种是人与人的&#xff0c;另一种是人与地面&#xff0c;不包括脚底和地面的&#xff0c;跟我之前做的这…

Chapter 6 -Fine-tuning for classification

Chapter 6 -Fine-tuning for classification 本章内容涵盖 引入不同的LLM微调方法准备用于文本分类的数据集修改预训练的 LLM 进行微调微调 LLM 以识别垃圾邮件评估微调LLM分类器的准确性使用微调的 LLM 对新数据进行分类 现在&#xff0c;我们将通过在大语言模型上对特定目标任…

【从零开始的LeetCode-算法】922. 按奇偶排序数组 II

给定一个非负整数数组 nums&#xff0c; nums 中一半整数是 奇数 &#xff0c;一半整数是 偶数 。 对数组进行排序&#xff0c;以便当 nums[i] 为奇数时&#xff0c;i 也是 奇数 &#xff1b;当 nums[i] 为偶数时&#xff0c; i 也是 偶数 。 你可以返回 任何满足上述条件的…

python 小游戏:扫雷

目录 1. 前言 2. 准备工作 3. 生成雷区 4. 鼠标点击扫雷 5. 胜利 or 失败 6. 游戏效果展示 7. 完整代码 1. 前言 本文使用 Pygame 实现的简化版扫雷游戏。 如上图所示&#xff0c;游戏包括基本的扫雷功能&#xff1a;生成雷区、左键点击扫雷、右键标记地雷、显示数字提示…

安全策略实验报告

1.实验拓扑图 2.实验需求 vlan2属于办公区&#xff0c;vlan3生产区 办公区pc在工作日时间可以正常访问OAserver&#xff0c;i其他时间不允许 办公区pc可以在任意时间访问Web server 生产区pc可以在任意时间访问OA server但不能访问web server 特例&#xff1a;生产区pc可以…