Skip to content

Moebius and Vue 3

Vue 3 introduces the Composition API which 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 Vue 2 plugins/components should be compatible with Vue.

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

bash
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
├── pnpm-lock.yaml
├── package.json
├── stylelint.config.js
├── tsconfig.json
├── vite.config.js

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

bash
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://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

bash
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 across your application: it can be a button, a navbar, a content section or whatever you want. You can create as many sub-folders 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 intended to have a single active instance.
Also when referring to components inside the template, consider using CamelCase instead of pascal-case notation.

TIP

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

The Composition API

bash
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 (useful 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

bash
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

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

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

Under the hood, when running pnpm dev, it runs vite.

To learn more about this awesome tool made by the Vue 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 extension 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 advantages of using Typescript are:

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

Keep it clean with linters

bash
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 length, ...)

You can check the quality of your code by running

bash
pnpm lint

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

bash
pnpm lint:fix

TIP

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

All Rights Reserved