深入探索Vue 3组合式API

深入探索Vue 3组合式API

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

深入探索Vue 3组合式API

一、组合式API诞生背景

1.1 Options API的局限性

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

1.2 设计目标

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

二、核心概念解析

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

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

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

(2)props处理规范

typescript 复制代码
import { defineComponent } from 'vue'

interface Props {
  title: string
  count?: 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)上下文对象解析

javascript 复制代码
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)响应式状态管理

javascript 复制代码
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)响应式核心原理

javascript 复制代码
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的进阶用法

javascript 复制代码
// 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的边界情况

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

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

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

Options API Composition API 触发时机
beforeCreate - 被setup替代
created - 被setup替代
beforeMount onBeforeMount DOM挂载开始前
mounted onMounted DOM挂载完成后
beforeUpdate onBeforeUpdate 响应式数据变更导致更新前
updated onUpdated 虚拟DOM重新渲染后
beforeUnmount onBeforeUnmount 组件卸载前(vue2的beforeDestroy别名)
unmounted onUnmounted 组件卸载后(vue2的destroyed别名)
errorCaptured onErrorCaptured 捕获后代组件错误时
renderTracked onRenderTracked 响应式依赖被追踪时(开发模式)
renderTriggered onRenderTriggered 响应式依赖触发更新时(开发模式)

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

javascript 复制代码
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同步调用生命周期钩子

    javascript 复制代码
    // 错误示例
    setTimeout(() => {
      onMounted(() => {}) // 将不会执行
    }, 100)
  2. 执行顺序:按照注册顺序同步执行

  3. 异步操作:钩子函数本身可以包含异步代码,但不会延迟生命周期进度

  4. 组件树顺序:父组件onBeforeMount先于子组件onBeforeMount

(4)调试钩子实践

javascript 复制代码
onRenderTracked((event) => {
  console.log('依赖追踪:', event)
})

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

2.4 响应式原理探秘

(1)依赖收集流程

javascript 复制代码
// 伪代码实现
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 = this
    return this.fn()
  }
}

function watchEffect(fn) {
  const effect = new ReactiveEffect(fn)
  effect.run()
}

(2)响应式类型对比

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

(3)响应式转换规则

javascript 复制代码
const raw = {}
const observed = reactive(raw)

console.log(observed === raw) // false
console.log(reactive(observed) === observed) // true

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

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

(1)跨层级访问示例

javascript 复制代码
// 祖先组件
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)模板引用转发

javascript 复制代码
// 子组件
import { defineExpose } from 'vue'

setup() {
  const publicMethod = () => { /* ... */ }
  defineExpose({ publicMethod })
}

// 父组件
const childRef = ref()

onMounted(() => {
  childRef.value.publicMethod()
})

三、核心API深度解析

3.1 响应式工具

javascript 复制代码
// 解构响应式对象
import { reactive, toRefs } from 'vue'

const state = reactive({
  x: 0,
  y: 0
})

const { x, y } = toRefs(state)

3.2 计算属性

javascript 复制代码
const doubleCount = computed(() => count.value * 2)

3.3 副作用管理

javascript 复制代码
// watchEffect自动追踪依赖
const stop = watchEffect(() => {
  console.log(`count变化: ${count.value}`)
})

// 精确控制的watch
watch(count, (newVal, oldVal) => {
  // 执行特定操作
})

四、逻辑复用模式

4.1 自定义组合函数

javascript 复制代码
// 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.pageX
    y.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 异步状态管理

javascript 复制代码
// 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 = true
      const 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 代码组织模式

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

    return { x, y, data, fetch, count }
  }
}

5.2 TypeScript集成

typescript 复制代码
interface User {
  id: number
  name: string
}

const users = ref<User[]>([])

六、与Options API对比

6.1 逻辑组织对比

javascript 复制代码
// 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 }
  }
}
相关推荐
geovindu1 小时前
D3.js Org Chart
开发语言·javascript·ecmascript
疋瓞1 小时前
excel实用问题:提取文字当中的数字进行运算
java·javascript·excel
CodeClimb2 小时前
【华为OD-E卷 - 任务最优调度 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
Chaoran2 小时前
vue3 封装右键菜单组件
前端·javascript
海岸边的破木船2 小时前
为什么Vue3能更好的支持TS
前端
苹果酱05672 小时前
人工智能基础知识速成 - 机器学习、深度学习算法原理及其实际应用案例
java·vue.js·spring boot·mysql·课程设计
小狸花子2 小时前
全平台异构文件上传架构设计:打破端侧限制的OSS上传实践
前端
慕斯-ing2 小时前
利用Vue编写一个“计数器”
前端·vue.js·经验分享
HackKong2 小时前
DeepSeek 遭 DDoS 攻击背后:DDoS 攻击的 “千层套路” 与安全防御 “金钟罩”_deepseek ddos
网络·安全·网络安全·ddos·黑客技术
杨枝甘露小码3 小时前
利用Muduo库实现简单且健壮的Echo服务器
服务器·网络