Skip to content
On this page

Moebius and Vue 3

Vue 3 introduces the Composition API wich is a set of additive, function-based APIs that allow flexible composition of component logic.

You may want to take a look of what changed since Vue 2 here:
https://vuejs.org/guide/migration/introduction.html#overview

You still can still write components using old Option API (data / computed / method components fields).
Therefore, all Vue2 plugins/components should be compatible with Vue3.

Here is a great cheat sheet provided by the vue core team to help you get started with the Composition API:
https://www.vuemastery.com/pdf/Vue-3-Cheat-Sheet.pdf

Global structure

sh
moebius/
├── .vscode/
├── nginx/
├── public/
   ├── api/
   └── img/
├── src/
   ├── assets/
   ├── components/
   ├── composition/
   ├── layouts/
   ├── models/
   ├── pages/
   ├── partials/
   ├── App.vue
   ├── main.ts
   ├── router.ts
   ├── shims-html5.d.ts
   └── shims-vue.d.ts
├── .dockerignore
├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── .prettierignore
├── .prettierrc.js
├── Dockerfile
├── index.html
├── package-lock.json
├── package.json
├── stylelint.config.js
├── tsconfig.json
├── vite.config.js
└── yarn.lock

The directory structure can seem unusual for someone who is not familiar with build tools and javascript bundlers. We will break each part of it, so you understand how everything works.

Router, layouts and pages structure

sh
moebius/
├── src/
   ├── layouts/
      ├── AppLayout.vue
      ├── AuthLayout.vue
      ├── LandingLayout.vue
      └── WizardLayout.vue
   ├── pages/
      ├── auth/
      ├── workspace/
      ├── 404.vue
      └── index.vue
   └── router.ts

Moebius uses vue-router for Vue 3, you can find more information by visiting the corresponding documentation: https://next.router.vuejs.org/

Vue router allows to dynamically render a component as a page based on a provided url.
In order to do so, we have to bind each route to a component.

Page components are regular components, they live under the pages/ folder. Note: You can create your page files anywhere but we recommend that you do it inside the pages/ folder as it helps keeping the project structured

The routes are defined in the ./src/router.ts file.
Here is an example of registering a new page with a new layout

typescript
// file ./src/router.ts
import { RouteRecordRaw } from 'vue-router'

// lazyload component using "const Component = () => import()" syntax
const MyLayout = () => import('/@src/layouts/MyLayout.vue')

const routes: RouteRecordRaw[] = [
  {
    // all routes declared as children will use MyLayout
    path: '/my-path',
    component: MyLayout,
    children: [
      {
        // match exact url: /my-path
        path: '',
        component: () => import('/@src/pages/index.vue'),
      },
      {
        // match exact url: /my-path/my-page
        path: 'my-page',
        name: 'my-path-my-page', // you can set a name for your route here
        component: () => import('/@src/pages/my-page.vue'),
      },
    ],
  },
]

To create a new layout, your component's template needs to have a <RouterView />, which gets replaced by your page component.
Note: You can create your layout files anywhere but we recommend that you do it inside the layouts/ folder as it helps keeping the project structured

Components structure

sh
moebius/
├── src/
   ├── components/
      ├── common/
      ├── landing/
      ├── users/
      ├── workspace/
         ├── drawers/
         ├── dropdown/
         ├── messages/
         ├── modals/
         └── panels/

The components folder holds all reusable elements. They consist in chunks of code that you can reuse accross your application: it can be a button, a navbar, a content section or whatever you want. You can create as many subfolders as you want to organize your components.

Here is an example of a component:

vue
<script lang="ts">
// file: ./src/components/MyComponent.vue
import { defineComponent } from 'vue'

// You can load any other components ('/@src/' is an alias to '<base>/src' folder)
import OtherComponent from '/@src/components/OtherComponent.vue'

const MyComponent = defineComponent({
  name: 'MyComponent',
  components: {
    OtherComponent,
  },
  setup() {
    // MyComponent Composition API

    return {}
  },
})

export default MyComponent
</script>

<template>
  <!-- MyComponent Template -->

  <OtherComponent />
</template>

TIP

start typing <script in an empty .vue file to trigger a snippet suggestion.

Naming a component:

Some components names begin with The, like TheConversation.vue. Those are inteded to have a single active instance.
Also when referring to components inside the template, consider using CamelCase insted of pascal-case notation.

TIP

You can find all recommandations and best practices in the Vue3 style guide
https://vuejs.org/style-guide

The Composition API

sh
moebius/
├── src/
   ├── composition/
      ├── state/
         ├── ui/
         ├── user/
         └── workspace/
      └── use/

Since we now have the powerful Composition API, we do not need to use vuex anymore to share state between components.
Here is an example using vuex composition api
Instead we register variables in separate files using the ref or reactive methods from the new vue package. Those variables will be reactive across all your project. You can see related examples in ./src/composition/state

Also, to reduce component size and allow easy code reusability, code logic is split into Composable functions. Those functions create a scoped state (usefull for a reusable dropdown, for instance). You can see related examples in ./src/composition/use

TIP

Advanced: Why the Composition API was introduced in Vue https://vuejs.org/guide/composition-api-introduction.html

Data API fixtures

sh
moebius/
├── public/
   ├── api/
      ├── auth/
         ├── login.json
         └── register.json
      ├── conversations/
         ├── conversation-x.json
         └── users.json
      └── users.json

You probably already noticed that there are json files located in the public/api folder.
Those files are requested by our state Composition API to simulate calls to a real world API.

The structure is arbitrary and probably won't reflect what you will need to implement for your backend. This fake API is simply here to give you examples of on how to request remote data using the Composition API (we are using the axios package, which is one of the most common for HTTP REST requests).

Vite, Rollup and Typescript

sh
moebius/
├── tsconfig.json
└── vite.config.js

Moebius uses Vite, which is a web development build tool that supports:

Under the hood, when runing npm run dev, it runs vite.

To learn more about this awesome tool made by the vuejs core team, check the Vite documentation. You will learn how to:

  • Add postcss pre-processors
  • Integrate JSX
  • Implement fancy Web Assembly code
  • Add new asset path aliases
  • And much more

Typescript is just an extention of Javascript, If you are new to it, don't be afraid because all valid JavaScript code is also TypeScript code. (TypeScript documentation)
The main advatages of using Typescript are:

  • Validates your code ahead of time
  • Provides auto-completion
  • Supports complex Type checking

Keep it clean with linters

sh
moebius/
├── .vscode/
   ├── extensions.json
   └── settings.json
├── .editorconfig
├── .eslintrc.js
├── .prettierignore
├── .prettierrc.js
└── stylelint.config.js

Because we love clean code, we have configured 3 linters which have their own purpose:

  • eslint: prevent code quality concerns (no unused vars, ...)
  • stylelint: prevent css quality concerns (no invalid colors, ...)
  • prettier: handles formatting rules (max line lenght, ...)

You can check the quality of your code by running

bash
# using npm
npm run lint

# using yarn
yarn lint

Linters can fix a lot of issues all by themselves. To do so, try runing

bash
# using npm
npm run lint:fix

# using yarn
yarn lint:fix

TIP

Install recommended extensions, linting will then occur each time files are saved!

All Rights Reserved