Logomotion: Introduction Javascript
This commit is contained in:
parent
8d4a57ed9b
commit
6eaec99f06
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
|
@ -0,0 +1,628 @@
|
||||||
|
<style>
|
||||||
|
pre { font-size: 0.5em !important; }
|
||||||
|
table { font-size: 0.6em !important; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
# Présentation de framework Javascript
|
||||||
|
## William Petit - S.C.O.P. Cadoles
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Riot
|
||||||
|
## React + Flux
|
||||||
|
## Ember
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Riot
|
||||||
|
|
||||||
|
### Présentation générale
|
||||||
|
### Concepts généraux
|
||||||
|
### Exercice
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Présentation générale (1)
|
||||||
|
|
||||||
|
- Publication: Septembre 2013
|
||||||
|
- Mainteneurs principaux: [@GianlucaGuarini](https://github.com/GianlucaGuarini) et [@tipiirai](https://github.com/tipiirai) (projet communautaire)
|
||||||
|
- License: MIT
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- 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
|
||||||
|
<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
|
||||||
|
|
||||||
|
```html
|
||||||
|
<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
|
||||||
|
|
||||||
|
```html
|
||||||
|
<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
|
||||||
|
|
||||||
|
```html
|
||||||
|
<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
|
||||||
|
|
||||||
|
|
||||||
|
```html
|
||||||
|
<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
|
||||||
|
|
||||||
|
```html
|
||||||
|
<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"
|
||||||
|
```javascript
|
||||||
|
// 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
|
||||||
|
|
||||||
|
```html
|
||||||
|
<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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## React + Flux
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
// 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
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
// 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
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
// 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
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
// 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>
|
||||||
|
<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](./img/flux-simple-f8-diagram-with-client-action-1300w.png)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ember
|
||||||
|
|
||||||
|
### Présentation générale
|
||||||
|
### Concepts généraux
|
||||||
|
### Initialisation d'un projet
|
||||||
|
### Exercice
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
# Licence
|
||||||
|
|
||||||
|
## CC BY-NC-SA 3.0 FR
|
||||||
|
|
||||||
|
[Creative Commons - Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 France](https://creativecommons.org/licenses/by-nc-sa/3.0/fr/)
|
Loading…
Reference in New Issue