技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTVUE

Vue3 Composition API如何替换Vue Mixins

所属馆藏
Vue
文件格式
Markdown
原始路径
Vue/25-Vue3 Composition API如何替换Vue Mixins
本文目录9 个章节
  1. 回顾 Mixins
  2. Mixins 的缺点
  3. 快速入门 Composition API
  4. 提取并复用 composable
  5. 命名冲突变成普通 JavaScript 问题
  6. 依赖更显式,但并非绝对没有隐式依赖
  7. 生命周期与副作用清理
  8. 总结
  9. 官方参考

Vue3 Composition API如何替换Vue Mixins

mixin 模式表面上看起来很安全。然而,通过合并对象来共享代码会增加脆弱性,并掩盖功能的来源。

Composition API 允许 Vue 借助普通 JavaScript 的函数参数、返回值和模块系统共享代码。Vue 3 仍然支持 mixins 以便迁移,但新代码更推荐 composables(组合式函数)。

回顾 Mixins

一个普通组件可能这样定义:

// MyComponent.js
export default {
  data() {
    return { myLocalDataProperty: null }
  },
  methods: {
    myLocalMethod() {}
  }
}

把公共属性提取到 mixin:

// MyMixin.js
export default {
  data() {
    return { mySharedDataProperty: null }
  },
  methods: {
    mySharedMethod() {}
  }
}

组件通过 mixins 使用它:

// ConsumingComponent.js
import MyMixin from './MyMixin.js'

export default {
  mixins: [MyMixin],
  data() {
    return { myLocalDataProperty: null }
  },
  methods: {
    myLocalMethod() {}
  }
}

运行时得到的效果近似于:

export default {
  data() {
    return {
      mySharedDataProperty: null,
      myLocalDataProperty: null
    }
  },
  methods: {
    mySharedMethod() {},
    myLocalMethod() {}
  }
}

Mixins 的缺点

命名冲突与合并规则

const mixin = {
  data() {
    return { myProp: 'mixin' }
  }
}

export default {
  mixins: [mixin],
  data() {
    return { myProp: 'component' }
  }
}

对象选项发生同名冲突时通常以组件选项优先;同名生命周期钩子不会覆盖,而是合并成数组,mixin 钩子先执行。自定义选项可以配置 merge strategy。冲突有时不会直接报错,却会静默覆盖,让多个 mixin 和第三方包的来源难以追踪。

隐式依赖

组件可以直接使用 mixin 数据,mixin 也可能假定组件上存在某个属性,例如校验 mixin 假设组件定义了输入值。重命名组件字段时,编辑器和 linter 不一定知道它破坏了 mixin,往往到运行时才暴露。

快速入门 Composition API

Vue 2 计数器:

export default {
  data() {
    return { count: 0 }
  },
  computed: {
    double() {
      return this.count * 2
    }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}

Vue 3 Composition API:

import { computed, ref } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const double = computed(() => count.value * 2)

    function increment() {
      count.value++
    }

    return { count, double, increment }
  }
}

ref 本身是带 .value 的对象包装器,但它可以包装 primitive;包装对象时,对象会被深层转换。模板会自动解包顶层 ref,JavaScript 中则必须使用 count.value

提取并复用 composable

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

export function useCounter() {
  const count = ref(0)
  const double = computed(() => count.value * 2)

  function increment() {
    count.value++
  }

  return { count, double, increment }
}
// MyComponent.js
import { useCounter } from './useCounter.js'

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

状态定义在函数调用内部,因此每个组件实例、每次调用都有独立状态。若有意共享全局状态,应明确使用模块级状态或 Pinia,而不是把 composable 误当成自动的全局 store。

命名冲突变成普通 JavaScript 问题

Composition API 不会自动消灭冲突,但返回值可显式改名:

const { count: localCount } = useCounter()
const { count: remoteCount } = useRemoteCounter()

这比 mixin 的实例选项合并更容易追踪,冲突也会按普通 JavaScript 绑定规则暴露。

依赖更显式,但并非绝对没有隐式依赖

import { ref } from 'vue'
import { useValidation } from './useValidation.js'

export default {
  setup() {
    const inputValue = ref('')
    const { errors, validate } = useValidation(inputValue)
    return { inputValue, errors, validate }
  }
}

输入通过参数传入、能力通过返回值导出,契约更明确。不过 composable 仍可能依赖导入的模块、provide/inject 或全局服务,所以不能宣称隐式依赖完全不存在。

生命周期与副作用清理

从 mixin 迁移时,不要遗漏监听器清理:

import { onMounted, onUnmounted, ref } from 'vue'

export function useMouse() {
  const x = ref(0)
  const y = ref(0)

  function update(event) {
    x.value = event.pageX
    y.value = event.pageY
  }

  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))

  return { x, y }
}

composable 应在 setup()<script setup> 中同步调用,使生命周期与当前组件实例正确关联。

总结

mixin 的主要问题是命名冲突、数据来源不清晰、隐式依赖和不直观的合并规则。Composition API 通过函数、模块、参数和返回值把这些关系显式化,但它不是万能方案;Options API 仍然可用,Vue 3 也仍支持 mixins。对于新逻辑复用,更推荐 composables。

官方参考

作者:独立开发者张张

原文链接:https://juejin.cn/post/6844904136065056781

来源:稀土掘金。著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS