Vue3 Hooks 对比 Vue2 Mixins 两者之间的区别?

在Vue生态中,"Hooks"这个概念随着Vue3的发布而变得火热。但很多开发者可能会有疑问:Vue2中不是也有"混入"(mixins)吗?它们有什么区别?为什么Vue3要引入Hooks?

本文将深入探讨Vue3中Hooks的实现方式,并与Vue2的Mixins进行对比,通过一个实际的例子,比较出两者使用上的区别。

一、 什么是Hooks?

Hooks 直白点说就是一个带状态的函数。其自身内部存在一套完整的响应式数据

打个比方:

  • 普通函数就像一次性筷子,用完就扔

  • Hooks 就像能反复使用的餐具,每次用都能记住上次的状态

 

假设我们要做个登录页面,有个"发送验证码"的按钮,点完之后要倒计时60秒,这60秒内按钮是灰色的点不了,倒计时结束按钮才能再点。

这个功能在很多地方都会用到,比如注册、找回密码等等,所以我们肯定想把它抽出来复用。

三、Vue2的实现方式(Mixins)

// countdownMixin.js
export default {
  data() {
    return {
      countdownSeconds: 60,  // 倒计时秒数
      isCounting: false,     // 是否正在倒计时
      timerId: null,        
      buttonText: '发送验证码' 
    }
  },
  
  methods: {
    startCountdown() {
      this.isCounting = true
      this.buttonText = `${this.countdownSeconds}秒后重发`
      
      this.timerId = setInterval(() => {
        this.countdownSeconds--
        if (this.countdownSeconds <= 0) {
          // 倒计时结束
          clearInterval(this.timerId)
          this.isCounting = false
          this.buttonText = '重新发送'
          this.countdownSeconds = 60
        } else {
          this.buttonText = `${this.countdownSeconds}秒后重发`
        }
      }, 1000)
    }
  },
  
  // 组件销毁 清理定时器
  beforeDestroy() {
    if (this.timerId) {
      clearInterval(this.timerId)
    }
  }
}

  

在组件里用这个 Mixin

<template>
  <div>
    <input type="text" v-model="phone" placeholder="手机号" />
    <button 
      @click="sendCode" 
      :disabled="isCounting"
    >
      {{ buttonText }}
    </button>
  </div>
</template>

<script>
import countdownMixin from './countdownMixin'

export default {
  mixins: [countdownMixin],  // 引入mixin
  data() {
    return {
      phone: '',
    }
  },
  methods: {
    sendCode() {
      console.log('发送验证码到', this.phone)
      this.startCountdown()
    }
  }
}
</script>

  

可能存在的问题?

问题1:不太清除数据从哪来的?
新来的同事看到代码里的 isCountingbuttonText,不太理解变量定义在哪里?得去翻 mixin 文件才能知道。

问题2:命名冲突
假如我组件里本来就有个 isCounting 变量,想记录别的状态,但是这样会跟 mixin 里定义的冲突。谁后加载谁覆盖。

问题3:方法函数覆盖
假如在组件里也写了个重名的 startCountdown 方法,mixin 里的就被覆盖了,倒计时功能就失效了。

问题4:难定位问题
倒计时出问题了,你得去 mixin 文件里找原因,但 mixin 可能被其它地方引入。改了问题之后,不太清楚其它引用地方会不会存在问题

 

四、Vue2的实现方式(Hooks)

// useCountdown.js
import { ref, computed, onUnmounted } from 'vue'

export function useCountdown(initialSeconds = 60) {
  // 所有的状态都写在里面
  const seconds = ref(initialSeconds)  // 当前剩余秒数
  const isActive = ref(false)          // 是否正在倒计时
  let timer = null                      // 定时器

  // 按钮上显示的文字(计算属性)
  const buttonText = computed(() => {
    if (isActive.value) {
      return `${seconds.value}秒后重发`
    }
    return '发送验证码'
  })

  // 开始倒计时
  const start = () => {
    if (isActive.value) return
    
    isActive.value = true
    timer = setInterval(() => {
      if (seconds.value > 0) {
        seconds.value--
      } else {
        // 倒计时结束
        clearInterval(timer)
        isActive.value = false
        seconds.value = initialSeconds
      }
    }, 1000)
  }

  onUnmounted(() => {
    if (timer) {
      clearInterval(timer)
    }
  })

  return {
    seconds,
    isActive,
    buttonText,
    start
  }
}

  

在组件里用Hook

<template>
  <div>
    <input type="text" v-model="phone" placeholder="手机号" />
    <button 
      @click="sendCode" 
      :disabled="isActive"
    >
      {{ buttonText }}
    </button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { useCountdown } from './useCountdown'

// 组件自己的数据
const phone = ref('')

// 使用倒计时功能
const { isActive, buttonText, start } = useCountdown(60)

// 可以自己定义一个新的isCounting
const isCounting = ref(false)

const sendCode = () => {
  console.log('发送验证码到', phone.value)
  start()  // 开始倒计时
}

const code1 = useCountdown(60)   // 手机验证码倒计时
const code2 = useCountdown(120)  // 邮箱验证码倒计时
</script>

  

这样写有啥好处?

1:代码出处一目了然
看到 isActive 就知道是从 useCountdown 来的。

好处2:命名不怕冲突覆盖,还可以对引入的变量重命名

javascript
const { isActive: codeActive, buttonText: codeBtnText } = useCountdown()

好处3:按需取用
只解构需要使用的东西。

好处4:多实例独立

posted @ 2026-02-27 15:32  收破烂的小伙子  阅读(3)  评论(0)    收藏  举报