A Vue3 Tutorial - 07 Vue Components without a Build System
Tags: vue, javascript, beginners, tutorial
Published: 2022-02-23 03:59:03

Edit - This section lets you write a .vue file and use ut from the browser. This works but a better to do this would be to to use the vuejs way of importing things in the next chapter.

https://github.com/Krowemoh/vue3-without-build

Now we are at the point where we can be dangerous. We know enough about Vue to do something useful however there is one more thing I really wanted from a web framework besides having no build system. I really want something that is modular and composable.

One of the things I really liked about react was the ability to write self contained code and build up my own custom tags where all you need to do is pass in props and you'll get a well formed and working set of elements. Vue has this as well and I imagine most frameworks do. You could also make this in plain javascript but ultimately you'll end up create your own custom framework.

Ideally I want to be able to create Vue components and use them in the browser. Unfortunately there isn't a way to do that through vue. ! This chapter wouldn't exist if that was impossible though.

Someone has created a small library to load vue components. This makes it pretty easy to create components and pass props to them. I'll need to dig into it to do more complex things but it works pretty well.

https://github.com/FranckFreiburger/vue3-sfc-loader

In this chapter, we're going to take our table and create a component out of it!

## Clean Up

The first thing we need to do is remove the code that is specific to the table and move it all into a new file called table.vue. This file will be slightly different than what we've been doing. Instead of calling createApp, our vue file simply export everything that would go inside a createApp regularly.

```
export default {
    props: ["headers", "workers"],
    data() {
        return {
            sortColumn: "",
            order: "ASC",
            searchString: "",
        }
    },
    computed: {
        filteredWorkers() {
            ...
        },
    },
    methods: {
        setSortColumn(column) {
            ...
        },
    },
}
```
Here we have the data, computed and methods properties being set but now we only keep the stuff that is relevant to the table.

We also have a new property called props which will contain a string of the keys we want to pass through. The parent component will pass in a variable called headers and a variable called workers when this table component is used.

Next we add the template code to our vue component.

```
<template>
    <div v-if="workers.length === 0">No workers available.</div>

    <div v-else>
        <input v-model="searchString" placeholder="search" class="mb-1">
        <table>
            ...
        </table>
    </div>
</template>
```

Finally we also move the styles over to table.vue.

```
<style>
th { cursor: pointer; }
.arrow { color: gray; }
.active { color: black; }
</style>
```

Now our table component has everything it needs to work. The next step is to now clean up the index.html file. Once the index file only contains what it needs, we can work on the code to load in the table component.

```
<body>
    <script src="https://unpkg.com/vue@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue3-sfc-loader/dist/vue3-sfc-loader.js"></script>
    <div id="app">
        <h1>People!</h1>
    </div>
    <script>
        Vue.createApp({
            data() {
                return {
                    headers: [],
                    workers: [],
                }
            },
            methods: {
                async getWorkers() {
                    ...
                }
            },
            mounted() {
                this.getWorkers();
            }
        }).mount('#app')
    </script>
</body>
```

## Using vue3-sfc-loader

The first step is to include the vue3-sfc-loader. This will let us use .vue files directly in the browser.

```
<body>
    <script src="https://unpkg.com/vue@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue3-sfc-loader/dist/vue3-sfc-loader.js"></script>
    ...
</body>
```

Next we need to set up the options and import in the loadModule function.

```
const options = {
    moduleCache: {
        vue: Vue
    },
    async getFile(url) {
        const res = await fetch(url);
        if ( !res.ok )
            throw Object.assign(new Error(res.statusText + ' ' + url), { res });
        return {
            getContentData: asBinary => asBinary ? res.arrayBuffer() : res.text(),
        }
    },
    addStyle(textContent) {
        const style = Object.assign(document.createElement('style'), { textContent });
        const ref = document.head.getElementsByTagName('style')[0] || null;
        document.head.insertBefore(style, ref);
    },
}

const { loadModule } = window['vue3-sfc-loader'];

Vue.createApp({
    ...
}).mount('#app');
```

I'm guessing that the reason we have getFile and addStyle here is that we may want to customize these functions but they work as is.

Now that we have vue3-sfc-loader ready, we can now start using components!

```
Vue.createApp({
    data() {
        return {
            headers: [],
            workers: [],
        }
    },
    components: {
        'Table': Vue.defineAsyncComponent( () => loadModule('./table.vue', options) )
    },
    template: `<Table :headers="headers" :workers="workers"></Table>`,
    methods: {
        ...
    },
    mounted() {
        ...
    }
}).mount('#app');
```

We specifiy the component we want to use in the components attribute and then in the template attribute we actually reference it. It's curious that it works with an uppercase Table name even though I didn't specify it. For now I'll choose to ignore it but if anyone has answer, please comment!

Now we can pass in props by using the colon followed by the propery to set up a binding. In our component, because we have the props attribute set up, we can then start using these variables that we passed through.

Voila! If everything was done properly, you should now have a single component file that you can include and use from the browser.

We can now use vue SFCs without a build step!

At this point, this is pretty much everything I know about Vue. So not much but enough to get started. Much of this might be the wrong way of doing things but I certainly like this. There is no build step involved and everything is in a file that is well structured. The only drawback is the filesize of the things that are transfered.

Vue is 600kb and vue3-sfc-loader is 1.4mb. So to make applications with the core idea being that there is no build step means shipping 2mb of javascript to the clients machine. This is their raw size, zipped, this comes out to 800kb which is still quite a bit. All that code needs to still be read and compiled before my application even starts.

I'll need to think about it for bit and try it out some more before I can really go all into it.

Overall, Vue was pretty easy to get and start using which was nice, React really did require more effort but it could be that react set me up well to pick up vue.

! Until next time.