HJ29 / vue3-tabs

A touch swipe tabs for vue 3
39 stars 7 forks source link

I got error "Component is missing" trying to use vue3-tabs component in vuejs3 app #1

Closed sergeynilov closed 3 years ago

sergeynilov commented 3 years ago

Hello, In @vue/cli app I try to use vue3-tabs component and for this in src/main.js I added lines :

import { Tabs, Tab, TabPanels, TabPanel } from 'vue3-tabs'

...

app.use(Tabs)
app.use(Tab)
app.use(TabPanels)
app.use(TabPanel)

export default app.config.globalProperties
app.config.globalProperties.toast = DKToast

app.mount('#app')

and in src/views/test.vue :

<template>
  <div>

    selectedTab:{{ selectedTab}}<br>
    stringVar:{{ stringVar}}<br>
    tabs:{{ tabs}}
    <h3>Basic Example</h3>
    <tabs
      v-model="selectedTab"
    >
      <tab
        class="tab"
        v-for="(tab, i) in tabs"
        :key="`t${i}`"
        :val="tab"
        :label="tab"
        :indicator="true"
      />
    </tabs>
    <tab-panels
      v-model="selectedTab"
      :animate="true"
    >
      <tab-panel
        v-for="(tab, i) in tabs"
        :key="`tp${i}`"
        :val="tab"
      >
        {{ tab }}
      </tab-panel>
    </tab-panels>
  </div>
</template>

<script>
  import { ref, onMounted, reactive } from 'vue'

  import { Tabs, Tab, TabPanels, TabPanel } from 'vue3-tabs'
  export default {
    name: 'testTrePage',
    components: {
      Tabs,
      Tab,
      TabPanels,
      TabPanel
    },
    setup () {
      let stringVar = ref('Init value')
      const tabs = ['A', 'B', 'C']
      const selectedTab = ref(2)
      const state = reactive({
        selectedTab: tabs[1]
      })

      const testOnMounted = async () => {
        console.log('testOnMounted!!!::')
      }

      onMounted(testOnMounted)

      return {
        stringVar,
        tabs,
        selectedTab
        // ...toRefs(state)
      }
    } // setup() {

  }
</script>

and I got error :

[Vue warn]: Component is missing template or render function. 
  at <Anonymous modelValue=2 onUpdate:modelValue=fn > 
  at <TestTrePage onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< null > > 
  at <RouterView> 
  at <App>

and what I see in browser : https://prnt.sc/vmmgxn

What is wrong?

In file package.json :

    "vue": "^3.0.0",
    "vue3-tabs": "^0.1.4",

Thanks!

HJ29 commented 3 years ago

use defineComponent() to wrap sfc object should make it work.

<script>
import { ref, onMounted, defineComponent } from 'vue'
import { Tabs, Tab, TabPanels, TabPanel } from 'vue3-tabs'
export default defineComponent({
  name: 'testTrePage',
  ...
})
</script>

I also not sure why there is problem without defineComponent, still studying about it. https://v3.vuejs.org/api/global-api.html#definecomponent From the documentation, defineComponent should do nothing but giving support for ide hinting I always writing it because it hint autocomplete in setup(), so i didn't discover there is problem without defineComponent. Kindly let me know if you find out something.


Extra info you mixed up using component and plugin

  1. Register globally by plugin https://v3.vuejs.org/guide/plugins.html#using-a-plugin
    
    // main.js
    import { createApp } from 'vue'
    import App from './App.vue'
    // default export vue plugin
    import TabsPlugin from 'vue3-tabs'

const app = createApp(App)

// app.use() install vue plugin app.use(TabsPlugin)

app.mount('#app')

2. Register globally by component
https://v3.vuejs.org/guide/component-registration.html#component-names
use this method if u want to customize template component name
``` js
// main.js
import { createApp } from 'vue'
import App from './App.vue'
// these export are vue components
import {Tabs, Tab, TabPanels, TabPanel} from 'vue3-tabs'

const app = createApp(App)

// app.component() register vue component
// this is for you to customize template component name like app.component('custom-tabs', Tabs),
// so you can use it like <custom-tabs/> in template
app.component('tabs', Tabs)
app.component('tab', Tab)
app.component('tab-panels', TabPanels)
app.component('tab-panel', TabPanel)

app.mount('#app')

From what I understand, your register component locally in test.vue, so u don't have to write those code in main.js https://v3.vuejs.org/guide/component-registration.html#local-registration

sergeynilov commented 3 years ago

Thanks! With defineComponent wrapper I removed all tabs refs from main.js and all works ok.

Could you please explain about defineComponent? Does it provide some possibilities for IDE?

I use PhpStorm 2019.2.5 and I would like to have more hints on vuejs objects, methods...

Also Using Composition API I found it rather messy after vuejs2 to use all vars, objects, methods, computed in 1 setup method... If yes, please provide link where it is described...

Thanks!

HJ29 commented 3 years ago

https://v3.vuejs.org/guide/typescript-support.html#defining-vue-components Not sure about phpstorm, I am using vscode When writing typescript, since {} is a plain object to ide, ide doesn't know the structure of this object. For example, ide doesn't know what are valid arguments for setup(). defineComponent has proper interface defined, so ide know what type to hint for defineComponent object.


One of the example Without defineComponent, everything is any in a plain object without interface Screenshot from 2020-11-23 01-52-09 With defineComponent, ide know there is props argument in setup(), and it know where the type of props comes from, so ide can hint user what had been defined in props. Screenshot from 2020-11-23 01-51-38


if you want to refer options api like methods, data, computed in setup(), I searched it before, and I didn't found a solution.


https://www.youtube.com/watch?v=6HUjDKVn0e0 For composition api, I am not sure if you watched the video above. I found it helpful to understand why use composition api. From what i had understood, composition api aims to write reusable feature code. In vue 2 era, it is harder to write reusable feature code when the feature involved of reactivity or component lifecycle. With composition api, you can write a very specific feature in a js/ts file, which i found cleaner instead of make it live in component or using mixin. So, the goal is to slice code by feature, separate out related computed, data, methods and logic for a specific feature and write it in a separate js/ts file.

That's my understanding, I am still learning vue 3, correct me if I am wrong.