Vue 3 的“降维打击”:Composition API 是如何让 Mixin 成为历史文物的?

哈喽大家好,我是大布布将军

上期咱们扒完了 Vue 3 的响应式原理,我看后台有不少兄弟留言:"布布将军,原理我是懂了,但这 Composition API (组合式 API)到底好在哪?我用 Vue 2 的 Mixin 不也挺香吗?"

听到 Mixin 这个词,我手里拿着的保温杯差点没拿稳。

说实话,当年写 Vue 2 的时候,我觉得 Mixin 简直是黑科技------一段代码,到处复用,感觉自己就是代码界的"复用大师"。但随着项目越来越大,Mixin 逐渐露出了它的獠牙,变成了传说中的 "Mixin 地狱"

今天,咱们就来一场硬核对比,看看 Vue 3 的 Composition API 是怎么把 Mixin 按在地上摩擦的。


一、Mixin 的"三大罪状":如果你恨一个人,就让他维护 Mixin

假设你接手了一个"祖传"项目,打开一个 .vue 文件,看到了这一幕:

// 复制代码
export default {
  mixins: [PageMixin, AuthMixin, ScrollMixin, LogMixin], // 😱 这四个大佬里到底有什么?
  data() {
    return {
      showModal: false
    }
  },
  mounted() {
    this.refreshPage(); // 👈 谁能告诉我,这个方法是哪个 Mixin 里的?
    console.log(this.userInfo); // 👈 这个变量又是谁定义的?
  }
}

这时候,你的脑瓜子是不是嗡嗡的?

罪状 1:来源不明(隐式依赖)

在上面的代码里,this.refreshPage() 可能是 PageMixin 里的,也可能是 AuthMixin 里的。如果想知道它的逻辑,你得把这 4 个 Mixin 文件全部打开找一遍。 这就好比你去吃自助餐,厨师把所有菜都倒进一个大缸里搅匀了端给你,你根本不知道嘴里这块肉是猪肉还是牛肉。

罪状 2:命名冲突(Name Clashing)

悲剧发生了:

  • PageMixin 里有个 data 叫 isLoading
  • ScrollMixin 里也有个 data 叫 isLoading结果: 它们会悄无声息地合并(覆盖),导致你的逻辑莫名其妙地炸掉。最可怕的是,控制台可能还不会报错,直到上线后用户投诉:"为什么我滑到底部加载更多的时候,整个页面重置了?"

罪状 3:滚雪球式的复杂度

当你为了复用逻辑,在 Mixin A 里又引用了 Mixin B,Mixin B 里又引用了 Mixin C... 恭喜你,你创造了一个逻辑黑洞。任何人都别想轻易修改里面的代码,动一行,挂一片。

✨ 二、Composition API 的"降维打击":清清白白做人

Vue 3 带来的 Composition API(配合 setup<script setup>),用一种函数式的思维彻底解决了上面的问题。

我们来看看,同样的逻辑,用 Composables(组合式函数) 是怎么写的:

复制代码
// 显式引入,清清楚楚
import { usePaginate } from './composables/usePaginate'
import { useAuth } from './composables/useAuth'

// 1. 来源清晰:谁的数据归谁管
const { currentPage, refreshPage } = usePaginate()
const { userInfo, checkPermission } = useAuth()

// 2. 这里的代码逻辑一目了然
function handleUserAction() {
  if (checkPermission('admin')) {
    refreshPage()
  }
}
</script>

优势 1:来源透明,拒绝"黑盒"

看上面的代码,refreshPage 明确来自于 usePaginate。你想看实现细节?按住 Ctrl 点击函数名直接跳转。 这不再是自助餐大乱炖,而是精致的分餐制。

优势 2:彻底告别命名冲突

如果两个 Composable 都返回了 isLoading 怎么办?没关系,ES6 解构赋值教做人:

const 复制代码
const { isLoading: authLoading } = useAuth()

轻轻一个重命名,世界和平了。 你在 Mixin 里能这么干吗?除了改源码,别无他法。

三、实战演练:写一个"鼠标追踪器"

咱们来写个实际的功能:追踪鼠标在页面上的坐标

❌ 选手 A:Mixin 方式(Vue 2)

// 复制代码
export default {
  data() {
    return {
      x: 0,
      y: 0
    }
  },
  mounted() {
    window.addEventListener('mousemove', this.update)
  },
  destroyed() {
    window.removeEventListener('mousemove', this.update)
  },
  methods: {
    update(e) {
      this.x = e.pageX
      this.y = e.pageY
    }
  }
}

痛点: 组件里使用时,突然多出来 this.xthis.y,万一组件自己也有个变量叫 x,直接完蛋。 ✅ 选手 B:Composable 方式(Vue 3)

// 复制代码
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'

// 随心所欲,想叫什么叫什么
const { x: mouseX, y: mouseY } = useMouse()
</script>

<template>
  鼠标位置:{{ mouseX }}, {{ mouseY }}
</template>

总结:为什么说 Composition API 是"降维打击"?

把逻辑复用从 "依赖上下文的魔法(this)" 变成了 "普通的 JavaScript 函数调用"

  1. Mixin 是把逻辑混入组件,像往咖啡里倒牛奶,倒进去就分不开了。
  2. Composition API 是把逻辑作为工具提供给组件,像你在咖啡旁放了一包糖,想放多少、怎么放,完全由你控制。

不仅如此,Composition API 天然对 TypeScript 友好。而在 Mixin 里写 TS 类型推导?那简直是在给自己找罪受。

也是一种思维的转变

很多兄弟刚从 Vue 2 转 Vue 3,觉得写 setup 很麻烦,要把东西 return 出来(虽然后来有了 <script setup> 省略了这一步)。但当你真正习惯了把业务逻辑 抽离成一个个独立的 useXxx 文件后,你会发现你的 .vue 文件变得异常清爽,维护起来简直不要太丝滑。


👋 互动时间: 你在项目里遇到过最坑爹的 Mixin 经历是什么?或者你对 Composition API 还有什么顾虑?欢迎在评论区吐槽,大布布将军在线吃瓜!🍉

下期预告: 既然 Vue 3 这么强,那状态管理工具 Pinia 又是凭什么取代了 Vuex 这个"老大哥"的?咱们下期见!

相关推荐
我也想好好学习2 小时前
使用Promise实现串行执行异步任务,含出错重试功能
javascript
Pluto_CRown2 小时前
H5 开发的各类小知识点
前端·javascript
Pluto_CRown2 小时前
上下文存储【下】
前端·javascript
AAA阿giao2 小时前
JavaScript 中基于原型和原型链的继承方式详解
前端·javascript·面试
用户600071819102 小时前
【翻译】如何在Vue中使用Suspense处理异步渲染?
前端
acaiEncode2 小时前
nvm use xxx 报错: exit status 145: The directory is not empty.
前端·node.js
三秦赵哥2 小时前
Prompt 优化教程
前端
光影少年2 小时前
react怎么实现响应式?
前端·react.js·前端框架
奋斗吧程序媛2 小时前
Vue Router的路由模式
前端·javascript·vue.js