Mixins

Mixins是一种分发Vue组件中可复用功能的非常灵活的一种方式。

什么时候使用Mixins

多个组件有相同的逻辑,抽取出来

弊端:

image.png
这些问题,可以在Vue3的composition Api得到改善

基础实例

我们有一对不同的组件,它们的作用是切换一个状态布尔值,一个模态框和一个提示框。这些提示框和模态框除了在功能上,没有其他共同点:它们看起来不一样,用法不一样,但是逻辑一样。

  1. // 模态框
  2. const Modal = {
  3. template: '#modal',
  4. data() {
  5. return {
  6. isShowing: false
  7. }
  8. },
  9. methods: {
  10. toggleShow() {
  11. this.isShowing = !this.isShowing;
  12. }
  13. },
  14. components: {
  15. appChild: Child
  16. }
  17. }
  1. // 提示框
  2. const Tooltip = {
  3. template: '#tooltip',
  4. data() {
  5. return {
  6. isShowing: false
  7. }
  8. },
  9. methods: {
  10. toggleShow() {
  11. this.isShowing = !this.isShowing;
  12. }
  13. },
  14. components: {
  15. appChild: Child
  16. }
  17. }

解决办法如下:

  1. const toggle = {
  2. data () {
  3. isshowing: false
  4. },
  5. methods: {
  6. toggleShow() {
  7. this.isshowing = !this.isshowing
  8. }
  9. }
  10. }
  11. // 下面即可使用了
  12. // mixins: [变量名]
  13. const Modal = {
  14. template: '#modal',
  15. mixins: [toggle],
  16. components: {
  17. appChild: Child
  18. }
  19. };
  20. const Tooltip = {
  21. template: '#tooltip',
  22. mixins: [toggle],
  23. components: {
  24. appChild: Child
  25. }
  26. };

如果你是以vue-cli创建的项目来写,可以这样

  1. // mixin.js
  2. export const toggle = {
  3. data () {
  4. isshowing: false
  5. },
  6. methods: {
  7. toggleShow() {
  8. this.isshowing = !this.isshowing
  9. }
  10. }
  11. }
  1. // modal.vue
  2. // 将mixin引入该组件,就可以直接使用 toggleShow() 了
  3. import {mixin} from '../mixin.js'
  4. export default {
  5. mixins: [mixin],
  6. mounted () {
  7. }
  8. }
  9. // tooltip组件同上

合并

当组件和混入对象含有同名选项时,这些选项将以恰当的方式混合。

一、数据对象内

mixin的数据对象和组件的数据发生冲突时以组件数据优先。

  1. var mixin = {
  2. data: function () {
  3. return {
  4. message: 'hello',
  5. foo: 'abc'
  6. }
  7. }
  8. }
  9. new Vue({
  10. mixins: [mixin],
  11. data: function () {
  12. return {
  13. message: 'goodbye',
  14. bar: 'def'
  15. }
  16. },
  17. created: function () {
  18. console.log(this.$data)
  19. // => { message: "goodbye", foo: "abc", bar: "def" }
  20. }
  21. })

二、钩子函数

同名钩子函数将会混合为一个数组,都将被调用到,但是混入对象的钩子将在组件自身钩子之前调用。

  1. var mixin = {
  2. created: function () {
  3. console.log('混入对象的钩子被调用')
  4. }
  5. }
  6. new Vue({
  7. mixins: [mixin],
  8. created: function () {
  9. console.log('组件钩子被调用')
  10. }
  11. })
  12. // => "混入对象的钩子被调用"
  13. // => "组件钩子被调用"

三、值为对象的选项

值为对象的选项,例如 methods, componentsdirectives,将被混合为同一个对象。两个对象键名冲突时,取组件对象的键值对。

  1. var mixin = {
  2. methods: {
  3. foo: function () {
  4. console.log('foo')
  5. },
  6. conflicting: function () {
  7. console.log('from mixin')
  8. }
  9. }
  10. }
  11. var vm = new Vue({
  12. mixins: [mixin],
  13. methods: {
  14. bar: function () {
  15. console.log('bar')
  16. },
  17. conflicting: function () {
  18. console.log('from self')
  19. }
  20. }
  21. })
  22. vm.foo() // => "foo"
  23. vm.bar() // => "bar"
  24. vm.conflicting() // => "from self"

全局混入

全局混合被注册到了每个单一组件上。因此,它们的使用场景极其有限并且要非常的小心。一个我能想到的用途就是它像一个插件,你需要赋予它访问所有东西的权限。但即使在这种情况下,我也对你正在做的保持警惕,尤其是你在应用中扩展的函数,可能对你来说是不可知的。

  1. Vue.mixin({
  2. mounted() {
  3. console.log("我是mixin");
  4. }
  5. })
  6. new Vue({
  7. ...
  8. })

再次提醒,小心使用它!那个 console.log将会出现在每个组件上。这种情况还不算坏(除了控制台上有多余的输出),但如果它被错误的使用,你将能看到它会多么的有害。

一个使用合理的例子

  1. // 为自定义的选项 'myOption' 注入一个处理器。
  2. Vue.mixin({
  3. created: function () {
  4. var myOption = this.$options.myOption
  5. if (myOption) {
  6. console.log(myOption)
  7. }
  8. }
  9. })
  10. new Vue({
  11. myOption: 'hello!'
  12. })
  13. // => "hello!"

总结

混合对于封装一小段想要复用的代码来讲是有用的。对你来说它们当然不是唯一可行的。混合很好,它不需要传递状态,但是这种模式当然也可能会被滥用。所以我们还是需要仔细斟酌使用喽!!

例子

  1. import {mapGetters} from 'vuex'
  2. // 目的是想要处理 scroll 的bottom值,在含有playlist列表的情况下
  3. export const playlistMixin = {
  4. computed: {
  5. ...mapGetters([
  6. 'playList'
  7. ])
  8. },
  9. mounted() {
  10. this.handlePlaylist(this.playList)
  11. },
  12. activated() {
  13. this.handlePlaylist(this.playList)
  14. },
  15. watch: {
  16. playlist(newVal) {
  17. this.handlePlaylist(newVal)
  18. }
  19. },
  20. methods: {
  21. // 如果组件中没有这个方法,那么就报错
  22. handlePlaylist() {
  23. throw new Error('component must implement handlePlaylist method')
  24. }
  25. }
  26. }