formations/developpement/intro_javascript/presentation/slides.md

12 KiB
Raw Blame History

Frameworks Javascript - Tour d'horizon

William Petit - S.C.O.P. Cadoles


Les frameworks

RiotJS

ReactJS

EmberJS


RiotJS

Présentation générale

Concepts généraux

Exercice


Présentation générale (1)


Présentation générale (2)

Avantages Inconvénients
Une librairie et pas un framework Une librairie et pas un framework
Projet communautaire Projet communautaire
Orientée "composant"
API minimaliste
Prêt à l'emploi (pas de transpilage nécessaire en développement)
Routeur "frontend" disponible

Concepts généraux

Composants ("tags")

Observable


Composants

Structure et utilisation d'un composant

Templating

Styles de composant

Évènements du DOM

Cycle de vie du composant


Structure d'un composant

<!-- components/my-tag.tag -->

<!-- Racine du Tag -->
<my-tag>

  <!-- Définition du DOM -->
  <span>{ opts.foo }</span>
  
  <!-- Style du composant -->
  <style>
    :scope {
      display: block
    }
  </style>
  
  <!-- Script du composant -->
  <script>
    this.on('mount', () => {
      this.opts.foo = "bar";
    });
  </script>
  
</my-tag>

Utilisation du composant

Compilation dans le navigateur

<html>
  <body>
  	
    <!-- Utilisation du composant dans notre page HTML -->
    <my-tag></my-tag>
    
    <!-- Inclusion de notre "tag" -->
    <script type="riot/tag" src="components/my-tag.tag"></script>
    
    <!-- Inclusion de la librairie Riot + compilateur -->
    <script type="text/javascript" src="js/riot+compiler.js"></script>
    
    <!-- Montage des composants Riot -->
    <script type="text/javascript">
      var opts = {}; // Options passées à tous les tags
      riot.mount('*', opts)
    </script>
    
  </body>
</html>

Templating

<my-tag>

  <!-- Interpolation simple -->
  <span>{ foo }</span> <!-- <span>bar</span> -->
  
  <!-- Boucles -->
  <ul>
    <li each={ item in myItems }>{ item.text }</li>
  </ul>
  
  <!-- Condition avec appel de méthode -->
  
  <span if={ isFooBar() }>foo est égal à "bar" !</span>
  
  <script>
    
    this.foo = "bar"
    
    this.myItems = [
    	{ text: "Hello World" },
        { text: "Foo Bar" }
    ];
    
    isFooBar() {
      return this.foo == "bar";
    }
    
  </script>
  
</my-tag>

Styles de composant

Feuille de style

Styles et classes dynamiques


Feuille de style

<my-tag>

  <!-- Définition du DOM -->
  <span id="foo">Foo</span>
  <div class="bar">Bar</div>
  
  <!-- Style du composant -->
  <style>
    :scope {
      display: block
    }
    #foo {
      color: red;
    }
    .bar {
      background: blue;
    }
  </style>
  
</my-tag>

Styles et classes dynamiques

<my-tag>

  <!-- Définition du DOM -->
  <span style={ fooStyles }>Foo</span>
  <div class={ barClasses }>Bar</div>
  
  
  <script>
    
    this.fooStyles = {
      color: "red"
    }
    
    this.barClasses = {
      "bar": true, // Classes conditionnelles
      "foo": false
    }
    
  </script>
  
</my-tag>

Évènements du DOM

<my-tag>

  <button onclick={ onButtonClick }>Click me !</button>
  
  <script>
    
    onButtonClick(evt) {
    
      // Si l'élément à l'origine se trouve dans une boucle each={ ...}, 
      // evt.item pointera vers l'élément courant
      
      console.log(evt);
      
    }
    
  </script>
  
</my-tag>

Cycle de vie du composant

<my-tag>
  
  <script>
    this.on('before-mount',   () => console.log('before-mount'));
    this.on('mount',          () => console.log('mount'));
    this.on('update',         () => console.log('update'));
    this.on('updated',        () => console.log('updated'));
    this.on('before-unmount', () => console.log('before-unmount'));
    this.on('unmount',        () => console.log('unmount'));
  </script>
  
</my-tag>

Observable (1)

Déclaration d'un "service"

// services/auth.js
class MyAuthService {

  constructor() {
    riot.observable(this)
    this.user = null;
  }
  
  login(user, password) {
   
   var form = new FormData();
   form.append('login', login);
   form.append('password', password);
   
   return fetch('/login', { method: 'POST', form: form })
     .then(res => res.json())
     .then(user => {
       this.user = user;
       this.trigger('loggedIn', user);
     })
   ;
  }
  
  isLoggedIn() {
    return this.user != null;
  }
  
}

const myAuth = new MyAuthService();

Observable (2)

Utilisation du service

<my-app></my-app>

<!-- Tag "inline" -->
<script type="riot/tag">

  <my-app>
    <button if={ !user } 
      onclick={ login }>
      Login
    </button>
    <span if={ user }>User: { user.name }</span>
  </my-app>

  <script>
    this.user = null;
    
    this.on('mount', () => {
      this.opts.auth.on('loggedIn', user => {
        this.user = user;
        this.update();
      });
    });
    
    login() {
      this.opts.auth.login();
    }
    
  </script>

</script>

<script type="text/javascript" src="services/auth.js"></script>
<script type="text/javascript">riot.mount('*', { auth: myAuth })</script>

Exercice

Implémenter une application minimaliste de gestion de tâches avec Riot. Cette application doit comprendre les fonctionnalités suivantes:

  • Ajouter une nouvelle tâche (champs texte) avec une priorité parmi les suivantes: "haute", "moyenne", "basse" et un statut: "en cours" ou "terminé"
  • Marquer une tâche comme "terminée".
  • Afficher la liste des tâches en attente, triées par priorité décroissante + tâches terminées en dernier.
  • Les tâches doivent être persistées dans le "Local Storage" du navigateur.

ReactJS

Présentation générale

Concepts généraux

Prise en main


Présentation générale (1)

  • Publication: Mars 2013
  • Mainteneur principal: Facebook
  • License: MIT

Présentation générale (2)

Avantages Inconvénients
Grande communauté, très active Changement de license puis rétropédalage en 2017
Maintenu et utilisé par Facebook en interne Nécessité de mettre en place un pipeline de transpilage
Architecture Flux Architecture Flux
Possibilité de faire des applications "isomorphiques" avec NodeJS
"React Native"
API stable
Virtual DOM
Moteurs "lightweight" compatibles

Concepts généraux

Composants

L'architecture Flux


Composants

Structure d'un composant

Props

State

Évènements du DOM

Cycle de vie d'un composant


Structure d'un composant

// components/hello-world.js

import React from 'react'

class HelloWorld extends React.Component {

  render() {
    return (
      <div className='hello-world'>
        <h1>Hello World</h1>
        <p>Welcome to React</p>
      </div>
    )
  }

}

export default HelloWorld


Props

// components/props-example.js

import React from 'react'

class PropsExample extends React.Component {

  render() {
    return (
      <span>{ this.props.text }</span>
    )
  }

}

export default PropsExample

// app.js

import React from 'react'
import ReactDOM from 'react-dom'
import PropsExample from './components/props-example'

const mountpoint = document.getElementById('props-example');

ReactDOM.render(<PropsExample text="foo bar" />, mountpoint)


State

// components/clock.js

import React from 'react'

class Clock extends React.Component {

  constructor(props) {
    // On fait appel au constructeur de la classe
    // parente
    super(props)
    
    // Initialisation du "state" du composant
    this.state = {
      time: new Date()
    }
    // On appelle la méthode tick() du composant
    // toutes les secondes
    setInterval(this.tick.bind(this), 1000);
  }
	
  // Méthode de rendu du composant
  render() {
    return (
      <div>Time: { this.state.time.toString() }</div>
    )
  }
  
  // La méthode tick() met à jour le state du composant avec
  // la date courante
  tick() {
    this.setState({ time: new Date() });
  }

}


export default Clock


Évènements du DOM

// components/counter.js
import React from 'react'

class Counter extends React.Component {

  constructor(props) {
    // On fait appel au constructeur de la classe
    // parente
    super(props)

    // Initialisation du "state" du composant
    this.state = {
      count: 0
    }
  }

  // Méthode de rendu du composant
  render() {
    return (
      <div>
      	Count: <span>{ this.state.count }</span>&nbsp;
        <button onClick={ () => this.increment() }>+1</button>
        <button onClick={ () => this.decrement() }>-1</button>
      </div>
    )
  }

  // La méthode increment() incrémente la valeur du compteur de 1
  increment() {
    this.setState(prevState => ({ count: prevState.count+1 }))
  }

  // La méthode decrement() incrémente la valeur du compteur de 1
  decrement() {
    this.setState(prevState => ({ count: prevState.count-1 }))
  }

}

export default Counter

Cycle de vie d'un composant

// components/clock-lifecycle.js
import React from 'react'

class Clock extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      time: new Date()
    }
  }

  componentDidMount() {
    console.log('mount')
    this.intervalId = setInterval(this.tick.bind(this), 1000);
  }

  componentWillUnmount() {
    console.log('unmount')
    clearInterval(this.intervalId);
  }

  render() {
    return (
      <div>Time: { this.state.time.toString() }</div>
    )
  }

  tick() {
    this.setState({ time: new Date() });
  }

}

export default Clock

L'architecture Flux

center


Exercice

Implémenter une application minimaliste de gestion de tâches avec React (sans l'architecture Flux). Cette application doit comprendre les fonctionnalités suivantes:

  • Ajouter une nouvelle tâche (champs texte) avec une priorité parmi les suivantes: "haute", "moyenne", "basse" et un statut: "en cours" ou "terminé"
  • Marquer une tâche comme "terminée".
  • Afficher la liste des tâches en attente, triées par priorité décroissante + tâches terminées en dernier.
  • Les tâches doivent être persistées dans le "Local Storage" du navigateur.

EmberJS

Présentation générale

Concepts généraux

Initialisation d'un projet

Exercice


Présentation générale (1)

  • Publication: Décembre 2011
  • Mainteneur principal: Ember Core Team (projet communautaire)
  • License: MIT

Présentation générale (2)

Avantages Inconvénients
"Clés en main" "Opiniatre"
API stable Structure MVC classique
Long-term support (1 an)
Documentation exhaustive
Ember Data

Concepts généraux

Ember CLI

Routes

Templates

Modèles

Contrôleurs

Composants


Ember CLI

Générer une nouvelle application

npm install ember-cli -g
ember new my-app
cd my-app
ember serve

Routes

Générer une nouvelle route

ember generate route foo

Templates

{{!-- Interpolation de variable --}}
{{ myVar }}

{{!-- Utilisation d'un "helper" --}}
{{helper params... }}

{{!-- Définition d'un "block" --}}
{{#block params... }}
 ...
{{/block}}

Modèles

Générer un nouveau modèle

ember generate model bar

Exercice

Implémenter une application minimaliste de gestion de tâches avec EmberJS. Cette application doit comprendre les fonctionnalités suivantes:

  • Ajouter une nouvelle tâche (champs texte) avec une priorité parmi les suivantes: "haute", "moyenne", "basse" et un statut: "en cours" ou "terminé"
  • Marquer une tâche comme "terminée".
  • Afficher la liste des tâches en attente, triées par priorité décroissante + tâches terminées en dernier.
  • Les tâches doivent être persistées dans le "Local Storage" du navigateur.

Licence

CC BY-NC-SA 3.0 FR

Creative Commons - Attribution - Pas dUtilisation Commerciale - Partage dans les Mêmes Conditions 3.0 France