Comme beaucoup je travaille 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 ?
On peut commencer par se demander : pourquoi changer ? AngularJS est un très bon framework, c'est indéniable, mais sa conception me pose plusieurs problèmes.
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.
Je ne parlerais ici que de la partie composant du framework, car, comme son petit frère, AngularJS 2 intègre de nombreux outils supplémentaires comme un module HTTP ou un validateur de Formulaire.
Au premier abord, Angular 2 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 :
Plus d'informations sur le détecteur de changements
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 :
Inconvénients :
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 est 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 :
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 :
Inconvénients :
shouldComponentUpdate()
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 :
items[1] = 3
, il faudra alors utiliser une fonction de VueJS (vm.items.$set(1, 3)
)N'hésitez pas à faire un tour sur la documentation pour avoir une explication en profondeur de ce fonctionnement.
Avantages :
vue-cli
Inconvénients :
Cette nouvelle architecture en composant pose tout de même un problème : comment faire communiquer ensemble nos composants de manière globale. Par exemple, sur Facebook, lorsque l'on reçoit un nouveau message, une notification s'affiche à plusieurs endroits sur le site. Nos composants doivent donc partager une sorte de state global. Facebook propose une solution à cette problématique sous la forme d'un pattern : Flux. Le principe de ce pattern est le suivant.
Un store représente des données, par exemple une liste de todos
var TodosStore = {
todos: []
};
Un composant va lancer une action, par exemple createNewTodo :
createNewTodo: function( evt ) {
AppDispatcher.dispatch({
type: 'TODO_ADD',
todo: { name: 'Faire les courses' }
});
}
Le store souscrit à une série d'actions et modifie son contenu en fonction de l'action appelée
var TodosStore = {
todos: []
};
AppDispatcher.register( function( action ) {
switch( action.type ) {
case 'TODO_ADD':
TodosStore.totos.push( action.todo );
TodosStore.trigger( 'change' )
break;
}
});
Le store émet un évènement pour informer d'un changement.
Le composant souscrit à cet évènement et va se mettre à jour en fonction. La méthode de mise à jour dépendra du framework utilisé.
TodosStore.bind('change', function () {
this.setState({todos: TodosStore.todos});
});
Il n'existe cependant pas de "framework Flux", mais plusieurs librairies proposent des implémentations plus ou moins proches de l'idée originale :
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.