유형 문제: Nuxt, Vue 구성 API로 Vuetify

안녕하세요 여러분! 이것은 내 첫 번째 기사이며 Nuxt.jsVue Composition API 을 사용하여 최근 경험을 공유할 수 있게 되어 정말 기쁩니다.

저는 현재 Nuxt를 기반으로 하는 작은 장난감 프로젝트를 진행하고 있습니다. 이 프로젝트는 다음 기술 기반을 사용합니다.
  • Nuxt.js
  • 타이프스크립트
  • 뷰티파이
  • 동화책

  • 또한 Vue3에서 사용할 Vue Composition API를 추가했습니다. 그러나 Nuxt와 Typescript를 사용하는 환경에는 몇 가지 문제가 있습니다.

    시작하겠습니다! 어떤 문제가 있었고 그것을 해결하는 방법.

    Nuxt 구성 요소 옵션



    Nuxt.js는 다양한 구성 요소 옵션을 제공하며 Typescript를 사용하는 경우 @nuxt/types에서 구성 요소 옵션을 찾을 수 있습니다.

    // node_modules/@nuxt/types/app/vue.d.ts
    
    /**
     * Extends interfaces in Vue.js
     */
    
    import Vue from 'vue'
    import { MetaInfo } from 'vue-meta'
    import { Route } from 'vue-router'
    import { Context, Middleware, Transition, NuxtApp } from './index'
    import { NuxtRuntimeConfig } from '../config/runtime'
    
    declare module 'vue/types/options' {
      interface ComponentOptions<V extends Vue> {
        asyncData?(ctx: Context): Promise<object | void> | object | void
        fetch?(ctx: Context): Promise<void> | void
        fetchDelay?: number
        fetchOnServer?: boolean | (() => boolean)
        head?: MetaInfo | (() => MetaInfo)
        key?: string | ((to: Route) => string)
        layout?: string | ((ctx: Context) => string)
        loading?: boolean
        middleware?: Middleware | Middleware[]
        scrollToTop?: boolean
        transition?: string | Transition | ((to: Route, from: Route | undefined) => string | Transition)
        validate?(ctx: Context): Promise<boolean> | boolean
        watchQuery?: boolean | string[] | ((newQuery: Route['query'], oldQuery: Route['query']) => boolean)
        meta?: { [key: string]: any }
      }
    }
    
    declare module 'vue/types/vue' {
      interface Vue {
        $config: NuxtRuntimeConfig
        $nuxt: NuxtApp
        $fetch(): void
        $fetchState: {
          error: Error | null
          pending: boolean
          timestamp: number
        }
      }
    }
    

    그러나 Nuxt 구성 요소에서 Vue Composition API를 사용하면 기본 유형 범위가 @nuxt/types에서 @vue/composition-api로 변경됩니다.

    따라서 layout , middleware , fetch와 같이 nuxt에만 있는 일부 구성 요소 옵션에는 유형을 사용할 수 없습니다.

    예를 들어 보겠습니다.

    <template>
      <div>Hello Vue Composition API!</div>
    </template>
    
    <script lang="ts">
    import { defineComponent } from '@vue/composition-api'
    
    export default defineComponent({
        layout: 'some-layout' // Error: No overload matches this call
    })
    </script>
    

    기본적으로 Typescript 환경에서 composition-api를 사용하기 위해 definedComponent 를 선언합니다.
    layout 속성을 사용하려면 definedComponent 에서 선언해야 하지만 IDE 또는 편집기에서 해당 유형을 찾을 수 없다는 오류(또는 경고)가 표시됩니다.

    이 상황에서 layout를 사용할 수 없는 이유를 추론할 수 있습니다.

    // node_modules/@vue/composition-api/dist/index.d.ts
    
    import Vue$1, { VueConstructor, ComponentOptions, VNode, CreateElement } from 'vue';
    
    ...
    
    interface ComponentOptionsBase<Props, D = Data, C extends ComputedOptions = {}, M extends MethodOptions = {}> extends Omit<ComponentOptions<Vue, D, M, C, Props>, 'data' | 'computed' | 'method' | 'setup' | 'props'> {
        data?: (this: Props, vm: Props) => D;
        computed?: C;
        methods?: M;
    }
    
    ...
    
    declare type ComponentOptionsWithoutProps<Props = unknown, RawBindings = Data, D = Data, C extends ComputedOptions = {}, M extends MethodOptions = {}> = ComponentOptionsBase<Props, D, C, M> & {
        props?: undefined;
        setup?: SetupFunction<Props, RawBindings>;
    } & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>;
    
    ...
    
    declare function defineComponent<RawBindings, D = Data, C extends ComputedOptions = {}, M extends MethodOptions = {}>(options: ComponentOptionsWithoutProps<unknown, RawBindings, D, C, M>): VueProxy<unknown, RawBindings, D, C, M>;
    declare function defineComponent<PropNames extends string, RawBindings = Data, D = Data, C extends ComputedOptions = {}, M extends MethodOptions = {}, PropsOptions extends ComponentPropsOptions = ComponentPropsOptions>(options: ComponentOptionsWithArrayProps<PropNames, RawBindings, D, C, M>): VueProxy<Readonly<{
        [key in PropNames]?: any;
    }>, RawBindings, D, C, M>;
    declare function defineComponent<Props, RawBindings = Data, D = Data, C extends ComputedOptions = {}, M extends MethodOptions = {}, PropsOptions extends ComponentPropsOptions = ComponentPropsOptions>(options: HasDefined<Props> extends true ? ComponentOptionsWithProps<PropsOptions, RawBindings, D, C, M, Props> : ComponentOptionsWithProps<PropsOptions, RawBindings, D, C, M>): VueProxy<PropsOptions, RawBindings, D, C, M>;
    

    예! 찾았습니다! 문제는 definedComponent가 기본 VueComponentOptions 유형만 지원한다는 것입니다. 이 문제를 어떻게 해결할 수 있습니까?

    vue-shims.d.ts



    먼저 프로젝트 루트의 vue-shim.d.ts 폴더에 파일@types을 생성합니다. (이 documentation , vue-shim.d.ts 를 본 적이 있다면 이미 존재합니다.)

    import Vue from 'vue'
    
    import { Context, Middleware } from '@nuxt/types'
    
    ...
    
    declare module 'vue/types/options' {
      interface ComponentOptions<V extends Vue> {
            fetch?(ctx: Context): Promise<void> | void
        layout?: string | ((ctx: Context) => string)
            middleware?: Middleware | Middleware[]
      }
    }
    

    그리고 위의 코드와 같이 ComponentOptions 인터페이스를 'vue/types/options' 모듈에서 extends Vue로 선언합니다.

    내부적으로 이 선언의 의미는 다음과 같습니다.
  • vue-shim.d.ts가 Vue의 기본 ComponentOptions를 확장함
  • definedComponent가 1단계에서 선언된 새 ComponentOptions 인터페이스를 확장합니다
  • .
  • definedComponent에서 새로 추가된 유형을 사용할 수 있습니다.

  • 좋은! 이제 Nuxt.js ComponentOptions 유형을 사용할 수 있습니다!

    $vuetify



    Vuetify is a Material Design component framework for Vue.js



    Vuetify는 Nuxt 및 Composition 환경의 ComponentOptions와 같은 유형과 유사한 문제가 있습니다. 즉, this.$vuetify 에서 definedComponent 유형에 접근할 수 없습니다.

    아마도 Nuxt.js에서 Vueitfy를 사용한다면 @nuxtjs/vuetify
    @nuxtjs/vuetify는 다음과 같이 Nuxt 컨텍스트에서 $vuetify 유형을 제공합니다.

    // node_modules/@nuxtjs/vuetify/dist/index.d.ts
    
    import { Module } from '@nuxt/types';
    import { Framework } from 'vuetify';
    import { Options, TreeShakeOptions, VuetifyLoaderOptions } from './options';
    declare module '@nuxt/types' {
        interface Configuration {
            vuetify?: Options;
        }
        interface Context {
            $vuetify: Framework;
        }
    }
    declare const vuetifyModule: Module<Options>;
    export { Options, TreeShakeOptions, VuetifyLoaderOptions };
    export default vuetifyModule;
    

    이 문제는 위의 문제와 같이 새로운 타입을 선언함으로써도 해결할 수 있습니다.

    // vue-shim.d.ts
    
    import { Framework } from 'vuetify'
    
    ...
    
    declare module 'vue/types/vue' {
      interface Vue {
        $vuetify: Framework
      }
    }
    

    이제 $vuetify 유형도 이와 같이 사용할 수 있습니다!

    <script lang="ts">
    import { defineComponent } from '@vue/composition-api'
    
    export default defineComponent({
      setup(_, context) {
            ...
        const { width } = context.root.$vuetify.breakpoint
            ...
      }
    })
    </script>
    

    결론


    Nuxt.jsVue Composition API를 함께 사용하는 것은 아주 좋은 선택이 될 수 있습니다. 그러나 합성 API는 Nuxt(특히 TypeScript)에 대해 아직 완전히 지원되지 않습니다.

    물론 이 글의 내용이 전부는 아니지만 Typescript 환경에서 Nuxt와 Composition API를 사용하고자 하는 분들에게 도움이 되었으면 합니다.

    이 주제가 더 궁금하시다면 nuxt-community/composition-api 프로젝트를 확인해보세요!

    고맙습니다!

    좋은 웹페이지 즐겨찾기