How do you export default from inside script setup in Vue 3?

The export default statement does not seem to work inside <script setup>.

If I try to export it in test.vue:

<template>
  <div id="test" class="test">

  </div>
</template>


<script setup>
const x = 5

export default {
    x
}
</script>


<style scoped lang="scss">
</style>

and then importing it into another blog.vue:

<script setup>
import x from './test'
</script>

I am getting this bulk of error:

app.js?id=3b6365f542826af47b926162803b3ef6:37396 Uncaught Error: Module build failed (from ./node_modules/vue-loader/dist/index.js):
TypeError: Cannot read properties of null (reading 'content')
    at selectBlock (:3000/Users/artur/PhpstormProjects/safa-ameedee.com/node_modules/vue-loader/dist/select.js:23:45)
    at Object.loader (:3000/Users/artur/PhpstormProjects/safa-ameedee.com/node_modules/vue-loader/dist/index.js:67:41)
    at Object../node_modules/babel-loader/lib/index.js??clonedRuleSet-5.use[0]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./resources/vue/backend/components/test.vue?vue&type=script&setup=true&lang=js (app.js?id=3b6365f542826af47b926162803b3ef6:37396:7)
    at __webpack_require__ (app.js?id=3b6365f542826af47b926162803b3ef6:64806:42)
    at Module../resources/vue/backend/components/test.vue?vue&type=script&setup=true&lang=js (app.js?id=3b6365f542826af47b926162803b3ef6:60116:217)
    at __webpack_require__ (app.js?id=3b6365f542826af47b926162803b3ef6:64806:42)
    at Module../resources/vue/backend/components/test.vue (app.js?id=3b6365f542826af47b926162803b3ef6:59477:102)
    at __webpack_require__ (app.js?id=3b6365f542826af47b926162803b3ef6:64806:42)
    at Module../node_modules/babel-loader/lib/index.js??clonedRuleSet-5.use[0]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./resources/vue/backend/components/blog.vue?vue&type=script&setup=true&lang=js (app.js?id=3b6365f542826af47b926162803b3ef6:37336:63)
    at __webpack_require__ (app.js?id=3b6365f542826af47b926162803b3ef6:64806:42)
    at Module../resources/vue/backend/components/blog.vue?vue&type=script&setup=true&lang=js (app.js?id=3b6365f542826af47b926162803b3ef6:60084:217)
    at __webpack_require__ (app.js?id=3b6365f542826af47b926162803b3ef6:64806:42)
./node_modules/babel-loader/lib/index.js??clonedRuleSet-5.use[0]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./resources/vue/backend/components/test.vue?vue&type=script&setup=true&lang=js @ app.js?id=3b6365f542826af47b926162803b3ef6:37396
__webpack_require__ @ app.js?id=3b6365f542826af47b926162803b3ef6:64806
./resources/vue/backend/components/test.vue?vue&type=script&setup=true&lang=js @ app.js?id=3b6365f542826af47b926162803b3ef6:60116
__webpack_require__ @ app.js?id=3b6365f542826af47b926162803b3ef6:64806
./resources/vue/backend/components/test.vue @ app.js?id=3b6365f542826af47b926162803b3ef6:59477
__webpack_require__ @ app.js?id=3b6365f542826af47b926162803b3ef6:64806
./node_modules/babel-loader/lib/index.js??clonedRuleSet-5.use[0]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./resources/vue/backend/components/blog.vue?vue&type=script&setup=true&lang=js @ app.js?id=3b6365f542826af47b926162803b3ef6:37336
__webpack_require__ @ app.js?id=3b6365f542826af47b926162803b3ef6:64806
./resources/vue/backend/components/blog.vue?vue&type=script&setup=true&lang=js @ app.js?id=3b6365f542826af47b926162803b3ef6:60084
__webpack_require__ @ app.js?id=3b6365f542826af47b926162803b3ef6:64806
./resources/vue/backend/components/blog.vue @ app.js?id=3b6365f542826af47b926162803b3ef6:59328
__webpack_require__ @ app.js?id=3b6365f542826af47b926162803b3ef6:64806
./resources/js/router.js @ app.js?id=3b6365f542826af47b926162803b3ef6:39847
__webpack_require__ @ app.js?id=3b6365f542826af47b926162803b3ef6:64806
./resources/js/app.js @ app.js?id=3b6365f542826af47b926162803b3ef6:39770
__webpack_require__ @ app.js?id=3b6365f542826af47b926162803b3ef6:64806
(anonymous) @ app.js?id=3b6365f542826af47b926162803b3ef6:64971
__webpack_require__.O @ app.js?id=3b6365f542826af47b926162803b3ef6:64843
(anonymous) @ app.js?id=3b6365f542826af47b926162803b3ef6:64973
(anonymous) @ app.js?id=3b6365f542826af47b926162803b3ef6:64975

Upvotes: 16

Views: 50923

Answers (7)

Emmanuel Ovensehi
Emmanuel Ovensehi

Reputation: 39

Rename your component to TestComponent.vue. Locate into a folder /src/components/. You can try the code below in your parent vue file.

<template>
   <TestComponent/>
</template>

<script>
   import { defineComponent } from 'vue'
   import TestComponent from '/src/components/TestComponent.vue'
   export default defineComponent({
      components: {
         TestComponent
      }
   })
</script>

<script setup>
   // store
   // locals
   // methods
   // lifecycle hooks
</script>

Upvotes: 3

tao
tao

Reputation: 90103

TL/DR

By design, a <script setup> cannot have any ES module exports (explanation below). To export from a Vue component you have at least two options:

  • place whatever you want to export in its own .js or .ts file and import it in the Vue file as well as anywhere else you might need it
  • use a normal <script> alongside the <script setup> and export from the normal one. Each block is treated as a separate module by the the vue SFC (single file component) compiler (@vue/compiler-sfc), so you can use both default or named exports in a normal <script>, even when that component also has a <script setup> block.

Why doesn't <script setup> allow exports?

<script setup> is an alternative way of writing the script part of Vue SFC. The contents of a <script setup> are parsed by the script setup macro and exported as default.

That is why you can't declare the default export inside a <script setup>: the default export is reserved for the macro's output from parsing its contents.

Also note that, because having both a default and named exports from the same module is not recommended, <script setup> does not allow any exports inside of it, although technically it could allow named exports.


In a nutshell, the macro takes the contents of <script setup>, separates the imports from everything else and outputs:

/** imports go here **/

const componentDefinition = defineComponent({ 
  setup() {
    /** the rest of contents goes here **/
  }
})
export default componentDefinition

The above is an over-simplification, outlining the principle 1.

In reality, the macro does more: it extracts emits, watch, props, expose and more from the setup contents, and places each in the appropriate section of the component definition, which is then exported as default.

And it also constructs the return value of the setup() function, making all declared constants and imports available for use in <template>.


1 - In reality, the script setup macro does not export a defineComponent() code block, because defineComponent is sugar syntax for lower level options, such as the render function of the component. I'm only claiming it does to outline the way the script setup macro works.
To see it in action, go to Vue SFC Playground, open up the JS tab and watch the output changing as you change <script setup> contents.

Upvotes: 0

PrestonDocks
PrestonDocks

Reputation: 5418

The solution is to use defineOPtions I had the a similar problem where without the setup script I would done the following

<script>
import AnotherLayout from "../layouts/Another.vue"
export default {
layout: AnotherLayout
}
</script>

With the setup script, you have to use the following syntax.

<script setup>
import AnotherLayout from "../layouts/Another.vue"
defineOptions({
  layout: Another
})
</script>

See the VueJS Docs for more info https://vuejs.org/api/sfc-script-setup.html#defineoptions

Upvotes: 2

Johan
Johan

Reputation: 435

'Components using script setup are closed by default' - said from their own docs.

You need to expose using defineExpose()

<script setup>

const x = 5;

defineExpose({
  x
})
</script>

https://vuejs.org/api/sfc-script-setup.html#defineexpose

Upvotes: 1

Jeremy Chong
Jeremy Chong

Reputation: 27

Hello i just see from others and it went well for me. Just add name properties next to script setup and it will export your component.

<script setup name="Greet">
</script>

it is the same as

 export const MyComponent = {
 name: "MyComponent" // I'm not 100% set on this part}

So you can just add your code in the parent component like this.

<script setup>
 import Greet from './components/Greet.vue';
</script>

<template>
  <Greet/>
</template>

Upvotes: -3

Bear Bones
Bear Bones

Reputation: 39

I didn't see this marked as answered so here's what I found dealing with same issue:

You can just use regular block alongside the block and place your export in that.

Here is the relevant documentation: https://vuejs.org/api/sfc-script-setup.html#usage-alongside-normal-script

Upvotes: 2

hungify
hungify

Reputation: 387

Create new file *.d.ts like vue.d.ts or paste script below this vite-env.d.ts file (generate by vite)

declare module '*.vue' {
  import type { DefineComponent } from 'vue';
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, any>;
  export default component;
}

and then you can use script setup> without defining two script tags

Upvotes: 3

Related Questions