I. Vue et NodeAtlas▲
Tout d'abord Vue et NodeAtlas sont tous les deux écrits en JavaScript et tournent avec un moteur JavaScript. Vue tourne grâce au moteur JS embarqué dans les navigateurs, NodeAtlas tourne grâce au moteur serveur JS installé avec Node.js. Oui, on parle bien ici d'un développement intégral avec HTML, CSS et JavaScript seulement.
Nous avons ici deux frameworks aux rôles complémentaires :
- Vue (vue.js) est un data-binder simple (équivalent à Angular ou Riot mais bien plus performant) dont la versatilité et la suite d'outils lui permettent de devenir un puissant système MVVM (équivalent à Angular2 ou React mais plus performant). Attention, il ne remplace pas jQuery (ou Vanilla JS) qui servent avant tout à manipuler le DOM. Vue lie les données en provenance de fichiers JavaScript au HTML de sorte qu'une modification des données se reflète directement dans le HTML sans aucune manipulation de votre part. Là où faire ce travail avec jQuery demanderait de trouver une liste, de récupérer un item template, d'insérer la nouvelle donnée dans l'item template, d'insérer l'item template à la fin de la liste, de trouver le compteur qui compte les lignes, de l'incrémenter de 1, etc. ; il suffit avec Vue de simplement ajouter une donnée dans le tableau JavaScript lié et tout est « recalculé ».
- NodeAtlas (node-atlas.js) est un serveur HTTP simple dans sa forme la plus basique (équivalent à Express) par référencement de route dont le point commun avec Vue est l'évolutivité et la versatilité. Cela signifie que l'on peut faire tourner des sites multilingues performants avec un nombre conséquent de pages uniquement avec une partie route et vue active (parfait pour débuter en Node.js). Les parties modèle et contrôleur sont à activer au besoin (parfait pour les experts). Il suit une architecture MVC dans sa pleine utilisation (équivalent d'un Sails.js) avec des contrôleurs dédiés ou une architecture MVC2 avec un contrôleur commun (ou les deux, ou aucun) et permet de créer des sites orientés composants si souhaité et des architectures orientées service (Site Front simple + Collection d'API distantes + Serveur d'authentification + …).
II. NodeAtlas - Sans MVVM, les bons vieux sites habituels▲
Lançons-nous dans une petite page HTML sans prétention que vous auriez faite dans les règles de l'art avec tout ce qui va bien. Ici nous allons rester minimalistes, le but du tutoriel étant de comprendre et de résoudre le problème de référencement.
Avec NodeAtlas, créons-nous une page qui liste des actions futures à entreprendre. Nous allons faire cela en utilisant une vue dans le dossier views et en utilisant une variation dans le dossier variations comme source de données.
Ce qu'il faut retenir c'est que l'injection des données dans le HTML va se faire « côté serveur ». La réponse HTTP contient donc les données pour l'indexeur de contenus. C'est typiquement le cas avec n'importe quelle techno serveur (PHP, Ruby, C#, etc.).
Nous avons donc l'architecture NodeAtlas suivante :
2.
3.
4.
5.
├─ variations/
│ └─ data.json
├─ views/
│ └─ show.htm
└─ webconfig.json
avec le contenu des fichiers suivants :
webconfig.json
Nous créons une page a-faire composée du HTML de show.htm et des données de data.json.
2.
3.
4.
5.
6.
7.
8.
{
"routes"
:
{
"/a-faire"
:
{
"view"
:
"show.htm"
,
"variation"
:
"data.json"
}
}
}
variations/data.json
Nous ajoutons trois entrées dans la variation spécifique derrière la propriété todos.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
{
"todos"
:
[{
"title"
:
"v1.0"
,
"description"
:
"Il va falloir faire la v1.0 !"
},
{
"title"
:
"v2.0"
,
"description"
:
"Puis faudra faire la v2.0, parce que la v1.0 on la sent déjà pas."
},
{
"title"
:
"v3.0"
,
"description"
:
"Il faudra faire la v3.0 parce que une fois la v2.0 finie, on voudra encore changer ce qui va pas !"
}]
}
views/show.htm
Ici on alimente notre HTML avec les données en provenance du fichier de variation en utilisant le moteur de template de NodeAtlas.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
<!
DOCTYPE html
>
<
html lang
=
"
fr
"
>
<
head>
<
meta charset
=
"
utf-8
"
>
<
title>
SSR<
/title
>
<
/head
>
<
body>
<
div class
=
"
todo-list
"
>
<
h1>
Dans le futur<
/h1
>
<?
if
(specific.
todos.
length) {
?>
<
ul>
<?
for
(var
i =
0
;
i <
specific.
todos.
length;
i++
) {
?>
<
li><strong>
<?
-
specific.
todos[
i].
title ?>
<
/strong
>
: <?
-
specific.
todos[
i].
description ?>
<
/li
>
<?
}
?>
<
/ul
>
<?
}
?>
<
/div
>
<
/body
>
<
/html
>
Maintenant, lançons notre instance serveur NodeAtlas que l'on va afficher en français avec la commande suivante :
\> node-atlas --browse a-faire --lang fr-fr
Notre navigateur par défaut s'ouvre à l'adresse http://localhost/a-faire et le code source, celui mangé par les indexeurs, ressemblera à ceci :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
<!
DOCTYPE html
>
<html lang
=
"fr"
>
<head>
<meta charset
=
"utf-8"
>
<title>SSR</title>
</head>
<body>
<div class
=
"todo-list"
>
<h1>Dans le futur</h1>
<ul>
<li><strong>v1.0</strong>
: Il va falloir faire la v1.0 !</li>
<li><strong>v2.0</strong>
: Puis faudra faire la v2.0, parce que la v1.0 on la sent déjà pas.</li>
<li><strong>v3.0</strong>
: Il faudra faire la v3.0 parce que une fois la v2.0 finie, on voudra encore changer ce qui va pas !</li>
</ul>
</div>
</body>
</html>
Jusque-là tout va bien, tout est indexable. C'est assez simple étant donné que nous n'avons pas besoin de manipuler les données depuis le navigateur, nous n'avons donc pas besoin d'un système MVVM.
Vous pouvez retrouver l'intégralité des fichiers de cet exemple dans le dépôt VueAtlas partie step/step1.
III. Vue - Avec MVVM, l'interaction facile à mettre en place !▲
Nous allons maintenant utiliser Vue ! Cela signifie que nous allons injecter les données dans le HTML côté client pour permettre à Vue de savoir quelles données sont liées à quelles parties du HTML : cela va nous permettre d'ajouter ou de retirer des entrées simplement ! Pour réaliser cela, nous allons ajouter un fichier assets/javascripts/todo-list.js accessible côté client et créer une nouvelle page basée sur views/update.htm.
2.
3.
4.
5.
6.
7.
8.
9.
├─ assets/
│ └─ javascripts/
│ └─ todo-list.js
├─ variations/
│ └─ data.json
├─ views/
│ ├─ update.htm
│ └─ show.htm
└─ webconfig.json
En ce qui concerne variations/data.json, rien ne va bouger, il va servir de source de données aussi bien pour les pages views/show.htm que views/update.htm.
webconfig.json
Ajoutons notre nouvelle page :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
{
"routes"
:
{
"/a-faire"
:
{
"view"
:
"show.htm"
,
"variation"
:
"data.json"
},
"/gerer-a-faire"
:
{
"view"
:
"update.htm"
,
"variation"
:
"data.json"
}
}
}
views/update.htm
Nous allons maintenant :
- changer l'implémentation en remplaçant les tags NodeAtlas par les tags Vue. Ils ne seront donc pas touchés lors de l'analyse du rendu côté serveur et le code sera envoyé côté client tel quel ;
- glisser les données en provenance de variations/data.json dans un attribut data-model sur la balise todo-list afin de pouvoir alimenter notre vue quand elle s'initialisera dans le navigateur côté client ;
- ajouter une partie destinée à ajouter ou supprimer des entrées.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
<!
DOCTYPE html
>
<
html lang
=
"
fr
"
>
<
head>
<
meta charset
=
"
utf-8
"
>
<
title>
SSR<
/title
>
<
/head
>
<
body>
<
div class
=
"
todo-list
"
data-model
=
"
<?
=
JSON.
stringify(specific.
todos) ?>
"
>
<
div class
=
"
todo-list--content
"
>
<
h1>
Dans le futur<
/h1
>
<
ul v-if
=
"
todos.length
"
>
<
li v-for
=
"
todo in todos
"
>
<
strong>
{{
todo.
title }}
<
/strong
>
: {{
todo.
description }}
<
span v-on:click
=
"
removeTodo(index)
"
>
[X]<
/span
>
<
/li
>
<
/ul
>
<
/div
>
<
div class
=
"
todo-list--form
"
>
Nouveau:
<
input v-model
=
"
newTitle
"
placeholder
=
"
Titre
"
>
<
input v-model
=
"
newDescription
"
placeholder
=
"
Description
"
>
<
button v-on:click
=
"
addTodo
"
>
Ajouter<
/button
>
<
/div
>
<
/div
>
<script src
=
"
https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.min.js
"
></
script>
<script src
=
"
javascripts/todo-list.js
"
></
script>
<
/body
>
<
/html
>
assets/javascripts/todo-list.js
Nous créons donc la partie modèle de Vue. Nous allons l'associer à l'élément <div class
=
"todo-list"
>
. Pour cela nous allons le chercher dans le DOM avec la variable todosSource. Ensuite nous allons alimenter notre new Vue
(
) avec l'élément en question pour el, avec les données en provenance de data-model pour data.todos. Nous créons ensuite tout ce qu'il faut pour ajouter ou supprimer des entrées.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
var todosSource =
document
.getElementsByClassName
(
"todo-list"
)[
0
];
new Vue
({
el
:
todosSource,
data
:
{
"todos"
:
JSON.parse
(
todosSource.getAttribute
(
"data-model"
)),
"newTitle"
:
""
,
"newDescription"
:
""
},
methods
:
{
addTodo
:
function (
) {
this.
todos.push
({
"title"
:
this.
newTitle,
"description"
:
this.
newDescription
}
);
this.
newTitle =
""
;
this.
newDescription =
""
;
},
removeTodo
:
function (
index) {
this.
todos.splice
(
index,
1
);
}
}
}
);
En coupant l'instance précédente (Ctrl + c) et en lançant notre instance serveur NodeAtlas avec la commande suivante (NodeAtlas est maintenant déjà en français) :
\> node-atlas --browse gerer-a-faire
L'adresse http://localhost/gerer-a-faire s'ouvre de nouveau dans le navigateur et le code source ressemblera à ceci :
http://localhost/gerer-a-faire
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
<!
DOCTYPE html
>
<
html lang
=
"
fr
"
>
<
head>
<
meta charset
=
"
utf-8
"
>
<
title>
SSR<
/title
>
<
/head
>
<
body>
<
div class
=
"
todo-list
"
data-model
=
"
[{"title":"v1.0","description":"Il va falloir faire la v1.0 !"},{"title":"v2.0","description":"Puis faudra faire la v2.0, parce que la v1.0 on la sent déjà pas."},{"title":"v3.0","description":"Il faudra faire la v3.0 parce que une fois la v2.0 finie, on voudra encore changer ce qui va pas !"}]
"
>
<
h1>
Dans le futur<
/h1
>
<
div class
=
"
todo-list--content
"
>
<
ul v-if
=
"
todos.length
"
>
<
li v-for
=
"
(todo, index) in todos
"
>
<
strong>
{{
todo.
title }}
<
/strong
>
: {{
todo.
description }}
<
span v-on:click
=
"
removeTodo(index)
"
>
[X]<
/span
>
<
/li
>
<
/ul
>
<
/div
>
<
div class
=
"
todo-list--form
"
>
Nouveau:
<
input v-model
=
"
newTitle
"
placeholder
=
"
Titre
"
>
<
input v-model
=
"
newDescription
"
placeholder
=
"
Description
"
>
<
button v-on:click
=
"
addTodo
"
>
Ajouter<
/button
>
<
/div
>
<
/div
>
<script src
=
"
https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.min.js
"
></
script>
<script src
=
"
javascripts/todo-list.js
"
></
script>
<
/body
>
<
/html
>
C'est là que les bactéries attaquent ! Le code source de notre page est (presque) équivalent à ce que nous avons pu voir côté serveur. Car le code ici écrit est fait pour être rendu par le moteur de Vue côté client, et non par le moteur de NodeAtlas côté serveur. Cela ne ressemble donc à rien pour les indexeurs de contenus. Il y a bien l'attribut HTML « data-model » dont nous nous servons pour alimenter Vue qui est dans la source, mais rien d'exploitable…
Une solution coûteuse en temps est donc de délivrer le contenu statique sur une page pour les indexeurs (ex : a-faire), et de permettre aux utilisateurs d'ajouter des éléments depuis une autre page (ex : gerer-a-faire). Une solution coûteuse en temps donc puisque l'implémentation de la page est à écrire deux fois.
Vous pouvez retrouver l'intégralité des fichiers de cet exemple dans le dépôt VueAtlas partie step/step2.
IV. Vue + NodeAtlas - Avec SSR, les avantages des deux mondes !▲
Nous allons maintenant résoudre le problème en permettant aux fichiers Vue d'être exécutés côté serveur. Pour permettre cela à Vue, il va falloir dans un premier temps rendre accessible Vue côté serveur et utiliser en plus Vue Server Renderer en utilisant NPM :
Installons cela avec les commandes suivantes dans la console de notre OS.
\> npm install vue
et
\> npm install vue-server-renderer
Nous allons également mettre nos données sources dans un dossier data, car les variations de NodeAtlas ne sont normalement pas faites pour cela.
data/todo-list.json
2.
3.
4.
5.
6.
7.
8.
9.
10.
[{
"title"
:
"v1.0"
,
"description"
:
"Il va falloir faire la v1.0 !"
},
{
"title"
:
"v2.0"
,
"description"
:
"Puis faudra faire la v2.0, parce que la v1.0 on la sent déjà pas."
},
{
"title"
:
"v3.0"
,
"description"
:
"Il faudra faire la v3.0 parce que une fois la v2.0 finie, on voudra encore changer ce qui va pas !"
}]
Ce qui nous donne, avec les fichiers rémanents des deux exemples précédents l'arborescence suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
├─ node_modules/
│ ├─ vue/
│ └─ vue-server-renderer/
├─ assets/
│ └─ javascripts/
│ └─ todo-list.js
├─ data/
│ └─ todo-list.json
├─ variations/
│ └─ data.json
├─ views/
│ ├─ update.htm
│ └─ show.htm
└─ webconfig.json
Pour que Vue puisse s'exécuter des deux côtés, nous allons utiliser le contrôleur all.js en plus du côté client avec la vue NodeAtlas all.htm. Il faut également que le vue-modèle de Vue soit disponible sur le serveur et le client. Nous allons le faire en abonnant les fichiers nécessaires à la liste des fichiers statiques de NodeAtlas et en ajoutant une nouvelle route /.
webconfig.json
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
{
"statics"
:
{
"/view-model"
:
"views/partials"
,
"/data"
:
"/data"
},
"routes"
:
{
"/"
:
{
"view"
:
"all.htm"
,
"controller"
:
"all.js"
},
"/a-faire"
:
{
"view"
:
"show.htm"
,
"variation"
:
"data.json"
},
"/gerer-a-faire"
:
{
"view"
:
"update.htm"
,
"variation"
:
"data.json"
}
}
}
Maintenant, depuis le navigateur, nous aurons accès aux fichiers :
- Données : http://localhost/data/todo-list.json (accessible sur le serveur via data/todo-list.json) ;
- Modèle : http://localhost/view-model/todo-list.js (accessible sur le serveur via views/partials/todo-list.js) ;
- Vue : http://localhost/view-model/todo-list.htm (accessible sur le serveur via views/partials/todo-list.htm).
Les contenus de views/partials/todo-list.js et views/partials/todo-list.htm sont les suivants :
views/partials/todo-list.htm
Avec v-if="client" permettant de piloter ce qui ne doit apparaître que lors du rendu client (et donc ne pas être dans la source de la réponse HTTP).
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
<
div class
=
"
todo-list
"
>
<
h1>
Dans le futur<
/h1
>
<
div class
=
"
todo-list--content
"
>
<
ul v-if
=
"
todos.length
"
>
<
li v-for
=
"
(todo, index) in todos
"
>
<
strong>
{{
todo.
title }}
<
/strong
>
: {{
todo.
description }}
<
span v-if
=
"
client
"
v-on:click
=
"
removeTodo(index)
"
>
[X]<
/span
>
<
/li
>
<
/ul
>
<
/div
>
<
div v-if
=
"
client
"
class
=
"
todo-list--form
"
>
Nouveau:
<
input v-model
=
"
newTitle
"
placeholder
=
"
Titre
"
>
<
input v-model
=
"
newDescription
"
placeholder
=
"
Description
"
>
<
button v-on:click
=
"
addTodo
"
>
Ajouter<
/button
>
<
/div
>
<
/div
>
views/partials/todo-list.js
Avec un code encapsulant la fonctionnalité pour lui permettre d'être exécutable côté navigateur et côté Node.js.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
(
function (
) {
var setTodos =
function (
view,
model,
client) {
return new Vue
({
template
:
view,
data
:
{
"todos"
:
model,
"newTitle"
:
""
,
"newDescription"
:
""
,
"client"
:
client
},
methods
:
{
addTodo
:
function (
) {
this.
todos.push
({
"title"
:
this.
newTitle,
"description"
:
this.
newDescription
}
);
this.
newTitle =
""
;
this.
newDescription =
""
;
},
removeTodo
:
function (
index) {
this.
todos.splice
(
index,
1
);
}
}
}
);
};
if (
typeof module !==
'undefined'
&&
module.
exports) {
module.
exports =
setTodos;
}
else {
this.
setTodos =
setTodos;
}
}
).call
(
this);
Cette vue-modèle est ensuite appelée côté client depuis / grâce à la vue NodeAtlas suivante :
views/all.htm
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
<!
DOCTYPE html
>
<html lang
=
"fr"
>
<head>
<meta charset
=
"utf-8"
>
<title>SSR</title>
</head>
<body>
<section class
=
"todo-list"
></section>
<script src
=
"https://code.jquery.com/jquery-3.1.1.min.js"
></script>
<script src
=
"https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.min.js"
></script>
<script src
=
"view-model/todo-list.js"
></script>
<script>
$.ajax
({
url
:
"data/todo-list.json"
}
).done
(
function (
model) {
$.ajax
({
url
:
"view-model/todo-list.htm"
}
).done
(
function (
view) {
var todos =
setTodos
(
view,
model,
true);
todos.
$mount
(
".todo-list"
);
}
);
}
);
</script>
</body>
</html>
et le contrôleur NodeAtlas suivant pour utiliser Vue côté serveur :
controllers/all.js
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
exports.
changeDom =
function (
next,
locals) {
var NA =
this,
Vue =
require
(
"vue"
),
ServerRenderer =
require
(
"vue-server-renderer"
),
renderer =
ServerRenderer.createRenderer
(
),
path =
NA.
modules.
path,
fs =
NA.
modules.
fs,
view =
path.join
(
NA.
serverPath,
NA.
webconfig.
viewsRelativePath,
"partials/todo-list.htm"
),
model =
path.join
(
NA.
serverPath,
NA.
webconfig.
viewsRelativePath,
"partials/todo-list.js"
),
data =
path.join
(
NA.
serverPath,
"data/todo-list.json"
);
global.
Vue =
Vue;
fs.readFile
(
view,
"utf-8"
,
function (
error,
view) {
fs.readFile
(
data,
"utf-8"
,
function (
error,
data) {
renderer.renderToString
(
require
(
model)(
view,
JSON.parse
(
data)),
function (
error,
html) {
locals.
dom =
locals.
dom.replace
(
"<section class="
todo-
list"></section>"
,
html.replace
(
'server-rendered="true"'
,
""
));
}
);
}
);
}
);
};
ce qui nous donne l'arborescence complète suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
├─ node_modules/
│ ├─ vue/
│ └─ vue-server-renderer/
├─ assets/
│ └─ javascripts/
│ └─ todo-list.js
├─ controllers/
│ └─ all.js
├─ data/
│ └─ todo-list.json
├─ variations/
│ └─ data.json
├─ views/
│ ├─ partials/
│ │ ├─ todo-list.htm
│ │ └─ todo-list.js
│ ├─ all.htm
│ ├─ update.htm
│ └─ show.htm
└─ webconfig.json
En lançant notre instance serveur NodeAtlas avec la commande suivante :
\> node-atlas --browse
Notre navigateur par défaut s'ouvre à l'adresse http://localhost/ et le code source (celui mangé par les indexeurs), ressemblera à ceci :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
<!
DOCTYPE html
>
<html lang
=
"fr"
>
<head>
<meta charset
=
"utf-8"
>
<title>SSR</title>
</head>
<body>
<div class
=
"todo-list"
>
<h1>Dans le futur</h1>
<div class
=
"todo-list--content"
>
<ul>
<li><strong>v1.0</strong>
: Il va falloir faire la v1.0 ! <!---->
</li>
<li><strong>v2.0</strong>
: Puis faudra faire la v2.0, parce que la v1.0 on la sent déjà pas. <!---->
</li>
<li><strong>v3.0</strong>
: Il faudra faire la v3.0 parce que une fois la v2.0 finie, on voudra encore changer ce qui va pas ! <!---->
</li>
</ul>
</div>
<!---->
</div>
<script src
=
"https://code.jquery.com/jquery-3.1.1.min.js"
></script>
<script src
=
"https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.min.js"
></script>
<script src
=
"view-model/todo-list.js"
></script>
<script>
$.ajax
({
url
:
"data/todo-list.json"
}
).done
(
function (
model) {
$.ajax
({
url
:
"view-model/todo-list.htm"
}
).done
(
function (
view) {
var todos =
setTodos
(
view,
model,
true);
todos.
$mount
(
".todo-list"
);
}
);
}
);
</script>
</body>
</html>
Bingo !
-
Vue est fonctionnel côté serveur :
- votre document est valide W3C ;
- votre document est complètement SEO indexable (avec les formulaires retirés de la source).
- Vue est fonctionnel côté client (avec les formulaires inclus dans le DOM de ce côté).
- Vous n'avez écrit qu'une seule fois le code pour le rendu client et serveur.
Vous pouvez retrouver l'intégralité des fichiers de cet exemple dans le dépôt VueAtlas partie step/step3.
V. Bonus - Enregistrement côté serveur et Mise à jour temps réel▲
Ce tutoriel a atteint son objectif puisque nous avons une architecture qui démontre comment faire du rendu côté serveur avec Vue et NodeAtlas. Afin de finir ce tutoriel correctement, nous allons :
- épurer le code pour ne garder que l'exemple final ;
- enregistrer les modifications faites côté serveur pour que l'ouverture d'une page affiche toutes les entrées ;
- utiliser les Websockets de NodeAtlas pour mettre à jour la liste en temps réel dans toutes les pages déjà ouvertes !
Finalement notre arborescence va ressembler à cela :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
├─ node_modules/
│ ├─ vue/
│ └─ vue-server-renderer/
├─ assets/
│ └─ javascripts/
│ └─ index.js
├─ controllers/
│ └─ index.js
├─ data/
│ └─ todo-list.json
├─ variations/
│ ├─ edit.json
│ └─ show.json
├─ views/
│ ├─ partials/
│ │ ├─ todo-list.htm
│ │ └─ todo-list.js
│ └─ index.htm
└─ webconfig.json
Pour information, nous avons procédé, par rapport aux trois exemples précédents, aux modifications suivantes :
- controllers/all.js qui devient controllers/index.js ;
- views/all.htm qui devient views/index.htm ;
- views/show.htm et views/update.htm qui sont supprimés ;
- variations/data.json qui est supprimé ;
- variations/show.json et variations/edit.json qui sont ajoutés ;
- assets/javascripts/todo-list.js qui est supprimé ;
- assets/javascripts/index.js qui est ajouté.
Voici le contenu de chaque fichier restant après modification.
webconfig.json
Une page en lecture seule http://localhost/ et une page de modification http://localhost/editer.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
{
"statics"
:
{
"/view-model"
:
"views/partials"
,
"/data"
:
"/data"
},
"routes"
:
{
"/"
:
{
"view"
:
"index.htm"
,
"variation"
:
"show.json"
,
"controller"
:
"index.js"
},
"/editer"
:
{
"view"
:
"index.htm"
,
"variation"
:
"edit.json"
,
"controller"
:
"index.js"
}
}
}
Avec la vue NodeAtlas unique suivante :
views/index.htm
Vous remarquerez que nous avons ajouté les fichiers socket.io/socket.io.js et node-atlas/socket.io.js fournis par NodeAtlas pour faire fonctionner les échanges Websockets temps réel plus loin.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
<!
DOCTYPE html
>
<html lang
=
"fr"
>
<head>
<meta charset
=
"utf-8"
>
<title>SSR</title>
</head>
<body>
<section class
=
"todo-list"
></section>
<script src
=
"https://code.jquery.com/jquery-3.1.1.min.js"
></script>
<script src
=
"https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.min.js"
></script>
<script src
=
"socket.io/socket.io.js"
></script>
<script src
=
"node-atlas/socket.io.js"
></script>
<script src
=
"view-model/todo-list.js"
></script>
<script src
=
"javascripts/index.js"
></script>
<script>todoList
(<?-
specific.
editable ?>
);
</script>
</body>
</html>
Qui utilise les vue-modèle Vue suivantes :
views/partials/todo-list.js
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
(
function (
) {
var setTodos =
function (
view,
model,
editable,
callback) {
return new Vue
({
template
:
view,
data
:
{
"todos"
:
model,
"newTitle"
:
""
,
"newDescription"
:
""
,
"editable"
:
editable
},
methods
:
{
addTodo
:
function (
) {
this.
todos.push
({
"title"
:
this.
newTitle,
"description"
:
this.
newDescription
}
);
this.
newTitle =
""
;
this.
newDescription =
""
;
if (
callback) {
callback
(
this.
todos);
}
},
removeTodo
:
function (
index) {
this.
todos.splice
(
index,
1
);
if (
callback) {
callback
(
this.
todos);
}
}
}
}
);
};
if (
typeof module !==
'undefined'
&&
module.
exports) {
module.
exports =
setTodos;
}
else {
this.
setTodos =
setTodos;
}
}
).call
(
this);
views/partials/todo-list.htm
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
<
div class
=
"
todo-list
"
>
<
h1>
Todo list<
/h1
>
<
div class
=
"
todo-list--content
"
>
<
ul v-if
=
"
todos.length
"
>
<
li v-for
=
"
(todo, index) in todos
"
>
<
strong>
{{
todo.
title }}
<
/strong
>
: {{
todo.
description }}
<
span v-if
=
"
editable
"
v-on:click
=
"
removeTodo(index)
"
>
[X]<
/span
>
<
/li
>
<
/ul
>
<
/div
>
<
div v-if
=
"
editable
"
class
=
"
todo-list--form
"
>
Nouveau:
<
input v-model
=
"
newTitle
"
placeholder
=
"
Titre
"
>
<
input v-model
=
"
newDescription
"
placeholder
=
"
Description
"
>
<
button v-on:click
=
"
addTodo
"
>
Ajouter<
/button
>
<
/div
>
<
/div
>
Qui ne varie que de la variable specific.editable qui sert à indiquer si la page est en lecture seule ou éditable :
variations/show.json
2.
3.
{
"editable"
:
"false"
}
variations/edit.json
2.
3.
{
"editable"
:
"true"
}
Avec toujours le même jeu de données pour data/todo-list.json.
Attaquons-nous à présent aux deux fichiers qui vont permettre :
- les échanges client-serveur temps réel côté client et serveur respectivement grâce au fichier node-atlas/socket.io.js et au point d'ancrage setSockets() ainsi que,
- l'enregistrement côté serveur grâce à writeFile.
controllers/index.js
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
exports.
changeDom =
function (
next,
locals) {
var NA =
this,
Vue =
require
(
"vue"
),
renderers =
require
(
"vue-server-renderer"
),
renderer =
renderers.createRenderer
(
),
path =
NA.
modules.
path,
fs =
NA.
modules.
fs,
view =
path.join
(
NA.
serverPath,
NA.
webconfig.
viewsRelativePath,
"partials/todo-list.htm"
),
model =
path.join
(
NA.
serverPath,
NA.
webconfig.
viewsRelativePath,
"partials/todo-list.js"
),
data =
path.join
(
NA.
serverPath,
"data/todo-list.json"
);
global.
Vue =
Vue;
fs.readFile
(
view,
"utf-8"
,
function (
error,
view) {
fs.readFile
(
data,
"utf-8"
,
function (
error,
data) {
renderer.renderToString
(
require
(
model)(
view,
JSON.parse
(
data)),
function (
error,
html) {
locals.
dom =
locals.
dom.replace
(
'<section class="todo-list"></section>'
,
html.replace
(
'server-rendered="true"'
,
""
));
next
(
);
}
);
}
);
}
);
};
exports.
setSockets =
function (
) {
var NA =
this,
fs =
NA.
modules.
fs,
io =
NA.
io;
io.
sockets.on
(
"connection"
,
function (
socket) {
socket.on
(
"update-todo"
,
function (
todos) {
fs.writeFile
(
"data/todo-list.json"
,
JSON.stringify
(
todos),
function (
) {
socket.
broadcast.emit
(
"update-todo"
,
todos);
}
);
}
);
}
);
};
assets/javascripts/index.js
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
var todoList =
function (
editable) {
$.ajax
({
url
:
"data/todo-list.json"
}
).done
(
function (
model) {
$.ajax
({
url
:
"view-model/todo-list.htm"
}
).done
(
function (
view) {
var todos =
setTodos
(
view,
model,
editable,
function (
value) {
NA.
socket.emit
(
"update-todo"
,
value);
}
);
todos.
$mount
(
".todo-list"
);
NA.
socket.on
(
"update-todo"
,
function (
value) {
todos.
todos =
value;
}
);
}
);
}
);
};
Et voilà un exemple simple et fonctionnel !
En lançant la commande suivante :
node-atlas -browse
Vous ouvrirez le site sur la page en lecture seule http://localhost/.
Ouvrez donc plusieurs onglets aux pages http://localhost/ et http://localhost/editer et même dans plusieurs navigateurs ! Ensuite, modifiez la liste via une de vos pages http://localhost/editer et magie, tout est actualisé partout ! En ouvrant une nouvelle page http://localhost/ ou http://localhost/editer à partir d'ici, vous verrez les nouvelles entrées.
Vous pouvez retrouver l'intégralité des fichiers de ce tutoriel dans le dépôt VueAtlas sur Github.
VI. Remerciements▲
Nous remercions Bruno Lesieur qui nous a autorisés à publier ce tutoriel.
Nous remercions également Winjerome pour la mise au gabarit et Fabien pour la correction orthographique.