Les frameworks Front-end
Le Front-end est la partie visible d'un site web : ce qui est affiché à l'utilisateur.
Cela concerne bien entendu le design, mais pas seulement :
fonction appelée au clic d'un bouton, chargement temps-réel des données (AJAX), ...
De façon général, le Front-end doit gérer toutes les intéractions entre l'utilisateur
et les différents éléments de la page.
De plus en plus à la mode, les frameworks Front-end se sont multipliés depuis ces dernières années.
Parmi les raisons de ce gain de popularité, on peut en citer une :
la tendance actuelle, qui consiste à réduire le back-end au simple développement d'une
API.
Les développeurs peuvent ainsi se concentrer essentiellement sur la partie design,
et plus particulièrement sur comment construire la page à partir des données renvoyées par l'API.
Angular JS
Angular JS est un framework Javascript créé par Google. Il permet de réaliser des Single-page applications, c'est-à-dire que le site ne chargera la page qu'une seule fois au client. Par la suite, si l'utilisateur clique sur un lien par exemple, la page ne sera pas réellement rechargée, comme dans les sites classiques : il fera simplement une requête AJAX qui permettra de charger uniquement les données nécessaires à l'affichage de la page suivante. Cela permet de diminuer la charge réseau et donc à l'application d'avoir un comportement plus rapide.
Comme Symfony et Django,
Angular JS repose sur une architecture de type MVC :
Le modèle est caractérisée par les données javascript,
qui seront souvent chargées grâce à une API,
et qui seront amenées à changer avec l'intéraction de l'utilisateur.
Pour gérer cela, Angular a introduit le two-ways binding, dont on parlera plus loin.
La vue est gérée par des templates html, avec des attributs tels que
ng-repeat permettant de réaliser des opérations dépendant des données
(ici une boucle for).
Enfin, le contrôleur est simplement une fonction javascript qui se charge de
récupérer les données qui seront utilisées par la vue,
et qui indique comment ces données sont modifiées dynamiquement
(À la validation d'un formulaire par exemple).
Un fichier de configuration permet alors de relier une URL à un template et un contrôleur associé.
Le two-ways binding
La grosse fonctionnalité qui fait l'intérêt de Angular JS est le two-ways binding.
Il s'agit d'un système qui permet de synchroniser automatiquement les données du contrôlleur
avec ce qui est affiché à l'utilisateur.
Comme son nom l'indique, cette synchronisation se fait dans les 2 sens :
- Si on modifie les données du contrôleur, la vue affichée à l'utilisateur sera actualisée automatiquement.
- Si l'utilisateur intéragit avec la page (par exemple s'il remplit un champ d'un formulaire), les variables du contrôleur associées seront modifiées automatiquement.
Un exemple sera plus parlant : supposons que l'on veut réaliser une calculatrice.
Vue : calculatrice.html
<h1>Calculatrice</h1>
<p>
x1 : <input type="number" ng-model="x1" /><br />
x2 : <input type="number" ng-model="x2" />
</p>
<p>x1 + x2 = {{ resultat() }}</p>
Contrôleur : calculatrice.js
app.controller('CalcController', ['$scope', function($scope) {
// Valeurs par défaut
$scope.x1 = 2;
$scope.x2 = 3;
// Affichage du résultat
$scope.resultat = function() {
return $scope.x1 + $scope.x2;
};
}]);
Résultat :
Calculatrice
x1 :
x2 :
x1 + x2 = 5
Dans cet exemple, on voit bien la synchronisation dans les deux sens des variables x1 et x2 :
- À l'initialisation lorsque l'on donne une valeur par défaut aux variables dans le contrôleur, le texte de l'input est automatiquement mis à jour.
- Lorsque l'utilisateur modifie le texte de l'input, les variables du contrôleur sont directement mise à jour. De plus, Angular détecte qu'il faut appeler à nouveau la fonction resultat() pour afficher la nouvelle somme. Sans framework, on aurait dû associer un événement à l'input du type "lorsque la valeur a changé, appelle la fonction résultat", ce qui peut vite devenir lourd lorsque l'on commence à avoir beaucoup d'éléments qui intéragissent entre eux.
Les directives
Les directives sont un autre concept clé de Angular. Il consiste à pouvoir créer ses propres balises et attributs HTML. Cela permet de créer des composants réutilisables, qui s'incluront alors en une ligne de code dans le fichier HTML. Concrètement, une directive est un fichier JS contenant le nom de la balise, ou de l'attribut, un template HTML, et un contrôleur. Lorsqu'on utilise cette balise ou cet attribut dans le HTML, le template HTML sera affiché, et le contrôleur permet de rendre l'élément dynamque.
Un exemple de directive : un système d'autocomplétion. On rentre un texte dans une zone de texte, et on désire afficher en temps réel des suggestions de résultat. On définit le fichier de template, qui contient la zone de texte et la liste des résultats, et le fichier directive, qui réalise le filtrage des résultats et l'autocomplétion à la sélection de celui-ci.
Template : autocomplete.html
<input type="text" ng-model="value" /><!-- Zone de recherche -->
<!-- Affichage des suggestions -->
<ul>
<li ng-repeat="result in results" ng-show="match(value,result)" ng-click="select(result)">{{ result }}</li>
</ul>
Directive : autocomplete.js
app.directive('autocomplete', function() {
return {
scope: { // Attributs HTML
results: '='
},
controller: function ($scope) {
// Vérifie si le texte entré match une suggestion de résultat
$scope.match = function(value,result) {
return (result.indexOf(value) != -1);
};
// Sélection d'une seggestion de résultat
$scope.select = function(result) {
$scope.value = result;
};
},
templateUrl: 'autocomplete.html'
};
});
Une fois la directive définie, il suffit d'insérer ce code dans une vue pour intégrer le système d'autocomplétion :
<autocomplete results="myResults"></autocomplete>
Où myResults serait un simple tableau défini dans un contrôleur.
Résultat :
Angular permet ainsi réaliser simplement des composants réutilisables.
React JS
React JS est un autre framework Front-end créé récemment par Facebook.
Ce framework est orienté composants, comme nous allons le détailler dans la suite.
React est basé sur ES 6 qui est une forme « évoluée » de Javascript.
Il contient des spécifications supplémentaires qui permettent des éléments de syntaxe
plus pratiques :
- Introduction de la programmation orientée objet
-
Support des paramètres par défaut dans une fonction :
var link = function(height = 50, color = 'red', url = 'http://azat.co')
-
Supprt des arrow functions,
qui permettent de définir des callbacks de manière beaucoup plus légère
qu'habituellement.
Ainsi, là où avant on était amené à écrire quelque chose comme ça :
Avec les arrow functions, la syntaxe est plus simple :$("button").click(function(e) { doStuff(); });
$("button").click(e => doStuff());
- Ainsi que d'autres features du même type : chaînes de caractères multilignes, Promises... Pour plus d'informations, rendez-vous sur ce site.
Un compilateur (babel.js) permet alors de convertir le code ES6 en code JS standard.
React a également introduit un autre élément de syntaxe : le JSX.
Il permet d'insérer des balises HTML au milieu du code JavaScript.
On sera ainsi amené à écrire du code de ce genre :
function getGreetings(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
Les composants
Le composant est le concept clé de React.
Un composant est un fragment de HTML généré en Javascript.
Un peu à la même manière que les directives de Angular,
il va permettre de créer ses propres balises HTML,
qu'il sera alors très facile d'intégrer à la page.
Concrètement, un composant est une fonction javascript
quit prend en argument un objet JS contenant les attributs de la balise.
Cette fonction va renvoyer le code HTML à générer avec la syntaxe JSX.
Exemple : supposons que l'on veuille créer un composant Commentaire.
Le code pourrait ressembler à quelque chose comme ceci :
function Commentaire(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar" src={props.author.avatarUrl} />
<div className="UserInfo-name">{props.author.name}</div>
</div>
<div className="Comment-text">{props.text}</div>
<div className="Comment-date">{formatDate(props.date)}</div>
</div>
);
}
On peut également déclarer un composant comme une classe Javascript, qui hérite alors de la classe React.Component, et renvoie le code dans une méthode, render(). Le code est légèrement plus lourd mais il permet de faire des choses plus évoluées, comme la gestion d'états que l'on verra plus tard.
class Commentaire extends React.Component {
render() {
return (
<div className="Comment">
[...]
</div>
);
}
}
Le composant s'utilisera alors très simplement, comme ceci :
const comment = {
"date": new Date(),
"text": "J'ai découvert un nouveau framework, il est génial !",
"author": {
"name": "Timothé",
"avatarUrl": "http://www.example.com/images/tmalahie.jpg"
}
};
ReactDOM.render(
<Comment date={comment.date} text={comment.text} author={comment.author} />,
document.getElementById('root')
);
On voit que la création et l'utilisation d'un composant est assez simple.
Et c'est là tout l'intérêt de React : être basé un maximum sur les composants.
En React, le code HTML de la page sera essentiellement constitué de composants
imbriqués les uns dans les autres, chaque composant ayant une fonction précise.
Ainsi, le code précédent pourrait par exemple être réduit :
function Commentaire(props) {
return (
<div className="Comment">
<Avatar user={props.author} />
<div className="Comment-text">{props.text}</div>
<div className="Comment-date">{formatDate(props.date)}</div>
</div>
);
}
Où Avatar serait un autre composant qu'il faudrait créer. Le concept de composant a plusieurs avantages :
- Factorisation : une fois un composant créé, on va pouvoir l'inclure n'importe où dans le site en une ligne de code.
- Réutilisabilité : on va pouvoir créer des templates de composants qui pourront alors être utilisé sur n'importe quel projet.
- Clarté du code : la simple lecture du nom de la balise permet de savoir à quoi elle sert.
États et one-way data-flow
De même qu'avec Angular, on a une sorte de data-binding permettant de faire la synchronisation des données javascript avec ce qui est affiché à l'utilisateur. Cependant, ce data-binding est géré de manière un peu plus subtile :
- Il n'est réalisé que dans un sens : des données vers la vue. Pour l'autre sens, on utilise des événements comme en JS "classique".
- Il passe par une nouvelle notion : les états.
L'état d'un composant est un simple objet javascript qui contient l'ensemble des propriétés
nécessaires pour décrire ce composant.
Pour modifier un état, on appelle la fonction setState,
ce qui actualisera alors automatiquement le rendu du composant
(la méthode render() sera appelée).
Exemple de code utilisant les états : reprenons la calculatrice
implémentée précédemment.
class Calculator extends React.Component {
constructor(props) {
super(props);
this.updateX1 = this.updateX1.bind(this);
this.updateX2 = this.updateX2.bind(this);
this.state = {x1: props.x1, x2: props.x2};
}
updateX1(e) {
this.setState({x1: e.target.value});
}
updateX2(e) {
this.setState({x2: e.target.value});
}
render() {
const x1 = Number(this.state.x1), x2 = Number(this.state.x2);
const result = x1+x2;
return (
<div>
<p>
x1 : <input type="number" value={x1} onChange={this.updateX1} />
x2 : <input type="text" value={x2} onChange={this.updateX2} />
</p>
<p>x1 + x2 = { result }</p>
</div>
);
}
}
ReactDOM.render(
<div>
<h1>Calculatrice</h1>
<Calculator x1="2" x2="3" />
</div>,
document.getElementById('root')
);
L'intérêt de la notion d'états est que les composants sont complètement indépendants les uns des autres. En effet, un composant donné ne peut pas accéder aux états d'un autre composant (les états sont dits encapsulés), de sorte qu'on peut aisément modifier le comportement d'un composant sans risque d'affecter les autres composants. Ce principe apporte ainsi des garanties importantes en terme de maintenabilité.
Comparaison
Vitesse de rendu
La vitesse du rendu est un facteur important pour ces deux frameworks.
En effet, de la façon dont ils sont concus, dès que l'on modifie une donnée,
on doit actualiser l'ensemble du document HTML en conséquence.
Cela peut vite se révéler lourd lorsque la page commence à contenir de nombreux éléments.
Angular comme React utilisent des techniques d'optimisation du rendu.
L'idée est que lorsque l'on modifie l'état d'un composant, on ne rafraîchit pas vraiment
l'ensemble de la page HTML : on affecte uniquement les balises HTML qui sont effectivement modifiées.
Cela peut se voir si on utilise l'inspecteur d'élément du navigateur :
On voit effectivement que seul le texte est actualisé, ce qui est bien plus rapide à traiter pour le navigateur.
Et concrètement, quel framework est le plus optimisé en terme de vitesse de rendu ?
Pour le savoir, un test de vitesse sur un grand nombre d'éléments a été réalisé sur les 2 frameworks.
On obtient le résultat suivant :
- Angular : 30 FPS
- React : 4 FPS
Angular se révèle donc meilleur en pratique sur les très grosses pages. Ceci est en partie dû à une optimisation supplémentaire d'Angular : le "Transparent scope", qui consiste à n'actualiser qu'une partie du document en cas de gros taux de rafraîchissement.
Maintenabilité
En ce qui concerne la maintenabilité, on peut noter plusieurs choses :
Organisation
Angular présente l'avantage de l'organisation du code (modèle MVC). La logique du projet est séparée en parties indépendantes, de sorte que si on est amené à reprendre un projet qui a été commencé par d'autres personnes, il sera facile de s'y retrouver.
À l'inverse, React n'a pas la notion de modèle MVC.
Ce framework est limité à la partie "vue",
ce qui peut s'avérer insuffisant en pratique sur des projets importants.
Notamment, il n'y a pas de module par défaut pour les requêtes AJAX
qui doivent donc être écrites à la main. Il n'y a pas non plus de système de routes.
Néanmoins, le principe des composants apporte un plus
en permettant une clarté du code et une indépendance entre les différents éléments de la page.
Réutilisabilité
Sur ce point, les deux frameworks sont plutôt de bons candidats. Les deux permettent de créer ses propres balises HTML, ce qui donne un moyen élégant de générer des composants qui, s'ils sont bien concus, pourront être uilisés tel quels dans n'importe quel autre projet.
Simplicité de développement
Ici, React est plutôt avantageux, le framework nécessite finalement peu de concepts nouveaux hormis celui des composants. Un autree point non négligeable ou React se révèle plus simple : le débuggage. En effet, repérer une erreur de développement en Angular peut rapidement s'avérer délicat : une fois sur deux, la console Javascript n'affichera pas les erreurs dans votre code... mais dans la librairie Angular elle-même !
React, lui, avec son système de compilation, s'arrange pour détecter au mieux les erreurs et les affiche de manière claire dans le navigateur (figure ci-contre).
Le problèmes de débuggage seront partiellement corrigé avec Angular2, qui passera également au JS compilé avec l'introduction du TypeScript. Mais Angular2 comporte de nombreuses différences par rapport à Angular 1, et ces nouvelles fonctionnalités se font au prix d'une complexité et d'abstractions supplémentaires relativement importantes.
Popularité
Comme pour le back-end, on utilise l'indice de popularité fourni par le site
hotframeworks.com.
On voit que React est un framework relativement nouveau, de sorte qu'on n'a pas encore beaucoup de données.
C'est donc un peu tôt pour tirer des conclusions, mais son score est plutôt élevé et il semble donc prometteur.
Angular, quand à lui, affiche un score très satisfaisant,
et ce framework apparaît comme un candidat de choix pour le Front-end.
Tableau récapitulatif
Angular | React | |
---|---|---|
Avantages |
|
|
Inconvénients |
|
|
Utilisé par |
En bref : Angular et React sont de bons frameworks qui feront l'affaire sur la plupart des projets à moyen voire à long terme. React est plus orienté "vue" et sera donc surtout adapté aux sites présentatifs où la partie design est dominante. Angular est plus polyvalent mais plus complexe, ce qui en fait un parfait framework pour des sites avec de nombreuses pages à gérer, ou pour des pages dynamiques impliquant avec une forte intéraction entre les éléments.