Vue3混入

Vue 3 混入完全指南

本文档从零开始讲解 Vue 3 的混入(Mixins):什么是混入、如何定义和使用、选项如何合并、全局混入,以及混入的局限和 Vue 3 更推荐的组合式函数(Composables)替代方案,配有示例,适合新手系统学习。


一、什么是“混入”?

1.1 为什么需要“混入”?

多个组件里经常有相同的逻辑:相同的数据、相同的方法、相同的生命周期代码。
例如:很多页面都要“打印当前时间”“在挂载时请求用户信息”“共享一个格式化日期的函数”。
如果每个组件都复制粘贴一遍,会很难维护;把公共部分抽出去、再“混”进组件,就是混入在做的事。

1.2 混入是什么?

混入(Mixin) 是一个普通 JavaScript 对象,里面可以写和组件选项一样的东西:datamethodscomputedwatch生命周期钩子等。
组件通过 mixins 选项把这个对象“混进来”,Vue 会把混入里的选项和组件自己的选项按一定规则合并,就像把这些逻辑“复制”进当前组件一样。

1.3 重要说明:Vue 3 更推荐什么?

在 Vue 3 中,组合式 API(Composition API)组合式函数(Composables) 是官方更推荐的方式来实现“逻辑复用”。
混入 仍然支持,但在 不能直接使用 mixins,混入主要用在选项式 API(Options API) 的组件里。
学习混入有助于:

  • 阅读或维护仍在使用选项式 API 和 mixins 的老项目。
  • 理解“逻辑复用”的演进:混入 → 组合式函数。

本文会先讲清混入的用法和合并规则,再说明其局限,并简要介绍如何用组合式函数替代。


二、如何定义和使用混入?

2.1 定义一个混入对象

混入就是一个普通对象,属性名和组件选项一致:datamethodscreatedmounted 等。

示例:一个“时间相关”的混入

// mixins/timeMixin.js
export const timeMixin = {
  data() {
    return {
      currentTime: ''
    }
  },
  methods: {
    updateTime() {
      this.currentTime = new Date().toLocaleTimeString()
    }
  },
  mounted() {
    this.updateTime()
    this._timeTimer = setInterval(this.updateTime, 1000)
  },
  beforeUnmount() {
    if (this._timeTimer) clearInterval(this._timeTimer)
  }
}
  • data 必须写成一个函数,返回对象(和组件里 data 的写法一样)。
  • mountedbeforeUnmount 等是生命周期钩子。
  • 这里用 this._timeTimer 存定时器,在 beforeUnmount 里清除,避免组件销毁后仍执行。

2.2 在组件里使用混入(选项式 API)

选项式 API 的组件里,通过 mixins 选项把混入引进来:

// 选项式 API 组件
import { timeMixin } from './mixins/timeMixin'

export default {
  mixins: [timeMixin],
  data() {
    return {
      name: '我的页面'
    }
  },
  mounted() {
    console.log('组件挂载了,当前时间:', this.currentTime)
  }
}

合并后,这个组件会拥有:

  • datacurrentTime(来自混入)+ name(来自组件)。
  • methodsupdateTime(来自混入)。
  • mounted:先执行混入的 mounted,再执行组件自己的 mounted(详见下文“合并规则”)。

模板里可以直接用混入提供的数据和方法:

<template>
  <div>
    <p>页面:{{ name }}</p>
    <p>当前时间:{{ currentTime }}</p>
  </div>
</template>

2.3 混入在 中不可用

没有 mixins 选项,不能写:

// 这样不行
const mixins = [timeMixin]

若你用的是 ,要实现类似“混入”的逻辑复用,请用组合式函数(见第六节)。


三、选项的合并规则(重要)

组件和混入里同名选项会怎样合并?规则如下。

3.1 data、methods、computed、inject、provide

这些选项会合并成一个对象
组件混入同名的 key以组件自己的为准(组件覆盖混入)。

const mixin = {
  data() {
    return { count: 0, name: 'Mixin' }
  },
  methods: {
    fn() {
      console.log('mixin fn')
    }
  }
}

export default {
  mixins: [mixin],
  data() {
    return { name: 'Component', age: 18 }
  },
  methods: {
    fn() {
      console.log('component fn')
    }
  }
}

合并后:

  • data{ count: 0, name: 'Component', age: 18 }(name 被组件覆盖,count 来自混入,age 来自组件)。
  • methods.fn:组件的 fn(组件覆盖混入)。

3.2 生命周期钩子(created、mounted 等)

同名的生命周期钩子会合并成数组都会执行
执行顺序:先执行混入的钩子,再执行组件自己的钩子

const mixin = {
  mounted() {
    console.log(1)
  }
}

export default {
  mixins: [mixin],
  mounted() {
    console.log(2)
  }
}

控制台输出顺序:1 → 2

若有多个混入,混入的钩子按 mixins 数组顺序执行,然后再执行组件的钩子。

3.3 watch

watch 里同名的监听会合并成数组,都会生效;若监听同一属性,会按合并顺序依次执行。

3.4 小结表

选项类型 合并方式 冲突时(组件 vs 混入)
data、methods 等 合并为对象 组件为准
生命周期钩子 合并为数组,都执行 先混入,后组件
watch、components 等 合并为对象/数组 组件为准(或都执行)

四、多个混入与全局混入

4.1 使用多个混入

mixins 是一个数组,可以写多个混入;合并时按数组顺序依次合并,后面的不会覆盖前面的同名 data/methods(组件最后合并,组件优先)。

import { timeMixin } from './mixins/timeMixin'
import { logMixin } from './mixins/logMixin'

export default {
  mixins: [timeMixin, logMixin],
  data() {
    return {}
  }
}

4.2 全局混入

main.js 里用 app.mixin(混入对象),会对当前应用下所有组件生效,慎用。

// main.js
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)
app.mixin({
  mounted() {
    console.log('每个组件挂载时都会打印')
  }
})
app.mount('#app')
  • 适合做“全局打点、全局埋点”等,但要避免在全局混入里写大量逻辑容易重名的 data/methods,否则难以维护、难以排查问题。
  • 一般更推荐在需要的地方局部混入,或改用组合式函数

五、完整示例与常见场景

5.1 混入:请求用户信息

// mixins/userMixin.js
export const userMixin = {
  data() {
    return {
      user: null,
      userLoading: false
    }
  },
  methods: {
    async fetchUser() {
      this.userLoading = true
      try {
        const res = await fetch('/api/user')
        this.user = await res.json()
      } finally {
        this.userLoading = false
      }
    }
  },
  mounted() {
    this.fetchUser()
  }
}
// 组件
import { userMixin } from './mixins/userMixin'

export default {
  mixins: [userMixin],
  template: `
    <div v-if="userLoading">加载中...</div>
    <div v-else-if="user">你好,{{ user.name }}</div>
  `
}

5.2 混入:公共方法(格式化、校验)

// mixins/utilsMixin.js
export const utilsMixin = {
  methods: {
    formatDate(date) {
      if (!date) return ''
      return new Date(date).toLocaleDateString()
    },
    isPhone(str) {
      return /^1d{10}$/.test(str)
    }
  }
}
export default {
  mixins: [utilsMixin],
  methods: {
    submit() {
      if (!this.isPhone(this.phone)) {
        alert('手机号格式错误')
        return
      }
      console.log('提交时间:', this.formatDate(new Date()))
    }
  }
}

5.3 混入 + 组件都有的 data / 生命周期

const mixin = {
  data() {
    return { mixinCount: 0 }
  },
  created() {
    console.log('mixin created')
  }
}

export default {
  mixins: [mixin],
  data() {
    return { mixinCount: 100, myCount: 1 }
  },
  created() {
    console.log('component created')
  }
}
  • data:最终 mixinCount100(组件覆盖混入),myCount 为 1。
  • created:先打印 mixin created,再打印 component created

六、混入的局限与替代方案:组合式函数

6.1 混入的常见问题

  1. 来源不清晰:模板里用的 currentTimeupdateTime 是组件写的还是混入写的,不打开 mixins 列表很难一眼看出。
  2. 命名冲突:多个混入或混入与组件有同名 data/methods 时,虽然“以组件为准”,但容易误以为用的是混入的值,导致难以排查的 bug。
  3. 无法在 中使用:Vue 3 推荐 ,而混入只适用于选项式 API,在新写法里用不上。

因此,Vue 3 官方更推荐用组合式函数(Composables)来做逻辑复用。

6.2 用组合式函数替代混入(推荐)

把“混入”里想复用的 data + methods + 生命周期,改成一个普通函数,在函数里用 refreactiveonMounted 等组合式 API,然后 return 出需要给模板或其它逻辑用的数据和方法。
组件里只要 import调用这个函数,就能得到同样的能力,且来源清晰、无合并冲突。

原来用混入的“时间”示例,改成组合式函数:

// composables/useTime.js
import { ref, onMounted, onBeforeUnmount } from 'vue'

export function useTime() {
  const currentTime = ref('')
  let timer = null

  function updateTime() {
    currentTime.value = new Date().toLocaleTimeString()
  }

  onMounted(() => {
    updateTime()
    timer = setInterval(updateTime, 1000)
  })

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

  return { currentTime, updateTime }
}

在 里使用:

<template>
  <p>当前时间:{{ currentTime }}</p>
</template>

<script setup>
import { useTime } from './composables/useTime'
const { currentTime } = useTime()
</script>
  • 数据和方法来自 useTime(),一眼能看出来源。
  • 多个组件用 useTime,各自有一份 currentTime,不会互相串。
  • 支持 ,也符合 Vue 3 推荐写法。

“用户信息”示例改成组合式函数:

// composables/useUser.js
import { ref, onMounted } from 'vue'

export function useUser() {
  const user = ref(null)
  const userLoading = ref(false)

  async function fetchUser() {
    userLoading.value = true
    try {
      const res = await fetch('/api/user')
      user.value = await res.json()
    } finally {
      userLoading.value = false
    }
  }

  onMounted(() => {
    fetchUser()
  })

  return { user, userLoading, fetchUser }
}
<script setup>
import { useUser } from './composables/useUser'
const { user, userLoading } = useUser()
</script>

七、何时还用混入?

  • 你正在维护选项式 API 的老项目,且已经大量使用 mixins,可以继续按原有方式使用混入,并了解合并规则便于排错。
  • 新写 Vue 3 项目或新功能,更推荐用 + 组合式函数,而不是混入。

八、混入速查表

内容 说明
定义混入 普通对象,包含 data、methods、mounted 等选项;data 必须是函数
组件中使用 选项式 API:mixins: [myMixin]
不能使用 mixins,改用组合式函数
data/methods 冲突 以组件为准
生命周期冲突 都执行,先混入后组件
全局混入 app.mixin(对象),慎用
推荐替代 组合式函数(composables)

九、学习建议

  1. 若你写的是 ,直接学组合式函数即可,不必在混入上花太多时间。
  2. 若需要读/改选项式 API + mixins 的代码,重点理解:data/methods 以组件为准、生命周期会合并成数组且先混入后组件
  3. 新逻辑复用优先用 useXxx() 这种组合式函数,更清晰、更易维护。

把本文档里的混入示例和对应的组合式函数示例对比着看,能更好理解“为什么 Vue 3 推荐用组合式函数替代混入”。祝你学习顺利。

发表评论