【问题标题】:Vue JS typescript component cannot find inject instance propertiesVue JS typescript 组件找不到注入实例属性
【发布时间】:2019-08-06 13:40:30
【问题描述】:

我正在使用 typescript 和 vue。

在我的应用中有一个service,它是每个子组件的全局变量。

我在vue JS上找到this native vue solution在子组件上注入这个属性。

在 main.ts 上

const service = new MyService(...);

new Vue({
  router,
  provide() { return { service }; } // provide the service to be injected
  render: h => h(App),
}).$mount("#app");

在任何 typescript vue 组件上

import Vue from "vue";

export default Vue.extend({
  inject: ["service"], // inject the service
  mounted() {
    console.log(this.service); // this.service does exists 
  },
});

这样我就可以在我的子组件上获得注入的service

但是我得到了休闲错误

“CombinedVueInstance > > 类型上不存在属性“服务”。

我该如何解决这个打字稿编译错误?

【问题讨论】:

    标签: javascript typescript vue.js vuejs2


    【解决方案1】:

     使用 Vue 属性装饰器

    Vue-property-decorator,它在内部从vue-class-component 重新导出装饰器,公开了一系列提供非常好的智能感知的打字稿装饰器。不过你必须使用类 api。

    @Inject@Provide 就是其中两个这样的装饰器:

    在提供者中:

    import {Vue, Component, Provide} from 'vue-property-decorator';
    
    @Component
    export default class MyClass {
      @Provide('service') service: Service = new MyServiceImpl(); // or whatever this is
    }
    

    在提供的组件中:

    import {Vue, Component, Inject} from 'vue-property-decorator';
    
    @Component
    export default class MyClass {
      @inject('service') service!: Service; // or whatever type this service has
      mounted() {
        console.log(this.service); // no typescript error here
      },
    }
    

    我认为这是最佳解决方案,因为它在使用 Vue 时提供了更好的智能感知。

    但是,现在您可能不想更改所有组件的定义,或者由于外部限制而根本无法更改。在这种情况下,您可以执行下一个技巧:

    铸造这个

    您可以将this 投射到任何您要使用this.service 的位置。可能不是最好的,但它确实有效:

      mounted() {
        console.log((this as any).service);
      },
    

    肯定还有其他方法,不过我已经不习惯Vue.extends api了。如果你有时间和机会,我强烈建议你切换到类 API 并开始使用 vue-property-decorators,它们确实提供了最好的智能感知。

    【讨论】:

    • 是否可以在 Vue 3 + typescript + vue-class-component 的类组件中使用提供/注入,但没有 vue-property-decorator?
    【解决方案2】:

    使用插件

    如果你想在所有 Vue 组件中使用某些服务,你可以尝试使用plugins
    在 main.ts 中,您只需导入它:

    import Vue from "vue";
    import "@/plugins/myService";
    

    plugins/myService.ts 你必须写这样的东西:

    import _Vue from "vue";
    import MyService from "@/services/MyService";
    
    export default function MyServicePlugin(Vue: typeof _Vue, options?: any): void {
        Vue.prototype.$myService = new MyService(...); // you can import 'service' from other place
    }
    
    _Vue.use(MyServicePlugin);
    

    在任何 vue 组件中你都可以使用这个:

    <template>
      <div> {{ $myService.name }}</div>
    </template>
    <script lang="ts">
      import { Component, Vue } from "vue-property-decorator";
    
      @Component
      export default class MyComponent extends Vue {
        private created() {
          const some = this.$myService.getStuff();
        }
      }
    </script>
    

    不要忘记在d.ts 文件上声明$myService。在项目的某处添加文件myService.d.ts,内容如下:

    import MyService from "@/services/MyService";
    
    declare module "vue/types/vue" {
      interface Vue {
          $myService: MyService;
      }
    }
    

    【讨论】:

    • 您可能还应该添加 declare module 'vue/types/options' { interface ComponentOptions&lt;V extends Vue&gt; { myService?: MyService; } } 以便您可以在 main.ts 中执行 export default new Vue(myService
    【解决方案3】:

    您不需要使用类组件来获得它。对于对象组件声明,您有两种方法:

    修补数据返回类型

    export default {
      inject: ['myInjection'],
      data(): { myInjection?: MyInjection } {
        return {}
      },
    }
    

    缺点是您必须将其标记为可选才能将其添加为数据返回。

    扩展 Vue 上下文

    declare module 'vue/types/vue' {
      interface Vue {
        myInjection: MyInjection
      }
    }
    
    export default {
      inject: ['myInjection'],
    }
    

    【讨论】:

      【解决方案4】:

      仅为注入的组件扩展 Vue

      为注入创建一个接口,并为需要的特定组件扩展 Vue:

      main.ts:

      const service = new MyService(...);
      
      export interface ServiceInjection {
        service: MyService;
      }
      
      new Vue({
        router,
        provide(): ServiceInjection { return { service }; } // provide the service to be injected
        render: h => h(App),
      }).$mount("#app");
      
      

      使用你的界面的组件

      import Vue from "vue";
      import { ServiceInjection } from 'main.ts';
      
      export default ( Vue as VueConstructor<Vue & ServiceInjection> ).extend({
        inject: ["service"], // inject the service
        mounted() {
          console.log(this.service); // this.service does exists 
        },
      });
      

      【讨论】:

        猜你喜欢
        • 2018-09-30
        • 2021-09-03
        • 2020-08-26
        • 2020-04-03
        • 2020-07-08
        • 2018-07-01
        • 2018-01-03
        • 2021-03-18
        • 2019-03-06
        相关资源
        最近更新 更多