Skip to content

Documentation Front End

Galadows edited this page Jul 25, 2022 · 20 revisions

Nuxt Logo


FrontEnd maintenable et scalable avec Nuxt2

L’objectif de cette documentation est de décrire comment fonctionne l’architecture du projet FrontEnd de Fiches&Chips afin que n’importe quel développeur puisse aborder Nuxt plus facilement. Les principales parties seront :

  • Javascript ES6
  • Nuxt en général (SSR, Vue)
  • Architecture du projet
  • Dossier Pages
  • Dossier Api
  • Store Vuex
  • Middleware
  • Tailwind
  • I18n

Si des erreurs se manifestent dans cette documentation, n’hésitez pas à le signaler.

Javascript ES6 - Best Practices

L’arrivée de Javascript ES6 facilite le développement avec ce même langage. De nombreuses conventions sont disponibles pour gagner du temps lors de l’écriture du code de notre FrontEnd. Il est pertinent de pointer du doigts ces principales conventions avant de discuter autour du framework utilisé. Celles-ci permettront alors à tout le monde d’avoir un code + lisible et même plus performant. On pourrait en parler des heures, si le passage en ES6 vous intéresse vous avez tous les changements à ce lien. Ces points suivants visent à proposer quelques bonnes pratiques. Si vous avez des propositions ou suggestions, n’hésitez surtout pas à le faire remonter ! Si vous souhaitez des bonnes pratiques côté Vue, vous pouvez consulter ce lien ci.

Import et Require

Pour la lisibilité, d’abord regrouper les imports externes aux projets. Après deux sauts de ligne, écrire les imports internes au projet.

Import Vue from ‘vue’
Import i18n from ‘i18n’
 
Import Modal from ~/src/components/common/modal.vue’
Import LoginModal from ~/src/components/common/LoginModal.vue’

Simplifier l’écriture d’une variable de type object

const firstname = ‘Gabriel’
const lastname = ‘Riboldi’
const age = 25

const user = {
    'firstname': firstname,
    'lastname': lastname,
    'minor': age < 18,
    'is-active': true
}

On peut simplifier ca avec les règles suivantes :

  • Si la clé et la variable utilisée comme valeur portent le même nom, on peut simplement passer la variable, la clé prendra le même nom
  • Pas besoin de quotes lorsque la clé est simple, mais elles restent nécessaires lorsqu’on a des caractères spéciaux (tirets, espaces, etc.)
const firstname = ‘Gabriel’
const lastname = ‘Riboldi’

const user = {
    firstname,
    lastname,
    minor: age < 18,
    'is-active': true
}

Chaines de charactères avec des variables

Privilégier l’utilisation des " ` " pour écrire des chaînes de caractère, particulièrement si elles sont contiennent des variables (que l’on intègrera à la chaîne de caractère avec la syntaxe ${variable}) ou des sauts de ligne.

console.log('Bonjour ' + firstname + ' ' + lastname + ',' + 'Comment vas-tu ?') 

Ce que l’on transforme en :

console.log(`Bonjour ${firstname} ${lastname}, comment vas-tu ?`) 

Si aucune variable n'est implémentée dans une chaine de caractères, il faut cependant éviter de l'utiliser (inutilement).

Simplifier la recherche dans un objet sur plusieurs niveaux

Il arrive souvent qu’un objet soit décrit sur plusieurs niveaux de profondeur. Lorsqu’on a besoin de vérifier la valeur d’une propriété située à un niveau de profondeur supérieur à 1 (par exemple response.data.user.active), cela déclenchera une erreur si response.data ou si response.data.user ne sont pas définis. On en vient donc à écrire un long code inutile avec beaucoup de copier/coller.

if (response.data && response.data.user && response.data.user.active) { //traitement }  

On peut simplifier cette écriture en naviguant à travers l’objet par l’opérateur ?.

if (response?.data?.user?.active) { //traitement } 

Si un des niveaux n’existe pas, le résultat sera undefined.

Déstructuration d’un Object

ES6 propose plusieurs options de destructuration d’un object.

Ne pas faire :

const firstname = user.firstname 
const lastname = user.lastname 

Plutôt faire :

const { firstname, lastname } = user 

Décomposition simple d’un array

Chaque valeur de l’array est assignée dans l’ordre à une variable.

const [a, b] = [10, 20] 
console.log(a, b) // valeur attendue: 10, 20 

Changer le nom de la variable décomposée

Les valeurs extraites peuvent être assignées à des variables portant un nom différent de la clé.

const numbers = { val1: 10, val2: 20 } 
const { val1: a, val2: b } = numbers 
console.log(val1, val2) // valeur attendue: undefined, undefined 
console.log(a, b) // valeur attendue: 10, 20  

Attention, dans l’exemple ci-dessus, aucune variable n’est déclarée pour val1 et val2, qui ont donc une valeur undefined

Il existe pleins d’autres conventions. A vous de les ajouter/suggérer/partager si vous en savez !

Nuxt, the framework en SSR … pour Vue !

Nuxt est le fameux framework basé sur Vue.js et Node.js. L'utilisation de ce framework a de nombreux avantages comme l'amélioration des processus de l'optimisation pour les moteurs de recherches du fait du rendu côté serveur (SSR) des pages web avant leur envoi vers le client ce qui n'est pas fait de manière générale dans les applications web monopages. En utilisant Nuxt.js les sites webs peuvent ainsi inclure des interfaces utilisateurs plus complexes tout en gardant les avantages du rendu HTML côté serveur.

Si vous êtes familier avec les framework FrontEnd en Server Side Rendering, vous ne serez pas perdu puisque l’on y retrouve ici de nombreuses conventions telles que le FileSystem Routing, la création d’une simple api (qui marche elle aussi en FileSystem Routing) dans un dossier api, du HotReloading, un Store style Redux (nommé Vuex) intégré directement au framework, l'auto-import (oui) etc...


Gif Incredible


Nuxt propose également un système de modules/plugins : des lib directement intégrées au framework.

SSR

Schema SSR

[WIP]

Architecture du projet

./Front
├── Dockerfile
├── README.md
├── assets
│   ├── images
│   ├── fonts
│   └── sounds
├── components
│   ├── Modal.vue
│   ├── NuxtLogo.vue
│   ├── Placeholder.vue
│   ├── subComponent
│   │   ├── CustomInput.vue
│   │   ├── CustomTable.vue
│   │   └── Sidebar.vue
│   └── subModals
│       └── exampleModal.vue
├── locales
│   ├── en.json
│   └── fr.json
├── middleware
│   └── index.js
├── nuxt.config.js
├── package-lock.json
├── package.json
├── pages
│   ├── index.vue
├── playwright.config.js
├── static
│   ├── favicon.ico
├── store
│   └── index.js
├── tailwind.config.js
├── tests
│   └── example.spec.js
├── tsconfig.json
└── yarn.lock

Architecture d'un framework SSR assez classique :

  • Dossier Assets : Tous les assets.

    1. images : Les images utilisées sur la plateforme.
    2. fonts : Les fonts utilisées sur la plateforme.
    3. sounds : Les sons utilisés sur la plateforme.
  • Dossier Components : Tous les composants voués à être réutilisés plusieurs fois.

    1. subComponents : Les Composants génériques, utilisés un peu partout (Input, Button...)
    2. subModals : Le contenu de différentes modales, héritant du design de base d'une modale (Bouton fermer, background...)
    3. Les composants principaux
  • Dossier Locales : Tous les fichiers de traduction sous forme de .json.

    1. locale.fr : Traductions françaises (langue par défaut).
    2. locale.en : Traductions anglaises.

(Il est tout à fait possible de rajouter d'autres fichiers locale.* pour d'autres traductions)

  • Dossier Middleware : Tous les Middleware.

  • Dossier Pages : Toutes les Pages/Routes.

A chaque fois qu'un fichier ou un dossier est créé dans ./pages, une route est ajoutée au serveur.

├── pages
    ├── index.vue ( https://www.fichesetchips.com/ )
    ├── about.vue ( https://www.fichesetchips.com/about
    user
        [id]
            ├── dashboard.vue ( https://www.fichesetchips.com/user/[id]/dashboard )

Dans un dossier, on peut rajouter un index.js pour la route par défaut d'un dossier (elle prendra le nom du dossier), ou une valeur entre [] pour une valeur dynamique envoyée en query dans la requete.

Il est aussi possible de créer un dossier api dans notre dossier Pages afin de créer des routes pour une api. La syntaxe sera la même qu'un serveur express classique.

  • Dossier Static : Fichiers public vouant à ne pas être modifiés.

  • Dossier Layouts : Layouts utilisés pour les différentes pages.

En quelques mots, un Layout c'est une base (design) que l'on va répéter sur plusieurs pages. Au lieu de dupliquer un design sur différentes pages, on utilise un Layout qui va hériter des composants enfants (le contenu de notre page). Par exemple, on peut utiliser un Layout pour définir une font utilisée ou une couleur de background par défaut dans une page.

Par défaut, le dossier n'est pas créé. On peut le créer à la root du projet, dans lequel nous allons mettre nos fichiers [NomDuLayout].vue pour chaque Layout. Petit plus : On peut créer un Layout default.vue, qui sera un Layout que l'on utilisera par défaut sur toutes nos Vues ! Comment écrire notre code pour un Layout :

// La classique balise `<template />`, avec le composant `<Nuxt />`
// qui représente le contenu de votre page (les enfants dudit Layout).

<template>
  <Nuxt />
</template>

Voilà ! Vous pouvez encapsuler cette balise <Nuxt /> avec d'autres composants ...

<template>
  <div>
    <TheHeader />
    <Nuxt />
    <TheFooter />
  </div>
</template>

N'oubliez pas que Tailwind est utilisable dans ces Layouts !

  • Dossier Store : Fichiers permettant la gestion du Store Vuex intégré à la plateforme (il est par défaut désactivé).

Tailwind : Du CSS sans fichier CSS

Tailwind Logo

Tailwind CSS est un framework CSS avec des classes prédéfinies que vous pouvez utiliser pour construire et concevoir des pages web directement dans votre balisage HTML. Cela a l'avantage de ne compiler qu'avec les classes CSS que vous utilisez, ce qui vous évite de vous retrouver avec une web app' comportant 20.000 lignes de CSS (Coucou Bootstrap).

// Exemple d'utilisation de Tailwind pour un Input générique. Merci Havane !

<div class="flex flex-col m-5">
    <label v-if="label" for="">
        {{ label }}
    </label>

    <input
        class="w-full h-12 p-3 border placeholder-gray-700 shadow-inner outline-none placeholder-opacity-50"
        :placeholder="placeHolder"
        type="text"
    />
</div>

Certains n'aiment pas Tailwind à cause du fait que cumuler les class dans une balise rend potentiellement la lecture indigeste (ce qui est un avis tout à fait fondé). Chacun son école, il n'y a pas de vérité absolue.

Nuxt possède un module Tailwind afin d'intégrer facilement la librairie au framework. Après quelques configurations dans le plugin pour purge le server des class CSS que l'on utilise pas...

// Fichier tailwind.config.js

[...]
module.exports = {
    mode: 'jit',
    purge: ['./public/**/*.html', './src/**/*.{js,jsx,ts,tsx,vue}'],
    content: [
        './components/*.{js,vue,ts}',
        './layouts/*.vue',
        './pages/*.vue',
        './plugins/*.{js,ts}',
        './nuxt.config.{js,ts}',
    ],
    theme: { ...
[...]

On arrive à un temps de compilation avec un framework CSS tout à fait honorable ...:

Tailwind Logo

Toutes les classes Tailwind sont disponibles à ce lien. Elles ressemblent fortement aux keywords classiques du CSS. Par exemple, pour un margin horizontal:

// Margin horizontal de 1 rem : mx-4 (soit margin-left: 1rem; margin-right: 1rem;)

<div className="mx-4">
    <p>Coucou la doc !</p>
</div>

On parle ici de margin, mais on peut très bien mettre un texte en Bold, aligner les éléments verticalement, mettre une ombre portée rouge ... ou même créer ses propres couleurs ! Pratique pour définir la palette de couleur d'une plateforme web. Tout se passe dans le fichier tailwind.config.js :

// tailwind.config.js

    [...]
    theme: {
        extend: {
            colors: {
                'fiche-green': '#4FEA74',
                'chips-yellow': '#FFDB57',
            },
        },
    },
    [...]

On a créé deux couleurs grace à 2 valeurs hexadecimales : fiche-green et chips-yellow. Ces couleurs peuvent ensuite être utilisées dans nos balises HTML :

// On peut l'utiliser avec le keyword "text" pour "texte" de Tailwind
<p className="text-fiche-green">
   Trop génial omg
</p>

// On peut aussi l'utiliser avec le keyword "bg" pour "background" de Tailwind
<div className="bg-fiche-green">
    [... Contenu de ma div mais en vert ...]
</div>

L'utilisation est multiple. De notre côté pour Fiches&Chips, pour que la palette reste lisible, nous définissons la couleur de cette manière :

// `fc` pour Fiches&Chips
fc-<couleur>

fc-red

I18n : Anglais & Français

i18n Logo

i18n Nuxt est une librairie permettant la traduction de tous les textes d'une application Web. Cela fonctionne à l'aide de fichiers .json que l'on va remplir avec une key + une valeur. Quand une key est rencontrée parmis le texte mentionné dans le code, ce texte est remplacé par une autre valeur en fonction de l'actuelle locale définie dans le front. Par exemple, notre locale par défaut est fr. Si l'on passe en locale en (anglais) à l'aide d'un i18n Switcher, nos textes passerons en locale en, donc avec les valeurs de notre fichier en.json.

Après quelques configurations dans le fichier nuxt.config.js ...

    i18n: {
        locales: ['fr', 'en'],
        defaultLocale: 'fr',
        vueI18n: {
            fallbackLocale: 'fr',
            messages: {
                en,
                fr,
            },
        },
    },

... nous n'avons qu'à remplir la locale de notre choix :

// Fichier `en.json`

{
    "Nom d'utilisateur": "Username",
    "Mot de passe": "Password"
}

Enfin, pour appeler l'utilaire de traduction dans le front qui va nous permettre d'aller chercher une valeur selon une key :

// Traduction avec la key `Nom d'utilisateur`

<p>
    {{ $t("Nom d'utilisateur") }}
</p>

// Ce qui affichera la valeur de la key, soit "Nom d'utilisateur" en locale fr, ou "Username" en locale en