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
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
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
// 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
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:
<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
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
Tip: keep the cheat sheet open!
https://www.vuemastery.com/pdf/Vue-3-Cheat-Sheet.pdf
TIP
Advanced: Why the Composition API was introduced in Vue https://vuejs.org/guide/composition-api-introduction.html
Data API fixtures
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
moebius/
├── tsconfig.json
└── vite.config.js
Moebius uses Vite
, which is a web development build tool that supports:
- a fast development environment with hot reload*(using
native javascript modules
)* - building an optimized version for production*(using
rollup
andtree-shaking
)* - native support of typescript
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
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
pnpm lint
Linters can fix a lot of issues all by themselves. To do so, try running
pnpm lint:fix
TIP
Install recommended extensions, linting will then occur each time files are saved!