Nous travaillons de plus en plus sur du JavaScript et l’utilisation d’un framework Frontend est devenue indispensable. Jusqu’à maintenant, j’utilisais AngularJS, mais, suite à quelques problèmes concernant son utilisation, je me suis dit qu’il serait intéressant d’apprendre un nouveau Framework, mais lequel choisir ?
Le problème d’AngularJS
On peut commencer par se demander : pourquoi changer ? AngularJS est un très bon framework, c’est indéniable, mais sa conception pose plusieurs problèmes.
- La conception sous forme de controllers montre rapidement ses limites. Faire communiquer les éléments ensemble force l’utilisation de controllers imbriqués ce qui devient rapidement délicat.
- La détection des changements à travers des watchers pose aussi des problèmes lorsque l’application grandit. Lorsqu’une action est effectuée au sein du framework, tous les watchers sont lancés en boucles.
- Le $scope qui a parfois un comportement inattendu (il est difficile de savoir à quel scope une propriété est attachée surtout dans les directives).
Les composants Web
Même s’il existe une très grande variété de Framework Frontend ils sont tous d’accord sur la manière d’organiser les choses : Les composants web. L’idée est de concevoir notre application sous forme de blocs indépendants réutilisables et fonctionnant de manière isolée. Les composants peuvent être considérés comme des balises HTML améliorées
<imagepicker images="images" onselect="displayImage">
Si tout le monde est d’accord sur l’approche pourquoi existe-t-il autant de Frameworks ? La problématique reste toujours la même : on souhaite rendre nos composants réactifs. Lorsque l’état d’un composant change, l’interface de ce dernier doit changer. C’est sur la mise en place de cette réactivité que les Frameworks ne sont pas d’accord.
AngularJS 2
Au premier abord, AngularJS2 semble approcher le problème de la même manière qu’avant. Des watchers sont attachés à chaque composant et dès qu’une modification est faite ils vérifient si des modifications ont été apportées. Plusieurs changements permettent cependant d’améliorer les performances de cette méthode :
- AngularJS2 n’implémente pas le « two way data-binding » par défaut. Lorsqu’une modification est apportée il est donc inutile de faire plusieurs cycles, on vérifie les changements du composant racine vers les composants enfants une seule fois.
- AngularJS 2 supporte les objets immutables. Si un composant ne dépend que d’objets immutables, alors aucune vérification ne sera faite lors d’une modification.
L’autre gros changement par rapport aux autres frameworks est l’utilisation du TypeScript par défaut. Ce langage apporte de nouvelles fonctionnalités au langage JavaScript telles que des annotations, le typage des variables, et les interfaces. Même s’il est tout à fait possible d’utiliser du JavaScript ES5, le framework incite fortement l’utilisation de ce nouveau langage dans ses exemples. Ce qui peut représenter un frein pour la compréhension du framework (il faudra comprendre le TypeScript avant Angular 2). Malgré tout ce choix de langue permet de rendre l’organisation du code plus propre et d’éviter certaines erreurs en amont.
Avantages :
- Un framework complet
- Le TypeScript permet une meilleure organisation du code avec les interfaces, le typage et les annotations
- Un getting started proposant une introduction simple à AngularJS2
Inconvénients :
- Une documentation pas encore complète
- Le TypeScript peut être déstabilisant et rend le framework plus complexe à appréhender
- Beaucoup de concept à comprendre avant d’être efficace
- en Béta
ReactJS
ReactJS est le framework Frontend utilisé par Facebook. Son approche du problème est assez particulière et plus « manuelle ».
var HelloMessage = React.createClass({
displayName: "HelloMessage",
render: function render() {
return React.createElement(
"div",
null,
"Hello ",
this.props.name
);
}
});
ReactDOM.render(React.createElement(HelloMessage, { name: "John" }), mountNode);
La première chose qui saute aux yeux et l’utilisation de React.createElement()
. Contrairement aux autres frameworks présentés ici, React n’interagit pas directement avec le DOM, mais travaille avec un VirtualDOM qui est une représentation JavaScript du DOM réel. Pour simplifier le travail avec ce VirtualDOM React propose d’ailleurs une syntaxe supplémentaire : le JSX. Ce VirtualDOM permet de limiter les interactions avec le DOM réel lors des évolutions de notre interface. Lorsque notre composant va changer d’état, un nouveau VirtualDOM sera généré et comparé au VirtualDOM de l’état précédent. Le résultat de cette comparaison permet à React de modifier le DOM de la manière la plus optimale possible. Cette approche offre plusieurs avantages :
- Les composants ReactJS peuvent être testés sans avoir besoin du navigateur, car nous n’interagissons pas directement avec le DOM
- Le VirtualDOM peut être connecté à autre chose qu’un navigateur.
Maintenant comment rend-on notre composant réactif ?
var Timer = React.createClass({
getInitialState: function() {
return {secondsElapsed: 0};
},
tick: function() {
this.setState({secondsElapsed: this.state.secondsElapsed + 1});
},
componentDidMount: function() {
this.interval = setInterval(this.tick, 1000);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
render: function() {
return (
<div>Seconds Elapsed: {this.state.secondsElapsed}</div>
);
}
});
ReactDOM.render(<Timer />, mountNode);
Les composants commencent par initialiser un « state » qui est un simple objet JavaScript représentant l’état interne de notre composant. Lorsque l’on souhaite faire évoluer notre composant, il faudra modifier le state de manière explicite à l’aide de la méthode setState()
en lui passant le nouveau state de notre composant. Lorsque le state est modifié, le virtualDOM est regénéré et la différence est ensuite appliquée au DOM réel.
Même si le virtualDOM limite les interactions avec le DOM réel, il apporte aussi un gros problème de performances. À chaque modification du state, le virtualDOM doit être regénéré pour être comparé, ce qui peut rapidement devenir problématique avec une application utilisant des centaines de composants. Il faudra penser aux performances dès le début de la conception de l’application en précisant par exemple si un composant doit être recalculé. React intègre ainsi une méthode shouldComponentUpdate()
qui permet d’éviter le recalcul du VirtualDOM suivant une condition précise
shouldComponentUpdate: function(nextProps, nextState) {
return this.props.value !== nextProps.value;
}
Avantages :
- Le VirtualDOM permet de travailler avec autre chose que le DOM
- L’évolution du state se fait de manière explicite
- Une approche tout JavaScript
Inconvénients :
- Le VirtualDOM rend parfois l’interaction avec les éléments web difficiles
- La regénération du VirtualDOM à surveiller de près, ne pas hésiter à utiliser
shouldComponentUpdate()
- Plus « verbeux » que les autres frameworks
VueJS
VueJS permet de résoudre le problème de la création de composants web réactif avec une approche qui ressemble beaucoup à l’approche proposée par AngularJS :
<div id="demo">
<p>{{message}}</p>
<input v-model="message">
</div>
<script>
var demo = new Vue({
el: '#demo',
data: {
message: 'Hello Vue.js!'
}
})
</script>
Contrairement à React, VueJS est lié au DOM et utilise des attributs HTML spéciaux pour rendre le DOM réactif. On peut alors se demander l’intérêt d’avoir un framework aussi proche de la philosophie d’AngularJS. Là où VueJS se démarque, c’est sur sa manière de rendre notre HTML dynamique. Quand un objet est passé à un composant, VueJS va parcourir toutes ses propriétés et les convertir en getter/setter. Lorsque l’on modifie une valeur au sein d’un composant this.timer += 1
, VueJS va être capable de notifier directement les watchers correspondants à cette propriété. Cette approche permet d’avoir un concept proche d’AngularJS tout en évitant le problème posé par des milliers de watchers lancés en permanence. Malgré tout, cette solution n’est pas parfaite et il faut être au courant du fonctionnement interne de VueJS pour éviter de chercher pourquoi un composant ne s’actualise pas :
- On ne peut pas setter une valeur en utilisant un index de tableau
items[1] = 3
, il faudra alors utiliser une fonction de VueJS (vm.items.$set(1, 3)
) - Les propriétés ajoutées ou supprimées d’un objet ne seront pas trackées. Il faudra ici aussi utiliser la fonction $set.
Avantages :
- Un framework très simple à prendre en main
- Une documentation complète et bien écrite
- Des outils permettant de simplifier la création de projets
vue-cli
Inconvénients :
- VueJS patche tous les objets pour observer les modifications, ce qui peut avoir des effets de bords indésirables (quid des performances sur des objets énormes)
- On a besoin du DOM pour tester nos composants
Conclusion
Au final, aucun framework ne propose la solution ultime à la problématique de la réactivité. Chaque solution proposée apporte aussi son lot de problème. Au final, le choix du framework doit se faire en fonction de votre affinité envers la solution proposée. Cette affinité dépendra du développeur (quel niveau d’automatisation recherché, le nombre d’outils fournis…). La multitude de frameworks permet de combler ces différents besoins. J’espère que cet article vous aura permis d’y voir plus clair afin de faire votre choix de manière plus réfléchie.