Aller au contenu

Nouveau membre ?! Pense à te présenter pour accéder au contenu du forum !

New member ?! Introduce yourself to get access to the forum !

Nicky31

 tutoriel / partage Introduction aux nouvelles technologies JS

Messages recommandés

Hello Funky Emu ! 

 

Pour la sortie tant attendue de la V6 de notre beau forum, j'avais bien envie de reprendre un peu de service en vous partageant quelques notions et nouveaux concepts d'un univers que j'apprécie tout particulièrement depuis 2 ans : le Web. Plus précisément, je vais essayer de vous dresser le tableau  d'une toile qui est sujette à de fortes mutations depuis quelques années, tant au niveau de son utilisation par ses grands acteurs que pour ses fondements techniques, très différents de la norme d'il y a dix ans.

Ayant démarré le développement informatique sur ce même forum il y a maintenant 6 ans sur du web en PHP (pour des CMS Dofus), vous retracer mon exploration de ce domaine et ses technologies me fait vraiment plaisir, et j'espère que ça vous intéressera tout autant que moi.

Bien que maintenant assez éloigné de l'émulation, il m'a semblé que quelque soit le jeux émulé, son écosystème (CMS, émulateurs, autres outils) n'évoluait que très peu au cours des années et qu'il faisait souvent appel aux mêmes architectures, librairies et autres composants : bref, que l'émulation ne bénéficie pas toujours des progrès de l'aire du numérique.

C'est donc dans cette idée que je tente d'amener un petit vent frais sur la section programmation, en espérant que certains soient suffisamment curieux pour me faire des retours et me dire si approfondir le sujet plus loin les intéresserait, ou même mettre en application eux-même ces outils à travers des projets persos.

 

Sommaire

  1. Petit historique
  2. NodeJS, du Javascript côté serveur
  3. NPM
  4. Les Single Page Application
  5. ReactJS
  6. Angular
  7. Le packaging
  8. Le Backend
  9. Les SPA isomorphiques
  10. Conclusion

 

 

1/ Petit historique

 

Commençons par rappeler rapidement la traditionnelle stack (pile de technologiesLAMP encore très utilisée aujourd'hui pour des sites webs : Linux + Apache + Mysql + PHP.
Ce n'est bien sûr pas la seule solution qu'il y avait avant les nouvelles technologies, nous pouvons par exemple citer Ruby on Rails ou J2EE qui ont aussi eu leur lot de popularité, mais LAMP reste certainement l'architecture la plus connue et répandue. 


Image result for lamp linux apache mysql php

 

Dans ce schéma, le navigateur de l'utilisateur (client) envoie une requête HTTP au serveur Web Apache (serveur), Apache exécute le fichier php demandé qui lui renvoie la page HTML après avoir requêté si besoin MySQL. Il ne reste donc à Apache plus qu'à renvoyer cette page au navigateur alors prêt à l'afficher, et exécuter le code javascript y étant contenu. A noter que chaque changement de page entraîne alors une nouvelle requête consommant des ressources serveur pour un traitement souvent en bonne partie identique à celui de la requête précédente.

Jusqu'à il y a quelques années, le javascript exécuté chez notre client était majoritairement utilisé pour des animations esthétiques ou pour dynamiser certaines pages via des requêtes AJAX.

Entre les mauvaises performances et incompatibilités des différents moteurs Javascript (= composant d'un navigateur chargé de l'exécution du javascript), ce langage a longtemps été moqué et c'est pourquoi il n'était alors utilisé que pour des tâches visuelles ou peu critiques.

 

     V8, la résurrection du Javascript

 

V8, c'est un moteur Javascript open-source développé par Google pour son navigateur Chrome, la première release datant de 2008. Ce qu'il a de particulier, ce sont ses performances : utilisant une compilation JIT (Just In Time: a la volée) du Javascript en bytecode (langage machine), V8 s'est avéré être assez performant pour, au cours de ces dernières années, remettre complètement en question la place du Javascript. C'est aussi une course à la performance dans laquelle se sont ainsi lancés tous les géants du web : Mozilla sur son moteur Javascript SpiderMonkey (le tout premier moteur javascript initialement développé par/pour Netscape), Apple sur son JavascriptCore (Safari), Microsoft sur Chakra (Edge/IE), ...

L'enjeux de cette course, c'est ni plus ni moins que de savoir qui pourra le plus bénéficier des nouvelles possibilités ouvertes par ce langage propulsé à une échelle bien supérieure à tout ce qu'il a pu connaître. 

 

Mais Jamie, à quoi ressemble donc ce nouveau champs des possibles o.O !?

 

 

2/ NodeJS, du Javascript côté serveur 

Pour commencer, non, NodeJS n'est bien sûr pas la première solution utilisant du JS côté serveur. L'expérience avait bien déjà été réalisée par Netscape avec la sortie de Rhino en 1997, un autre moteur Javascript prévu pour du server-side. Mais si l'utilisation de NodeJS a tout simplement explosé quelques années seulement après sa sortie en 2009, c'est sans aucun doute parce que son auteur Ryan Dahl a su les réunir les bons éléments au bon moment : 

 

  • Un moteur javascript assez performant pour rendre le JS côté serveur viable 
  • L'implémentation d'une gestion de modules au format CommonJS (Nativement et avant le standard ES2015, le Javascript ne permet pas d'inclure d'autres fichiers) 
  • Des modules permettant l'interaction avec des composants de base comme le système d'exploitation, le système de fichier, les sockets
  • Un gestionnaire de module (npm) permettant l'organisation d'une communauté open source publiant des modules répondant à tout type de besoin
  • L'asynchrone du Javascript utilisé avec un modèle concurrentiel (synchronisation des tâches asynchrones) fiable et efficace. Lorsqu'il reçoit une requête, un serveur Java standard crée un nouveau thread pour la gérer : ce modèle peut causer des problèmes d'accès concurrentiel à la mémoire, de nombreux threads bloqués en attente du retour d'une opération, un trop grand nombre de threads pour les capacités de la machine ... A son inverse, NodeJS fonctionne avec un unique thread disposant d'une queue listant toutes les tâches devant être exécutées. Lorsque vient le tour du traitement d'une nouvelle requête, les instructions sont donc exécutées linéairement jusqu'au prochain appel asynchrone dont le traitement du retour sera ajouté à la file d'attente. Mais tant qu'une instruction asynchrone n'est pas terminée, Node continue de traiter les autres requêtes sans être bloqué. De cette façon, la productivité de votre thread est maximisée, à l'opposé d'un langage comme le Java qui encapsulera ses appels bloquants dans de multiples threads régulièrement rendus 'inactifs'. Bien sûr, cela ne veut pas dire qu'un environnement node sera toujours plus performant que du Java, loin de là, mais c'est sûrement vrai pour de nombreux services webs ne nécessitant pas de lourds calculs CPU. Si vous êtes intéressé par le sujet, cet article décrit le fonctionnement de Node un peu plus en détail et dans quelles situations il prend tout son intérêt.

 

Ce sont je pense les éléments clés de cette plateforme de développement JS.

Pour les curieux, voilà les quelques lignes suffisant à ouvrir un serveur en écoute avec NodeJS (extrait de la doc nodejs) : 

Révélation
const http = require('http'); // Inclusion du module http

const hostname = '127.0.0.1';
const port = 3000;

// Préparation du serveur avec une callback appelée à chaque réception de requête
const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

// Démarrage serveur
server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

 

Comme vous pouvez le voir, ce serveur est minimaliste. A vous d'indiquer le code retour de chaque réponse (200 pour un succès), de renvoyer votre contenu avec une autre méthode (end), et même le dispatch vers la bonne unité d'exécution en fonction de l'URL devra être fait en regardant celle-ci vous même via le paramètre req. Certains seront ennuyés par tout ce superflu, d'autres apprécieront le grand contrôle qu'ils ont sur le cycle de vie d'une requête.

Quoi qu'il en soit, n'oubliez pas que de nombreux modules vous prémâchant le travail existent à tous les niveaux. C'est ce que fait très bien ExpressJS pour la partie HTTP : 

 

Révélation
const express =  require('express');
const app = express();

app.all('/chemin1', (req, res) => {
    res.status(200);
    res.sendFile('chemin/vers/index.html')
});
app.post('/chemin/formulaire', (req, res) => {
    res.status(200);
    res.sendFile('chemin/vers/result.html')
});

app.listen(3000, () => { console.log('Server listening on port 3000'); });

 

 

Je pourrai vous parler des nombreux autres modules et middlewares venant se plugger à express (comme pour de la gestion de session), mais ce n'est pas l'objectif de ce sujet. Retenez qu'une solution web à base de node + express serait très bien pour écrire une API (afin d'interfacer votre utilisation d'une base de donnée ou tout autre service), ou simplement servir des fichiers, mais que pour un site web classique (comme un CMS), Node sera largement insuffisant. On pourrait bien sûr entièrement réaliser un site avec uniquement NodeJS, mais on se retrouverait à générer puis renvoyer bêtement de l'HTML au navigateur en perdant tout l'intérêt du langage par rapport à du PHP 9_9

Nous allons rapidement parler du package manager de Node sur la prochaine partie, juste avant d'entamer la partie la plus intéressante, au sujet de ces technologies JS révolutionnaires qui déferlent sur notre web.

 

 

3/ NPM

Comme mentionné au début de l'introduction de Node, celui-ci dispose de son propre package manager : Node Package Manager.

Il a aussi son propre site, sur lequel vous pouvez chercher et souvent facilement trouver réponse à votre besoin. Une telle communauté s'est aggloméré autour de npm que son utilisation s'est étendue à tout type de projet JS, utilisant ou non Node. Par exemple, si vous devez faire une application entièrement front ne nécessitant même pas l'utilisation de node,  vous commencerez quand même sûrement par créer un projet npm, télécharger les modules de vos technos, puis de setup votre bundler (c'est la 7ème partie) de sorte à pouvoir produire une solution finale contenant chacune des dépendances installées par npm. 

Et pour le bonheur de tous, l'utilisation de npm est des plus simples : 

  • npm init : cette commande permet de créer un nouveau projet npm en initialisant tout simplement le fichier package.json, la configuration de npm pour ce projet
  • npm install : lorsque vous téléchargez / clonez un projet npm (qu'on repère donc à la présence d'un package.json), cette commande téléchargera chacune des dépendances listées dans le package.json
  • npm install --save package_name :  Si vous ajoutez un nom devant npm install, cela installera simplement le package en question. L'option --save permet d'ajouter aussi la nouvelle dépendance au fichier package.json, afin qu'il soit installé lors d'un simple npm install.
  • npm uninstall --save package_name : vous l'aurez compris, c'est l'opération inverse de celle-ci dessus. --save retire donc la ligne du package.json.
  • npm run script_name : au début de votre package.json, vous pouvez voir un objet scripts. Il contient toutes les commandes shell que vous pouvez exécuter avec cette commande, en lui passant en paramètre le nom du script désiré. Très utile pour minifier l'appel de longues commandes que vous devez régulièrement utiliser. Nous verrons un cas d'utilisation très pratique partie 7.
  • npm start : si votre objet scripts contient bien une entrée 'start', exécute celle-ci. C'est une forme raccourcie de npm run start qui ne fonctionne que pour start, une commande réservée au lancement de votre application. C'est donc généralement la deuxième et dernière commande à exécuter après avoir téléchargé une application npm simple, juste après le npm install.

 

Bon, on parle beaucoup d'application js, mais quand est-ce qu'on y vient vraiment ?! Et bien, l'heure est venue :P ...

 

 

4/ Les Single Page Application

Lors du petit historique de ce début de sujet, nous disions qu'avec une stack LAMP classique, chaque changement de page / envoi de formulaire entraînait une nouvelle requête entre le client et le serveur. Et si je vous disais qu'aujourd'hui, le serveur pourrait ne plus recevoir la moindre requête d'un utilisateur une fois que celui-ci a eu sa première page demandée ? Que les changements de page se feraient donc de manière quasi instantanée sans que le client doive attendre le rafraîchissement de sa page  ? C'est ce qu'est une SPA, une application dont l'intégralité du code se trouve dans une seule page. Une fois celle-ci téléchargée, le client peut naviguer de manière très fluide à travers les différents écrans de l'application, n'envoyant de nouvelles requêtes que pour valider un formulaire ou récupérer de nouvelles données à afficher dans une portion bien précise de la page. 

En fait, c'est un type d'application qu'on a tous déjà l'habitude de côtoyer : n'importe quel réseau social moderne (Facebook, Twitter, Linkedin, Youtube, ...) est une SPA.

Qu'est-ce qui nous empêche donc de nous tourner vers cette solution moderne ? Les pré-requis sont assez limités : connaissance du Javascript, curiosité des nouvelles technologies. Pour le premier point, la curiosité peut aussi faire l'affaire. Pour le deuxième, c'est précisément ce sur quoi j'espère vous ouvrir.

 

De nombreux frameworks web front-end défilent depuis quelques années, chacun avec une approche du problème différente, mais de manière générale plus travaillée au cours des années. Certains diront peut-être que je vulgarise un peu trop le 'problème', mais je pense que cette problématique cruciale et commune à tous les frameworks est ni plus ni moins que la synchronisation entre l'état d'une application et son DOM (Document Object Model).

Par cet état, j'entends l'ensemble des variables définies statiquement ou dynamiquement (depuis une requête au backend par exemple), pour être ensuite affichées au milieu du code HTML.

Le DOM, c'est l'arborescence de chaque bloc html (en partant de la racine <html>) construite en mémoire du navigateur après l'interprétation du code HTML, ou de toute manipulation faite en Javascript.
Par exemple, pour les quelques courageux addicts au VanillaJS, faire un elem.appendChild(document.createElement('span')) revient à ajouter dynamiquement un <span> dans elem, via ce fameux DOM virtuel. Même chose pour les déjà-plus-raisonnables pro-jQuery : $(".inner").append("<p>Test</p>");

Cependant, ces fonctions sont pratiques quand il s'agit de changer quelques parties précises de notre page, mais deviennent très vite laborieuses si on veut construire et mettre à jour l'ensemble de notre page html en Javascript.

C'est donc notamment à ce problème que répondent les célèbres frameworks front-end Ember, Backbone, React, Angular et de nombreux autres. Je me contenterai de ne vous présenter succinctement que les deux derniers, étant les plus modernes / populaires et aussi les deux seuls que je maîtrise.
Ces librairies vous permettront de construire des applications web entières comme de plus petits modules que vous pourrez tout aussi aisément intégrer dans un site déjà existant, comme par exemple un chat.

 

5/ ReactJS

ReactJS, c'est le petit bébé développé par et pour Facebook (entre autres) depuis 2013. Si le mobile intéresse certains, il se trouve que Facebook a aussi sorti en 2015 React-Native, un framework JS mobile permettant de développer des applications compatibles Android et iOS, utilisant la même organisation et suivant les mêmes principes que React. En moins de 3 ans de développement, des milliers de contributeurs ont déjà rejoint le projet et les releases se suivent à intervalles très courtes, de sorte que React-Native soit déjà tout à fait viable en production :x. Je tiens aussi à arrêter tout de suite les médisants : non, react-native n'est pas un framework hybride (Xamarin, Ionic ... : applications mobiles embarquant une webview), ou alors à moitié. React-Native utilise JavascriptCore (Moteur JS de Safari) pour exécuter votre code JS qui va faire appel à des composants codés nativement pour les deux plateformes : votre application communiquera donc bien avec le système hôte et non un moteur de rendu. 

 

Mais revenons à nos moutons, React. Et quoi de mieux pour introduire une technologie que son implémentation de l'indétrônable Hello World: https://embed.plnkr.co/ztEh59J1P48HNCglj69s/

Sa particularité, c'est son organisation centrée autour de ses components. Un component, c'est comme un nouveau bloc html qui reçoit des paramètres et se formate correctement tout seul. Dans l'exemple ci-dessus, le component HelloWorld déclaré au début du fichier index.jsx ne reçoit qu'un seul paramètre name pour afficher une simple div avec. 

<HelloWorld name="Funky Emulation" />

Cette syntaxe HTML, qui peut paraître déroutante au beau milieu du code Javascript, est en réalité transformée en une instruction Javascript valide par le module JSX à l'exécution : 

React.createElement(HelloWorld, {name: 'Funky Emulation'})

On se doute qu'une application React serait tout simplement immonde à écrire sans ce petit JSX incontournable, dont vous pouvez d'ailleurs voir la balise d'inclusion au début du fichier html d'exemple.

Bien, je vous ai montré l'éternel et simple exemple d'Hello World, mais qu'en est-il si on veut dynamiser un peu tout ça ? Une TODOLIST par exemple ? 

 

Plunkr <

 

Dans ce bout de code, plusieurs choses nouvelles. Déjà, nos components sont codés en classes Javascript (feature disponible depuis ES2015), une façon de faire que je préfère nettement aux simples fonctions qui ne permettent pas d'organiser les différentes fonctions de traitement du component aussi clairement qu'avec une classe. Remarquons par ailleurs que chacune de ces classes hérite de React.Component, la classe mère de tous les Components.

Ensuite, de nouveaux attributs HTML sont apparus : onSubmit={this.onSubmit.bind(this)} dans le form du component Input permet d'appeler la méthode onSubmit lors de l'évènement onsubmit (envoi du formulaire). De même, onChange={this.onChange.bind(this)} situé juste en dessous permet d'appeler la méthode onChange à chaque nouvelle valeur de l'input. A noter la présence d'accolade lors de l'assignation de ces attributs, qui permet d'injecter du code JS dans ce 'pseudo html' interprété par JSX.

 

Nous pouvons aussi voir une nouvelle assignation this.state = ... dans chaque constructeur : c'est une variable propre à React et contenant l'état interne d'un component. Avec le HelloWorld, nous avons vu la notion de props permettant d'injecter des variables au sein de component, afin de faire descendre une information depuis un component container jusqu'à un component contenu (par la suite, j'appellerai le container 'parent', et le contenu 'enfant').

La différence du state, c'est qu'il ne sera pas rempli par son component parent mais par son propre traitement (le curValue du component Input), ou par l'action d'un component enfant (le state.todolist dans le component App).

Le point commun et l'intérêt de ces deux variables, c'est que le moindre changement de leur valeur forcera l'actualisation du component. Pour le state, l'affichage ne sera cependant rafraîchi que en le modifiant via la fonction setState (héritée de React.Component), afin d'en informer React. Il y a un deuxième point capital à savoir pour le bon déroulé du refresh et très spécifique au fonctionnement de React : lorsqu'on modifie une valeur contenue dans state ou transmise en props à un component enfant, et si c'est un objet, il doit être un clone modifié de l'ancienne valeur. Ce qui explique l'étrange syntaxe que j'ai utilisé pour la fonction d'ajout d'un TODO dans mon component App : 

  newTodo(txt) {
    this.setState({
      todolist: [txt, ...this.state.todolist]
    })
  }

La valeur assignée à todolist est en fait un nouveau tableau composé en premier de la nouvelle valeur, suivie de toutes les valeurs de l'ancien tableau (ce que permet de faire les '...', encore une nouveauté du JS). C'est grâce à la référence de ce nouvel objet que React pourra détecter les changements entre l'ancien state (ou props) et le nouveau : il parcourt récursivement chaque attribut du nouveau state (ou props), compare sa référence à celle correspondante de l'ancien state, et met à jour la partie de l'interface associée si elles sont différentes. 

Voilà un petit schéma (pardonnez mes tristes talents artistiques :S) résumant les différentes interactions entre les 3 components (App, Input, Todolist) : 

 

225233react.png

 

  1. App injecte :
    1. Sa méthode à appeler lors de l'ajout d'une entrée en props de Input
    2. Sa TODOLIST contenu dans le state en props de Todolist 
  2. Input met à jour state.curValue à chaque changement du champs texte.
  3. Input appelle props.onAddTodo lors de l'envoi du formulaire en lui fournissant state.curValue
  4. onAddTodo() met à jour le state de App en reconstruisant le tableau todolist avec la nouvelle valeur
  5. Automatiquement, voyant que le props.todolist transmis à Todolist est un nouveau tableau, React déclenche la mise à jour du component

 

C'est souvent de cette manière que sera construite une application React : les composants enfants interagissant avec l'utilisateur traiteront les données remplies par celui-ci en interne (state), pour ensuite les faire remonter au parent via une fonction transmise dans ses props, permettant au parent de stocker l'information et de la redispatcher à d'autres components.

De manière générale, ce découpage en composants bien distincts est une excellente habitude à prendre et applicable dans n'importe quel contexte de développement, que ce soit ou non du Javascript, et même si ce n'est pas pour du web. Vous êtes ainsi forcé d'organiser clairement votre application en différentes unités chacune assignée à une tâche bien précise, dans un ensemble cohérent mais non couplé (lié trop étroitement) que vous interconnecterez par de l'injection de dépendance, à l'opposée d'une classe depuis laquelle vous instancierez ses dépendances. 

L'intérêt est multiple: chaque unité est  facilement remplaçable, testable, et réutilisable. De plus, le travail collectif sur un tel projet en est aussi grandement simplifié. 

Bref, c'était juste une petite parenthèse pour vous sensibiliser à cette notion de conception utile dans tout contexte de dév, et que les attentifs pourront remarquer dans la prochaine partie :ph34r: ...

 

 

6/ AngularJS

AngularJS, quant à lui, a été publié par Google pour la première fois en Octobre 2010, avant la sortie d'Angular en Mai 2016. Angular est officiellement la v2 d'AngularJS, mais en fait pas vraiment, leur plus gros point commun reste sûrement leur nom. En effet, après 6 ans de travail sur AngularJS et les retours de nombreux contributeurs, Google a certes gardé les grands axes de sa philosophie mais a construit très différemment Angular en fonction des points noirs relevés durant cette longue période : le système de détection de changement (synchronisation datas / views) a été bien plus réfléchi et ainsi largement optimisé, plusieurs notions ont été purement et simplement supprimées ou remplacées par soucis de simplification (AngularJS utilise de nombreux types de composants différents, ce qui est un frein à sa prise en main / bonne utilisation). Ces changements drastiques entre les deux versions a d'ailleurs été source de controverse auprès d'un grand nombre de développeurs, qui n'avaient juste plus affaire à leur outil habituel. Si la question vous intéresse, cet article et de nombreux autres en parlent en long et en large.

 

Si je ne vous parle ici que d'AngularJS, ce n'est absolument pas parce que je suis un vieil aigri nostalgique rebutant Angular, mais uniquement parce je n'ai pas encore eu l'occasion d'utiliser ce dernier, bien qu'il m'intéresse tout à fait. Et en raison de mon idéal de l'évolution des choses où AngularJS disparaîtrait inévitablement dans l'ombre de son successeur, je vais vous dresser une présentation plus succincte d'AngularJS que pour ReactJS, vous laissant l'opportunité de vous y intéresser mais avec tout de même ma suggestion de vous tourner plus vers Angular. Exception faite des cas où on peut avoir tout intérêt à rester sur AngularJS, l'article cité au dessus en parle en fin de page. Quoi qu'il en soit, les grandes lignes directrices de la v1 restent selon moi intéressantes et instructives à voir.

 

Comme pour React, commençons ce petit aperçu par un classique Hello World : https://embed.plnkr.co/1XGvtv75Ij4daOtvLaYZ/

Une première chose devrait vous sauter aux yeux, c'est la légèreté de la syntaxe par rapport à React. 

  1. angular.module('app', []) : cette ligne renvoie un nouvel objet module lié au block html ayant l'attribut "ng-app='app'". Notez la présence du deuxième paramètre permettant de lier d'autres modules dépendants au nôtre dès sa création. Pour accéder au module 'app' après sa création, omettez simplement ce deuxième paramètre. Sinon, il sera réécrit et toutes les ressources liées perdues.
  2. .controller('MyCtrl', ... : cette instruction chaînée au module lui associe un nouveau contrôleur, nommé 'MyCtrl' et implémenté par la fonction en deuxième paramètre
  3. function ($scope) { $scope.name = 'Funky Emulation }) : La fonction en question, ainsi responsable de son scope

 

Nous avons donc 3 notions centrales d'AngularJS : 

  • Le module, est un objet lié à un block html (ayant le bon ng-app) et contenant toutes les ressources nécessaires à la gestion du block en question. Parmi celles-ci, il y a cette liste de modules dépendants renseignée en deuxième paramètre du premier appel d'angular.module, et pouvant servir tout type de besoin : un module router pour amener le client sur la bonne vue en fonction de l'url, un module facilitant l'accès aux cookies, ... Bien souvent, ce sont des modules que vous pourrez télécharger via npm. 
  • Le contrôleur, est une simple fonction responsable de la logique métier de son block html lié et ses enfants. J'appellerai par la suite ce block la vue du contrôleur. Donc, la liaison entre le contrôleur et sa vue s'établit simplement avec l'attribut ng-controller sur cette dernière, que vous pouvez voir juste après l'ouverture du body.
    Le contrôleur accède à toutes ses dépendances via ses paramètres, et dans notre cas il n'en a qu'une : le $scope.
  • Le $scope : cette variable est comparable au state d'un component dans React. C'est un objet contenant toutes les valeurs accessibles depuis la vue (et ses enfants) et uniquement depuis celle-ci. Donc quand je crée un $scope.name depuis mon contrôleur, j'injecte la même variable name au sein de ma vue. La documentation d'AngularJS à ce sujet est très complète et très instructive.

 

Côté html, il suffit simplement d'utiliser une double paire d'accolade ({{variable}}) pour afficher une variable du scope. Cette syntaxe, repéré par AngularJS lors de son initialisation, crée un one-way-data-binding. En d'autres mots, la paire d'accolade devient un simple miroir de la valeur initialisée / modifiée depuis le contrôleur. Si la manière avec laquelle AngularJS maintient cette synchronisation (nommée dirty checking) vous intéresse, je vous invite à lire cet article qui discute et compare les façons de procéder de plusieurs frameworks dont React et AngularJS. En quelques mots et comme l'indique son nom, le dirty checking est aussi simple que bête, en testant chaque variable de scope affichée (toutes ne le sont pas forcément) après chaque action (un évènement onclick, un formulaire changeant de valeur, une requête ajax ...), à la recherche des différences.

 

Si je vous parle du one-way-data-binding, c'est qu'il existe un two-way-data-binding : c'est ainsi qu'on crée un lien bidirectionnel entre une variable du scope et le champs d'un formulaire HTML. 

A l'inverse de React, plus besoin donc de dispatch la nouvelle valeur lors d'un évènement onChange, la synchronisation est maintenue "toute seule". 

Sans plus attendre, voici donc la simplissime implémentation de la TODOLIST cette fois avec AngularJS B|https://embed.plnkr.co/tDRCx4N5BjO6ecfs7A3z/

Au niveau de l'index, on peut voir que notre vue est maintenant une simple balise fermante avec un attribut ng-include. C'était uniquement pour vous montrer qu'on peut bien sûr découper son template en plusieurs fichiers pour les inclure dans un deuxième temps. A noter que ce ng-include a toutefois un coût : le fichier en question est chargé depuis le client via une requête AJAX.

Jetons maintenant un oeil au todolist.html, on peut y voir trois nouveaux attributs : 

  • ng-submit : équivalent du onSubmit chez React, permet d'appeler une méthode lors de l'envoi du formulaire
  • ng-repeat : très utile, permet de répéter le block en question autant de fois que le tableau indiqué en paramètre a de valeurs, chaque nouveau block accédant à sa valeur via le nom placé avant le in
  • ng-model : un autre trésor d'AngularJS aussi utile que ng-repeat, c'est le fameux attribut permettant la synchronisation entre le formulaire et la variable renseignée en paramètre. À noter aussi que cette variable est attribut d'un objet, c'est très important et permet d'éviter tout problème de synchronisation dans ce cadre-là. Une petite recherche google vous renseignera sûrement sur les raisons derrière ce petit 'tricks'. 

 

En fait, l'essentiel est déjà là, comme vous le comprendrez en voyant le contrôleur. Celui-ci se contente d'initialiser les variables todolist / form.todo, puis de déclarer son callback permettant l'ajout d'une entrée avec la réinitialisation du champs texte. Magique, n'est-ce pas ? 

Il y aurait de nombreuses (presque trop) autres notions à vous présenter au sujet d'AngularJS mais je ne m'y avancerai pas pour les raisons évoquées au début de cette partie. Si vous voulez aller plus loin avec AngularJS (ce que je peux complètement comprendre, c'est quand même très sexy :$), je vous recommande de commencer par voir ce que sont les services, une des forces principales qui m'ont le plus plu au début (et c'est toujours le cas) ; une démonstration de l'injection de dépendances dans toute sa splendeur.

Le tutoriel disponible sur le SDZ est par ailleurs et comme souvent un très bon début. 

 

 

7/ Le packaging

À travers les deux dernières parties sur des frameworks front-end, nous avons pu constater une chose : c'est un univers composé de multiples dépendances. 

Au final, tout comme le développement d'un software quelque soit le langage utilisé, sauf si votre application est une calculatrice en ligne de commande. Et bien souvent, un software peut s'installer voir s'utiliser directement via un unique exécutable. Qu'en est-il de notre application ? Nous n'allons tout de même pas inclure des dizaines de fichiers JS différents dans notre fichier html ? Ça marcherait, mais ce n'est sûrement pas la meilleure solution, ne serait-ce que pour le maintien de ces dépendances. 

Non, a la place, plusieurs outils permettent une gestion poussée de la compilation de tous vos fichiers en un bundle, ce fichier final et unique qu'il suffira de joindre à votre .html pour lancer l'application. Parmi les outils (taskrunners, packagers ou encore bundlers) les plus complets et avancés, on peut citer webpack ou gulp : tous deux permettent la concaténation de tous vos fichiers, leur minification, optimisation, lint-age (lint est une opération de débuggage de base) ... Il est aussi possible d'ajouter des traitements personnalisés, comme par exemple la précompilation de certaines extensions précises, comme pour des feuilles de style .sass.

 

Personnellement, je n'ai jamais eu envie de m'intéresser à ce genre d'usine à gaz (malgré tout très performante, utile et utilisée) et leur configuration syntaxiquement bien trop verbeuse à mon goût. A la place, je vous propose d'écrire vos propres tâches npm de bundling. Les seuls pré-requis, c'est donc d'être sur un projet npm et d'installer les quelques modules nécessaires.

Sans perdre de temps, voilà quelques tâches opérant un premier bundling de base :

"bundle": "browserify ./entrypoint.js -o ./public/bundle.js",
"bundle:watch": "npm run bundle && watchify ./entrypoint.js -o ./public/bundle.js -v",
"bundle:prod": "browserify ./entrypoint.js | uglifyjs -mc > ./public/bundle.min.js"

Ces lignes sont à placer avant la fin de l'objet "scripts", dans votre package.json. Chaque script s'utilise en ligne de commande dans le répertoire du package.json ; npm run bundle/bundle:watch/...

Nous utilisons ici : 

  • browserify : cet utilitaire parcourt chaque fichier de votre application en commençant par le point d'entrée indiqué en premier paramètre, puis en suivant récursivement toutes ses inclusions de fichiers (instructions require)
  • watchify : celui-là, il surveille tous vos fichiers et appelle browserify à chacune de leur modification. Très pratique pour la période de développement 
  • uglifyjs : Un incontournable une fois la phase de production arrivée, uglifyjs minifie votre code et renomme notamment tous vos symboles (variables / classes ...)

 

Ne pouvant coder JS sans utiliser de feature Ecmascript récent, j'utilise aussi systématiquement babel, un transpileur Javascript :$. Son travail est tout simplement de réécrire chaque utilisation de syntaxe récente par un équivalent forcément portable. Pour lier babel au parcourt récursif de browserify, il nous faut aussi installer babelify

Nous pouvons alors dire à browserify de systématiquement appliquer babelify avec ces quelques lignes à ajouter dans votre package.json : 

  "browserify": {
    "transform": ["babelify"]
  }

Enfin, il faut indiquer à babel quels plugins nous voulons appliquer aux fichiers. En effet, beaucoup de nouvelles syntaxes sont apparues avec les dernières versions JS et chaque plugin babel n'en implémente que quelques unes. Chacun de ces plugins est un module node à installer, pour finalement les déclarer dans un fichier .babelrc à placer aux côtés du fichier package.json : 

{
	"presets": ["es2017", "es2015"],
	"plugins": ["transform-object-rest-spread"]
}

Ici, nous ajoutons la destructuration d'objet, ainsi que tous les plugins contenus dans les preset es2017 et es2015. Regardez sur ces pages babel pour installer les modules correspondant si vous les désirez. 

A noter que suivant votre environnement, toutes ces features JS peuvent ou non être disponibles. Sur un navigateur et à l'heure actuelle, babel est indispensable. Côté backend, avec node, ce site indique la portabilité de chaque feature sur les différentes versions de node. 

Une fois votre bundle généré, il suffit alors de l'inclure dans votre petite page .html servie par votre backend. 

 

8/ Le Backend

 

Bon, nous avons commencé ce topic par parler un peu de l'intérêt de NodeJS comme base pour développer une API web, avant de rapidement virer vers le front-end et toutes ses composantes. 
Je trouvais important de donner la priorité à la question NodeJS par soucis historique, et je ne suis certainement pas la bonne personne pour vous en apprendre plus sur le sujet. Cependant, il est quand même temps que nous revenions un peu dans les coulisses, s'assurer qu'on puisse bien répondre aux besoins de la scène ^_^

 

Au sein d'une stack LAMP, nous disions que l'accès aux bases de données se faisait depuis le code PHP. Ce qui est logique, puisqu'il est responsable de la bonne génération des pages webs. Mais dans notre cas, le site web est entièrement construit en Javascript ... côté client. 
Vous voyez le problème ? Fournir au monde entier les identifiants de votre BDD n'est sûrement pas la meilleure des idées, puisque le code JS en aurait obligatoirement besoin pour requêter le SGBD. 

L'idée est donc la suivante ; si on ne peut parler à notre SGBD directement depuis le front-end, on va devoir ajouter un interlocuteur : cette fameuse API pour laquelle NodeJS semble être parfaitement approprié. Ainsi, les accès à votre SGBD resteront stockés côté back-end, lequel sera interrogé par le front-end.

 

A partir de là, vous avez deux options : 

  1. Codez vous même chaque requête nécessaire avec le SGBD, chacune ayant son url d'accès définie depuis Node (n'oublions pas Express !). C'est l'option la plus rapide si vous n'avez que quelques requêtes à gérer 
  2. Tournez vous vers des API de stockage déjà existantes et reconnues, il n'en manque pas. Il vous faudra quelques jours pour bien la prendre en main, mais vous ne regretterez pas votre choix en voyant tous les bienfaits que ça vous aura apporté.

 

Effectivement, cette API vous fera oublier le SQL et ses malheurs, proposant généralement quelque chose comme un ORM pour opérer intuitivement et directement sur vos entités, les liant entre elles selon la configuration que vous aurez au préalable défini dans des fichiers, laissant à l'API le soucis de créer et formater la base de donnée, écrire correctement les requêtes de jointure ...

Vous pourrez alors complètement séparer la logique propre au SGBD de celle propre aux différentes opérations à faire sur vos données, permettant encore et toujours de pouvoir plus facilement remplacer votre SGBD, tester vos opérations, etc. Enfin, tout ce code implémentant vos différentes actions sur votre BDD ne sera pas non plus à faire si vous devez développer un autre front comme une application mobile, celle-ci n'aura qu'à requêter votre API comme le fait votre premier front.

N'étant pas vraiment mon rayon, je me contenterai de vous conseiller ces api assez connues : SailsParse.
Si le NoSQL vous intéresse, vous pouvez aussi setup votre propre API pour MongoDB

 

9/ Une SPA isomorphique

 

Vous avez maintenant eu l'aperçu d'une bonne partie d'une stack JS moderne telle qu'on peut facilement la voir aujourd'hui (quoi que, pas sûr que soit toujours vrai dans une institution vieillissante comme la SNCF ^_^) : Un backend constitué au moins d'un serveur NodeJS pouvant servir d'API mais servant surtout nos fichiers statiques (images/css/js), souvent un SGBD accompagné de son API, ainsi qu'une application front développée avec un framework comme Angular ou React, magnifiquement packagée puis servie au navigateur depuis notre NodeJS. 

Pour la petite culture, on vient presque de citer une stack assez connue : MEAN, pour MongoDB + Express + Angular + NodeJS. Express, je vous en parlais comme d'un module d'interface aux requêtes HTTP dans la partie sur Node, comme quoi c'est  bien un module incontournable avec ce dernier. 

 

Avec tout ça, vous avez quelque chose de tout à fait pro, scalable, et tutti cuenti. Je tenais simplement à vous apporter une dernière petite notion: ce sont les applications isomorphiques.

Au début de la partie sur les SPA, j'ai introduit les framework front-end comme des librairies permettant la conception d'application fonctionnant uniquement chez le client.

En fait, ce n'est pas tout à fait vrai. Ce qui est sûr, c'est qu'une fois l'application téléchargée et lancée par le navigateur client, le backend n'a plus rien à faire avec ce front-end, sauf pour les requêtes avec le SGBD. Mais au moment où le serveur reçoit la première requête du client, qu'en est-il ? Dans un cas simple, il se contente de retourner la SPA, avant que le navigateur initialise l'application et commence ses premiers traitements. Mais ne pourrait-on pas prémâcher le travail du navigateur ? 

Puisqu'on peut maintenant exécuter du code JS autant côté client que serveur, on pourrait effectivement réaliser le premier rendu du front avant même de retourner celui-ci, de sorte que le navigateur n'ai plus qu'à afficher les vues déjà insérées avec leurs données pré-injectées par le serveur. L'intérêt, c'est de gagner de précieuses ms au chargement du site, des ms qui se sentiront  notamment sur les mobiles, ainsi que pour votre SEO. Voilà donc ce qu'est qu'une application isomorphique.

Ce n'est bien sûr pas un impératif à mettre obligatoirement en place, mais c'est un plus que certains gros services ne peuvent même pas se permettre d'ignorer, et que d'autres plus petits sont bien heureux d'adopter.  

 

Dans les grandes lignes, l'implémentation d'une telle app passe en premier lieu par s'assurer que toutes les dépendances du front-end (modules comme les appels d'API propres au navigateur) sont disponibles aussi et de la même manière sur le back-end. Par exemple, tous les appels fetch (= requête ajax), une API propre aux navigateurs, nécessiteront l'utilisation d'un polyfill (implémentation d'une feature indisponible en raison d'un mauvais environnement / version) côté serveur. Si ces requêtes font appel à des services tournant sur le même serveur que celui délivrant l'application, il serait bien plus optimisé de carrément remplacer ces fetch par l'appel direct aux bonnes fonctions.

Ce problème de dépendance peut aussi s'appliquer à certains modules clés comme un module de routage, d'où l'intérêt d'autres modules prévus à cet effet, comme par exemple Universal-Router chez React.

Suivant comment votre framework stocke / gère ses données, il faudra aussi réfléchir à comment ré-injecter celles-ci dans l'application une fois qu'elle arrive chez le client. Une étape qui commencera inévitablement par la sérialisation des données dans une variable JS dynamiquement ajoutée à la page par le serveur. 

Bref, de nombreux articles existent à ce sujet pour les grands frameworks comme React ou Angular, je laisse donc les curieux se documenter eux-même.

 

 

10/ Conclusion

 

Et voilà, c'est sur cette dixième et dernière partie que je peux enfin conclure cet article (plus un article qu'un tutoriel proprement dit), après quelques soirées puis une dernière journée complète de rédaction. Me remettre derrière mon clavier pour vous apporter mon savoir comme j'en avais l'habitude il y a quelques années m'a fait vraiment plaisir, d'autant que c'est quelque chose que je voulais faire depuis quelques temps, peinant juste à trouver un point de ralliement entre l'activité du forum et mes centres d'intérêts, qui ont bien évolué depuis mes débuts ici. C'est finalement la sortie de la V6 qui m'a fait me décider, pour essayer de survoler avec vous le maximum de composantes d'un web en pleine évolution : on a vu le radical changement de rôle du Javascript, passant du langage boiteux servant à faire des animations à celui implémentant toute la logique d'une application de manière dynamique et directement chez le client, avec à son opposé un backend maintenant centré sur le stockage et le traitement de l'information en un service réutilisable depuis tout front-end. 

 

J'aurais pu faire un vrai tutoriel plus poussé sur l'un des sujets abordés ici mais je pense que ce n'était pas le plus intéressant. Quelque soit le jeux ou le forum, beaucoup de gens bossant dans l'émulation n'ont sûrement jamais eu connaissance de plusieurs des notions / technos abordées ici et c'est "dommage", parce qu'il suffit de savoir qu'une chose existe pour pouvoir exploiter tout son intérêt le bon moment venu, aussi obscure paraisse la chose jusqu'à ce moment. 

C'était pour moi aussi une bonne occasion de passer en revues et compléter mes connaissances, en malmenant mon pauvre chromium avec les je-ne-sais-combien d'onglet wikipédia et autres qui ont dû être ouverts. 

 

Je remercie les quelques courageux qui ont eu la patience de lire le sujet jusqu'ici, et n'hésitez pas à me faire des retours : si une partie n'est pas claire, mérite plus d'illustration, ou que l'une d'elle vous intéresse assez pour vouloir un tutoriel plus approfondi à ce sujet (ou sur tout autre sujet en JS).

 

Bonne soirée la Funky Family ! 

Partager ce message


Lien à poster
Partager sur d’autres sites

Ha enfin il est là !

 

Excellent tutoriel, merci à toi.

Pour ma part, je me suis lancé il y a 3 mois au NodeJS :P

Partager ce message


Lien à poster
Partager sur d’autres sites

Je ne tenais pas à le poster avant de bien le peaufiner ahah, quoi que j'ai peut-être manqué de patience sur les dernières heures :P

Sage décision eheh, bonne chance et merci à toi !

Partager ce message


Lien à poster
Partager sur d’autres sites

Pour ceux qui veulent voir ce que peut faire NodeJS rapidement, j'vous laisse ce lien https://nodered.org/

 

Partager ce message


Lien à poster
Partager sur d’autres sites

Ca par contre, c'est un roman !

Merci à toi :)

Partager ce message


Lien à poster
Partager sur d’autres sites

J'avais pas mal de choses à dire ahah, dites moi si vous préférez que je sépare en 2 topics front / back, mais la partie front sera dans tous les cas et surement pas mal plus grosse

De nada !

Partager ce message


Lien à poster
Partager sur d’autres sites

Bonjour ,

Très bon tutoriel pour ceux qui veulent apprendre, Merci à toi :)

Partager ce message


Lien à poster
Partager sur d’autres sites

Je pense plutôt deux topics ?

Partager ce message


Lien à poster
Partager sur d’autres sites

×

Information importante

By using this site, you agree to our Conditions d’utilisation.