vue.jsvuejs3nuxt.jsnuxt3.js

setup script for more than one component from one place - Nuxt3


i use Nuxt3

I have an /components/icons folder

Inside the /components/icons there are many icons :

The icons are in svg format and I want to write, for example, this code for all these icons without repeating them in each file.

Example of any icon file :

<script setup lang="ts">
const props = defineProps({
  size: {
    type: Number,
    required: false,
  },
});

const svg = ref();
const size = ref(props.size);

onMounted(() => {
  if (!size.value) {
    size.value = parseFloat(window.getComputedStyle(svg.value).fontSize);
  }
});
</script>
<template>
  <svg
    ref="svg"
    :width="size"
    :height="size"
    viewBox="0 0 24 24"
    fill="none"
    xmlns="http://www.w3.org/2000/svg">
    <path
      d="M3.35288 9.7093C4.00437 6.92879 6.17301 4.75775 8.95043 4.10553C10.9563 3.6345 13.0437 3.6345 15.0496 4.10553C17.827 4.75775 19.9956 6.92879 20.6471 9.70931C21.1176 11.7174 21.1176 13.8072 20.6471 15.8152C19.9956 18.5957 17.827 20.7668 15.0496 21.419C13.0437 21.89 10.9563 21.89 8.95044 21.419C6.17301 20.7668 4.00437 18.5957 3.35288 15.8152C2.88237 13.8072 2.88237 11.7174 3.35288 9.7093Z"
      fill="#363853"
      fill-opacity="0.15"
      stroke="#363853"
      stroke-width="1.5" />
    <path
      d="M14.5 12.7622H9.5M12 15.265L12 10.2595"
      stroke="#363853"
      stroke-width="1.5"
      stroke-linecap="round" />
  </svg>
</template>

I don't want to repeat this code in every icon, Also when calling icons it is by default like this and I do not want to change the way it is used :

<IconsAdd></IconsAdd>
<IconsApps></IconsApps>
<IconsBookmark></IconsBookmark>
<IconsArrowsUp></IconsArrowsUp>

This feature is available in nuxt :

Component Names If you have a component in nested directories such as:

-| components/
---| base/
-----| foo/
-------| Button.vue

... then the component's name will be based on its own path directory and filename, with duplicate segments being removed. Therefore, the component's name will be:

<BaseFooButton />

Solution

  • This always depends on the case.

    extends is optimal to extend base components with some script logic being augmented and template being fully replaced, as it effectively replaces render option in this case.

    Sometimes it's more effective switch to options API. This includes lifecycle hooks like mounted, they can be stacked up, according to how extends works.

    Otherwise extends can be used with composition API. It extended props and other options, but doesn't affect setup, it needs to be handled manually. Props can be explicitly specified, this can useful in some cases, e.g. for better support of prop autocompletion in IDE.

    setup can be composed but the variables that are local to base setup cannot be replaced due to how variable scopes work in JavaScript, this needs to be taken into account. E.g.:

    BaseIcon.vue

    <template></template>
    <script>
    export default {
      props: ...
      setup(props) {
        ...
    }
    </script>
    

    IconSome.vue

    <template>
    <svg>...
    </template>
    <script>
    import BaseIcon from './BaseIcon.vue'
    export default {
      extends: BaseIcon,
      props: {
        ...BaseIcon.props,
        foo: ...
      },
      setup(props, ctx) {
        const instance = Base.setup(props, ctx);
        instance.bar = ref(2); // potentially bad
        let baz = ref(3); // ok if Base.setup doesn't contain baz 
        return { ...instance , baz };
      }
    

    script setup should be avoided with extends because it's DSL and not spec-compliant JavaScript, it works in undocumented and unpredictable manner, e.g. it's known to compile to a component without render option in production, this prevents components with templates to be extended in an expected way.