Vue 3 组合式 API 详解:告别 Mixins,拥抱函数式编程

🆚 主要区别

特性 Vue 2(选项式 API) Vue 3(组合式 API)
逻辑组织方式 按选项分类(如data、methods、computed 等) 按功能逻辑分组(通过 setup()<script setup>
代码复用 依赖 Mixins 或高阶组件(容易导致命名冲突和逻辑分散) 使用 Composable 函数(更清晰、可复用性强)
响应式系统 基于 Object.defineProperty 实现 基于 Proxy 实现,性能更好、功能更强
TypeScript 支持 支持有限,类型推导不够完善 原生支持 TypeScript,类型推导更友好
编程范式 命令式为主 声明式 + 函数式

一、Vue 2 的痛点:Mixins 的"甜蜜陷阱"

在 Vue 2 中,当我们需要复用一段逻辑时,最常见的做法是使用 Mixins(混入) 。它的使用非常简单:只需要把公共逻辑抽离成一个对象,然后通过 mixins 属性注入到组件中即可。

javascript 复制代码
const myMixin = {
  data() {
    return { message: 'Hello' }
  },
  methods: {
    greet() {
      console.log(this.message)
    }
  }
}

export default {
  mixins: [myMixin],
  mounted() {
    this.greet() // 输出 "Hello"
  }
}

看似方便,但 Mixins 却埋下了不少隐患:

  1. 命名冲突:如果两个 Mixins 定义了同名属性或方法,后一个会默默覆盖前一个,导致难以排查的 bug。
  2. 逻辑碎片化:随着组件变复杂,逻辑会被分散到多个 Mixins 中,最终让人摸不清头绪。
  3. 调试困难:组件中的数据来源模糊不清,增加了维护成本。

这些问题让 Mixins 成为了"甜蜜的负担"。


二、Vue 3 的革新:Composable 函数登场

Vue 3 彻底改变了逻辑复用的方式,推出了 Composable 函数 。这是一种基于函数式编程思想的设计,核心理念是"把逻辑封装成独立的函数,按需调用"。

来看一个经典的例子------计数器:

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

function useCounter(initialValue = 0) {
  const count = ref(initialValue)
  const increment = () => count.value++
  const decrement = () => count.value--

  return { count, increment, decrement }
}

export default {
  setup() {
    const { count, increment, decrement } = useCounter(10)
    return { count, increment, decrement }
  }
}

这段代码的优点显而易见:

  • 所有关于计数器的逻辑都集中在 useCounter 函数中,结构清晰;
  • 调用时只需解构返回值,无需担心命名冲突;
  • 可以轻松复用到其他组件中,真正做到"一次编写,多处使用"。

Composable 函数的本质其实就是纯函数:输入确定则输出确定,没有副作用,逻辑高度解耦。


三、响应式系统的蜕变:从 defineProperty 到 Proxy

除了逻辑复用,Vue 3 还对底层的响应式系统进行了彻底重构。

Vue 2 的响应式原理

Vue 2 使用 Object.defineProperty 来劫持对象属性的变化:

javascript 复制代码
const obj = {}
Object.defineProperty(obj, 'name', {
  get() {
    console.log('读取 name')
    return this._name
  },
  set(value) {
    console.log('设置 name:', value)
    this._name = value
  }
})

这种方式虽然实现了基本的响应式功能,但也存在明显缺陷:

  1. 无法监听新增属性 :必须通过 $set 手动触发更新;
  2. 数组索引赋值失效 :如 arr[0] = newValue 不会被监听;
  3. 性能开销大:需要递归遍历所有属性。

Vue 3 的响应式原理

Vue 3 换用了 ES6 的 Proxy,它可以拦截整个对象的所有操作:

javascript 复制代码
const target = { name: 'Vue' }
const proxy = new Proxy(target, {
  get(target, key) {
    console.log(`读取 ${key}`)
    return target[key]
  },
  set(target, key, value) {
    console.log(`设置 ${key}: ${value}`)
    target[key] = value
    return true
  }
})

Proxy 的优势在于:

  • 自动支持新增属性和删除属性;
  • 数组操作也能完美监听;
  • 性能更好,采用惰性初始化策略。

这种改进不仅提升了开发体验,也让 Vue 3 的响应式系统更加健壮。


四、背后的思想:函数式编程的魅力

Vue 3 的设计深受 函数式编程 的影响。那什么是函数式编程呢?简单来说,它是一种以函数为核心的编程范式,强调以下几个核心理念:

1. 纯函数(Pure Function)

函数的行为只依赖输入参数,不修改外部状态。例如:

javascript 复制代码
function add(a, b) {
  return a + b
}
add(1, 2) // 始终返回 3

2. 不可变性(Immutability)

数据一旦创建就不能被修改,只能通过创建副本来反映变化:

javascript 复制代码
const arr = [1, 2, 3]
const newArr = [...arr, 4] // 创建新数组而不是修改原数组

3. 声明式编程(Declarative Programming)

关注"做什么"而不是"怎么做"。比如:

javascript 复制代码
// 命令式写法
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i])
}

// 声明式写法
arr.forEach(item => console.log(item))

Vue 3 如何体现函数式思想?

  • Composable 函数是典型的纯函数,它们接受参数、返回结果,不对外部造成影响;
  • Proxy 响应式系统通过代理层隔离副作用,让原始数据始终保持纯净;
  • 模板语法鼓励开发者使用声明式的方式描述 UI,而不是手动操作 DOM。

在 Vue 2 的项目中,一个用户管理页面的 datamethodscomputed 分散在各处,维护困难。使用 Composition API 重构时,可以将代码按照逻辑功能分组,代码更清晰、可复用性更强。例如:我将所有与'用户表单验证'相关的逻辑(响应式数据、验证函数、规则)抽取为 useUserForm Hook。这使得相同逻辑在不同组件间复用变得清晰 ,代码更像是编写纯函数。在大型项目中,我们通常会按 '功能域'(如useTableuseAuth 来组织组合式函数,并与业务组件分离,形成清晰的基础设施层。


如果你喜欢这篇文章,欢迎点赞、收藏或分享给更多朋友。也欢迎在评论区留下你的看法和经验,我们一起交流成长!

相关推荐
一位搞嵌入式的 genius1 小时前
从 URL 到渲染:JavaScript 性能优化全链路指南
开发语言·前端·javascript·性能优化
别叫我->学废了->lol在线等1 小时前
taiwindcss的一些用法
前端·javascript
感谢地心引力1 小时前
在Chrome浏览器中使用Gemini,附一键开启方法
前端·chrome·ai·gemini
晚霞的不甘2 小时前
Flutter for OpenHarmony 豪华抽奖应用:从粒子背景到彩带动画的全栈实现
前端·学习·flutter·microsoft·前端框架
云和数据.ChenGuang2 小时前
python 面向对象基础入门
开发语言·前端·python·django·flask
qq_12498707532 小时前
基于Javaweb的《战舰世界》游戏百科信息系统(源码+论文+部署+安装)
java·vue.js·人工智能·spring boot·游戏·毕业设计·计算机毕业设计
铅笔侠_小龙虾2 小时前
浅谈 Vue & React & Flutter 框架
vue.js·flutter·react.js
We་ct2 小时前
LeetCode 202. 快乐数:题解+思路拆解
前端·算法·leetcode·typescript
HWL56792 小时前
控制浏览器如何预先加载视频资源
java·服务器·前端