1. The state of Vue 3 and Composition API

Vue 3 has been released for already a year with its main new feature: a Composition API. As of Autumn 2021, the recommended way to setup a new project is to use Vue 3 with script setup syntax, so hopefully we'll see more and more production-grade apps built on Vue 3.

I had on opportunity to build an app on Vue 3 from scratch, and the information here comes from this experience. This article is meant to show some interesting ways to utilize composition API and how to structure an app around that. The basic knowledge about Vue and Composition API is prefferable.

2. Composable functions and code reuse

New composition API unlocks many interesting ways to reuse code across components. A refresher: previously we split component logic according to component Options: data, methods, created, etc:

// Options API style
data: () => ({
    refA: 1,
    refB: 2,
  }),
// Here we often see 500 lines of code ..
computed: {
  computedA() {
    return this.refA + 10;
  },
  computedB() {
    return this.refA + 10;
  },
},

With Composition API we are not limited to this structure and can separate code according to features, instead of options:

setup() {
    const refA = ref(1);
		const computedA = computed(() => refA.value + 10);
		/* 
			Here could be 500 lines of code as well.
			But the features can stay near each other!
		*/
    const computedB = computed(() => refA.value + 10);
		const refB = ref(2);

    return {
      refA,
      refB,
      computedA,
      computedB,
    };
  },

Vue 3.2 introduced <script setup> syntax, which is just a syntactic sugar of setup() function, making the code more terse. From now on, we'll use script setup syntax, as it is the most current one.

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

const refA = ref(1);
const computedA = computed(() => refA.value + 10);

const refB = ref(2);
const computedB = computed(() => refA.value + 10);
</script>

Here's, in my opinion, a big idea. Instead of keeping the features separated using their placement inside script setup, we can split these into their own own files. Here's the same logic done, with splitting up the files:

// Component.vue
<script setup>
import useFeatureA from "./featureA";
import useFeatureB from "./featureB";

const { refA, computedA } = useFeatureA();
const { refB, computedB } = useFeatureB();
</script>

// featureA.js 
import { ref, computed } from "vue";

export default function () {
  const refA = ref(1);
  const computedA = computed(() => refA.value + 10);
  return {
    refA,
    computedA,
  };
}

// featureB.js 
import { ref, computed } from "vue";

export default function () {
  const refB = ref(2);
  const computedB = computed(() => refB.value + 10);
  return {
    refB,
    computedB,
  };
}

Note that featureA.js and featureB.js export Ref and ComputedRef types, so all this data is reactive!

This specific snippet can seem as a bit overkill, however: