DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Writing multiple Vue components in a single file
Hugo Di Francesco
Hugo Di Francesco

Posted on • Originally published at codewithhugo.com on

Writing multiple Vue components in a single file

Writing multiple components in one file is a pattern from React where some files contain multiple components.

Some of those components are β€œprivate” to the file/exported component since no other component needs to consume them.

Here’s Swizec complaining about it:

Creating new tiny Vue components is cumbersome, so it leads to duplicated code like this 🀨

Booking description should be a tiny component that just gets stuff from props and maybe even lives in the same file as the Real Component.

Used twice, but not reusable or an atom πŸ€” pic.twitter.com/6WzEO5knen

β€” Swizec Teller (@Swizec) 6 September 2018

Since I don’t have the full code for the above, we’re just going to use the default β€œHello World” component from a project scaffolded using vue-cli as an example.

By default there are two files, one for App and one for HelloWorld (at src/App.vue and components/HelloWorld.vue). HelloWorld takes a msg prop and renders it.

To write these in a single file, using React it might look something like this:

const HelloWorld = ({ msg }) => (<div>
  <h1>Hello world</h1>
  <div>{msg}</div>
</div>);

const App = () => (<div id="app">
  <HelloWorld msg="Welcome to Your React App" />
</div>);

export default App;
Enter fullscreen mode Exit fullscreen mode

Since React is β€œJust JavaScript” you can have multiple component definitions in one file, not export some of them (just to keep the exported component DRY).

In Vue, it’s still possible, but it’s a tiny bit more complicated since there is more than one way to achieve this:

Examples repo at github.com/HugoDF/vue-multiple-components-in-sfc.

Using a render function

<template>
  <div id="app">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>
<script>
// inline component
const HelloWorld = {
  props: ['msg'],
  render(h) {
    return h('div', [
      h('h1', 'Hello world'),
      h('div', this.msg)
    ])
  }
};
export default {
  name: 'app',
  components: {
    HelloWorld
  }
}
</script>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
Enter fullscreen mode Exit fullscreen mode

Using Vue.component and a template

<template>
  <div id="app">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>
<script>
import Vue from 'vue';
// inline component with template string :+1:
const HelloWorld = Vue.component('hello-world', {
  props: ['msg'],
  template: `<div>
    <h1>Hello world</h1>
    <div>{{ this.msg }}</div>
  </div>`
});
export default {
  name: 'app',
  components: {
    HelloWorld
  }
}
</script>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
Enter fullscreen mode Exit fullscreen mode

Does this work if the runtime isn’t included?

NOPE

[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

found in

--------> <HelloWorld>
        <App>
          <Root>

Enter fullscreen mode Exit fullscreen mode

Thankfully, we can fix it using a build with the template compiler (see https://code.luasoftware.com/tutorials/vuejs/vue-cli-3-include-runtime-compiler/)::)Pretty much, create (if it doesn’t exist) vue.config.js and add:

module.exports = {
  runtimeCompiler: true
};

Enter fullscreen mode Exit fullscreen mode

As pointed out in the linked article, this adds the Vue template compiler to your bundle… which is around 10KB.Restart the dev server: npm run serve:

Using a template and no Vue.component

<template>
  <div id="app">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>
<script>
// inline component with template string :+1:
const HelloWorld = {
  props: ['msg'],
  template: `<div>
    <h1>Hello world</h1>
    <div>{{ this.msg }}</div>
  </div>`
};
export default {
  name: 'app',
  components: {
    HelloWorld
  }
}
</script>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
Enter fullscreen mode Exit fullscreen mode

Still works (if we have the right vue.config.js with runtimeCompiler enabled):

Since I don’t have the full code for the above, w# Using JSX (compiled to render functions)

We can rewrite our initial example of a render function with JSX:App.js:

<template>
  <div id="app">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>
<script>
// inline component with JSX
const HelloWorld = {
  props: ['msg'],
  render() {
    return (<div>
      <h1>Hello world</h1>
      <div>{this.msg}</div>
    </div>);
  } 
};

export default {
  name: 'app',
  components: {
    HelloWorld
  }
}
</script>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
Enter fullscreen mode Exit fullscreen mode

Vue CLI 3+

If you use Vue-cli greater or equal to version 3.0 you are in luck as JSX is supported.https://scotch.io/tutorials/using-jsx-with-vue-and-why-you-should-care

Vue CLI < 3.0

You’ll need to dig around and have a look at babel-plugin-transform-vue-jsx.

I will probably involve installing the following packages:

npm install\
  babel-plugin-syntax-jsx\
  babel-plugin-transform-vue-jsx\
  babel-helper-vue-jsx-merge-props\
  babel-preset-env\
  --save-dev
Enter fullscreen mode Exit fullscreen mode

Again examples repo at github.com/HugoDF/vue-multiple-components-in-sfc.

Feel free to tweet at me @hugo__df.

This was originally published at https://codewithhugo.com

Raymond Rasmusson

Top comments (0)

🌚 Browsing with dark mode makes you a better developer.

It's a scientific fact.