【问题标题】:unable to use Mixins in vue with typescript无法在带有打字稿的 vue 中使用 Mixins
【发布时间】:2019-01-23 04:19:12
【问题描述】:

我有这样的文件夹结构

--Page    
   -group.vue    
--Services
  -groupMixin.ts

group.vue 的脚本

<script lang="ts">
     import { Vue, Component, Mixins } from 'vue-property-decorator'

     import { GroupMixin } from '../../services/groupMixin';
     @Component
     export default class Group extends Mixins(GroupMixin) {
        created () {
          console.log(this.test)
        }
      }
</script>

groupMixin.ts 的代码

import { Vue } from 'vue-property-decorator'
//creating mixins.
export class GroupMixin extends Vue {
  test: string = 'sss'
}

我在这里面临两个问题。

首先,要导入我使用../../ 的ts 文件,有什么方法可以使用./ 或@/。在不使用 lang="ts" 的情况下,我可以导入这样的 js 文件 @/services/...

其次,无法访问我在 groupmixin.ts 中声明的变量 test

【问题讨论】:

  • 你在用vue-cli吗?
  • 是的,我正在使用它。
  • 您忘记在 groupMixin.ts 中添加 @Component

标签: typescript vue.js vue-mixin


【解决方案1】:

我今天花了很多时间试图弄清楚如何让 Vue mixin 在 TypeScript 项目中工作。显然,教程中所说的使用 mixin 的所有正常方式在 TypeScript 中根本不起作用。组件无法访问其 mixin 中定义的属性,因为 Vue 框架的 mixin 代码显然不是 TypeScript 友好的。

最终,我确实找到了一种让 mixin 在 TypeScript 中工作的方法。事实上,工作得很好。我的项目中有多个级别的 mixin 继承,mixin 扩展了其他 mixin,这一切都完全按照我的预期工作。秘密是我必须安装这个第三方包,这是有人写的用来修复 TypeScript 中的 mixins:

https://www.npmjs.com/package/vue-typed-mixins

几句警告(但都不是什么大不了的事):

  1. 这个插件只适用于我在 .ts 文件而不是 .vue 文件中定义我的 mixin。这对我来说不是问题,因为我的 mixin 只包含代码,没有 html 或 css(我想不出有什么意义)。

  2. 当您在组件中包含 mixin 时,请确保您按照与包网站上的示例相同的方式进行操作(上面的网址)。如果您只是安装包而不重构代码以遵循站点上的示例,它将无法正常工作。

这是一个简单的例子:

// /src/mixins/MyMixin.ts

import Vue from "vue";

export default Vue.extend({
    data: function () {
        return {
            mixinMessage: "this came from MyMixin!"
        };
    },
    created: function () {
        console.log("MyMixin.created()");
    },
    mounted: function () {
        console.log("MyMixin.mounted()");
    },
    methods: {
        mixinOutput: function (text:string) {
            console.log("mixin says: " + text);
        }
    }
});

然后由以下人员使用:

// /src/components/MyComponent.vue

<template>
    <div>
        whatever
    </div>
</template>

<style>
    /* whatever */
</style>

<script lang="ts">
    import mixins from "vue-typed-mixins";
    import MyMixin from "../mixins/MyMixin";

    export default mixins(MyMixin).extend({
        created: function () {
            console.log("MyComponent.created()");
        },
        mounted: function () {
            console.log("MyComponent.mounted()");

            this.mixinOutput("hello from MyComponent");
            this.mixinOutput(this.mixinMessage);
        }
    });
</script>

【讨论】:

  • 我没有使用此解决方案,但您在 (1) 中使用 .ts 而不是 .vue 文件的建议为我解决了这个问题。不需要插件。 .Vue 的一个用例是,例如,如果您有一些依赖于某些特定 CSS 的代码,那么您需要同时混合来自同一来源的 CSS 和与之关联的代码。 .. 一个 .vue 文件。
  • 这个解决方案可以很容易地包含多个 mixins export default mixins(mixin1, mixin2, mixin3).extend({})
【解决方案2】:

请尝试执行以下操作以使您的 mixin 正常工作:

group.vue

<script lang="ts">
 import Vue from 'vue';
 // mixins only exist in `vue-class-component` and Component is a default export.
 import Component, { mixins } from 'vue-class-component';

 import { GroupMixin } from '../Services/groupMixin';

 @Component
 export default class Group extends mixins(GroupMixin) {
    created () {
      console.log(this.test)
    }
  }
</script>

groupMixin.ts

import { Vue } from 'vue'

export class GroupMixin extends Vue {
  test: string = 'sss'
}

我使用Vue 导入import Vue from 'vue'; 是有原因的,主要是因为某些IDE 在从vue-class-component 导入时突出显示$emit 等Vue 函数。

至于导入 ts 文件,如果您不使用 vue-cli,则需要设置 webpack's resolve aliastsconfig.json,并且可能需要使用 tsconfig-paths

【讨论】:

  • 它的 vue-class-component 对吗?不是 vue-class-decorator。
  • 是的,你是对的,我把它和'vue-property-decorator'混淆了。刚刚更新。谢谢
  • 已更新以匹配您提供的文件夹结构。
  • 是的,文件夹结构一切正常,一旦我构建它,this.test 将其显示为未定义。
【解决方案3】:

基于@Joe Irby's answer,我发现它可以在没有 vue-typed-mixins 的情况下工作。

随着你的 mixin 扩展了 Vue,你可以通过扩展你的 mixin 来创建你的组件:

// MyMixin.ts

import Vue from "vue";

export default Vue.extend({
    data: function () {
        return {
            message: "Message from MyMixin!"
        };
    }
});


// MyComponent.vue

<template>
    ...
</template>

<script lang="ts">
    import MyMixin from "@/mixins/MyMixin";

    export default MyMixin.extend({
        mounted: function () {
            console.log(this.message);
        }
    });
</script>

【讨论】:

  • 如果我需要使用多个不同的 mixin 怎么办?
  • 你可以试试export default MyMixin1.extend(MyMixin2.extend({...}))。是的,它很丑,但这个解决方案更像是一种 hack,而不是一种可行的模式......?
  • 优秀的解决方案。
【解决方案4】:

截至目前,有 2 种方法可以在 Typescript/Vue 中使用 mixin:

  1. 如果您的 mixin 只包含变量:
// mixins/title.ts
import { Vue, Component } from 'vue-property-decorator'

@Component
export default class titleMixin extends Vue {
  public test: string = 'Hello, hello, hello'
}

// Page.vue
import { Component, Vue, Mixins } from 'vue-property-decorator'
import titleMixin from '@/mixins/title'

export default class Page extends Mixins(titleMixin) {
  private mounted(): void {
    console.log(this.test) // would print: Hello, hello, hello
  }
}
  1. 如果您使用生命周期挂钩:
// mixins/title.ts
import { Vue, Component } from 'vue-property-decorator'

@Component
export default class titleMixin extends Vue {
  private mounted(): void {
    console.log('somevalue')
  }
}

// Page.vue
import { Component, Vue } from 'vue-property-decorator'
import titleMixin from '@/mixins/title'

@Component({
  mixins: [titleMixin]
})
export default class Page extends Vue {} // this will print console.log

这种方式对我有用。你可以看看'vue-class-component'包:https://github.com/vuejs/vue-class-component/blob/master/test/test.ts#L389

【讨论】:

    【解决方案5】:

    mixins.ts

    import { Vue, Component } from "vue-property-decorator";
    
    @Component
    export default class Mixin extends Vue {
      public perfectScrollbarSetting: object = {
        maxScrollbarLength: 750
      };
      public widthClient: number = 0;
      public heightClient: number = 0;
    }
    

    文件 About.vue

    <template>
    </template>
    <script lang="ts">
    import { Vue, Component, Mixins } from "vue-property-decorator";
    import { generalInfo } from "../../store/modules/general";
    import Mixin from "../../mixin/mixins";
    @Component({
      mixins: [Mixin]
    })
    export default class About extends Mixins(Mixin) {
      mounted() {
        console.log(this.widthClient) //it's work
      }
    
    }
    </script>
    

    【讨论】:

    • 你只需要mixins: [Mixin]extends Mixins(Mixin)
    • 您绝对不需要为每个文件声明 mixins。通过为 mixins 声明模块来使用全局方式。
    【解决方案6】:

    如果您不使用 vue-class-component(目前我不是因为它不使用 setup/composition api work well),您可以使用 defineComponent 作为 mixin,它可以在带有 typescript 的 vue 3 中工作

    mixin 示例:

    yor_mixin.ts

    import {defineComponent} from "vue"
    
    interface MixinState{
        lastScrollPosition: number;
    }
    
    export default defineComponent({
        data(): MixinState{
            return {
                lastScrollPosition: 0,
            }
        },
        methods:{
            scrollDisable(){
                document.body.style.overflow = "hidden";
                this.lastScrollPosition = window.pageYOffset;
            },
            scrollEnable(){
                document.body.style.overflow = "auto";
                window.scrollTo(0, this.lastScrollPosition)
            }
        }
    })
    

    和组件

    
    <script lang="ts">
    import {computed, defineComponent} from 'vue';
    import {useStore, MUTATIONS} from "@/store";
    import scrollDisableMixin from "@/mixins/scrollDisable";
    
    export default defineComponent({
      mixins: [scrollDisableMixin],
    
      setup(){
        const store = useStore();
        return {
          expanded: computed(() => store.state.menu.expanded),
          toggle: () => store.commit(MUTATIONS.TOGGLE_MENU_MAIN),
        }
      },
    
      watch: {
        expanded: function(){
          this.expanded ? this.scrollDisable() :this.scrollEnable();
        }
      }
    
    })
    

    【讨论】:

    • 这是处理 vue3 和 options API 时最透明的解决方案。 Downsite 是 mixin 不再知道组件 this,因此可能需要进行一些重构。
    • @OnnovanderZee mixin 怎么知道这个组件? mixins 不知道它将在哪个组件中使用,所以它只知道它自己的 this。也许我什么都不知道(我从 vue 3 开始,在我专注于 react 之前)
    • mixin 被合并到组件中,就像 vue 执行 Object.assign(component, mixin1, mixin2) 一样。因此,在合并之后,组件中声明的所有内容都会暴露给 mixin,反之亦然。这包括已知的名称冲突风险。
    • 好的。所以你可以在 mixin 和 component 中声明相同的变量。默认情况下,组件中的合并策略变量会从 mixins 中覆盖它。但是您可以编写自己的合并策略,然后通过 mixins.docs 覆盖组件中的选项。我知道到处走走,但是当您在 mixins 中声明所有变量时,就会更干净。最后,文档说我们应该使用带有钩子而不是 mixins 的组合 API。
    【解决方案7】:

    作为 mixins 的缺点,为什么不尝试对 Vue3 的设置进行 mixins 的重构: https://codesandbox.io/s/delicate-sea-0xmim?file=/src/components/HelloWorld.vue:2262-2495 Comp.vue

    export default {
      name: "HelloWorld",
      setup() {
        const { test } = useGroup();
        console.log(test);
        return Object.assign({}, useGroup());
      },
      mounted() {
        this.compRef = this;
      },
      props: {
        msg: String,
      },
    };
    

    UseGroup.js

    import { ref, nextTick, onMounted } from "vue";
    
    export default () => {
      const compRef = ref(null);
      const test = "Hello World!";
    
      onMounted(() => {
        nextTick(() => {
          if (compRef.value) {
            const vm = compRef.value;
            console.log(vm.$el, vm.msg);
          }
        });
      });
      return {
        test,
        compRef
      };
    };
    

    【讨论】:

      【解决方案8】:

      此答案适用于愿意使用vue-class-component(装饰器)的人...您要做的就是从'vue-class-component'导入Options并添加您的在那里混合。

      步骤:

      1- 创建你的 mixin:在这个例子中,我正在创建一个 mixin 来格式化时间 (HH:MM:ss)

      //Mixin
       export default {
         methods: {
           formatTime(date: string) {
             return new Date(date)
               .toTimeString()
               .replace(/.*(\d{2}:\d{2}:\d{2}).*/, "$1");
           },
         },
       };
      

      2- 通过将 mixin 添加到 Option 装饰器中来使用组件中的 mixin。

         //Home.vue
      
          import { Options, Vue } from "vue-class-component";
      
          import formatTimeMixin from '../mixin/formatTime';
      
          <template>
             <h1> Time left: {{formatTime(timeLeft)}} </h1>
          </template>
      
          @Options({
            mixins: [formatTimeMixin],
          })
          export default class Home extends Vue {
             timeLeft = 10000;
          }
      

      就是这样,希望这对那里的人有所帮助!

      【讨论】:

        猜你喜欢
        • 2019-01-21
        • 2019-09-08
        • 2018-12-07
        • 2020-04-25
        • 2021-06-02
        • 2020-09-09
        • 2020-04-06
        • 2018-08-02
        • 2023-02-17
        相关资源
        最近更新 更多