Skip to content
Advertisement

Can I define nested component without creating new file?

What I want:

Parent.vue

<template>
    <div>
        <span>{{foo}}</span>
        <Child/>
    </div>
</template>

<script setup>
import { ref, defineComponent } from 'vue';

let foo = ref('foo');

let Child = defineComponent({
    name: 'Child',
    template: `<span>{{bar}}</span>`,
    setup() {
        let bar = ref('bar');

        return { bar };
    }
});
</script>

I wanted to make very small dedicated component for one page.

It looks pretty good. But it doesn’t work.

I don’t want to make a new file and use export default.

and also don’t want register it globally.

Is there no way to create component like this?

=====

answer:

main.js

import { createApp } from 'vue'; (X)

import { createApp } from 'vue/dist/vue.esm-bundler.js'; (O)

it works. Why…

Advertisement

Answer

Your code does not work because the template of the Child component is provided as a string.

Note on In-Browser Template Compilation

When using Vue without a build step, component templates are written either directly in the page’s HTML or as inlined JavaScript strings. In such cases, Vue needs to ship the template compiler to the browser in order to perform on-the-fly template compilation. On the other hand, the compiler would be unnecessary if we pre-compile the templates with a build step.

Our default tooling setups use the runtime-only build since all templates in SFCs are pre-compiled. If, for some reason, you need in-browser template compilation even with a build step, you can do so by configuring the build tool to alias vue to vue/dist/vue.esm-bundler.js instead

You are using Vue SFC – which means you are using Vite or Vue CLI. Both are preconfigured to use runtime only build and precompile all Vue templates inside the <template> tags of all SFC files. BUT your Child component has a template as a string and thus requires build with the compiler included. And this is the reason why importing from vue/dist/vue.esm-bundler.js fixes the issue (although I recommend to use a solution described in the docs instead of importing like that because you can easily end up with two versions of Vue used which leads to very strange and hard to debug errors)

Other solution is to avoid string templates and write your simple component using render function

<script setup>
import { ref, defineComponent, h } from 'vue'

const Child = defineComponent({
    name: 'Child',
    setup() {
        const bar = ref('bar');
        return () => h('span', bar.value)
    }
});
</script>
    
<template>
  <Child></Child>
</template>

Demo

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement