I. On s'organise !▲
Dans tous les exemples qu'on a vus dans précédent tutoriel on a placé le code JavaScript sur la page, on pourrait évidemment le mettre dans un fichier spécifique, ce qui ne changerait rien au principe. Tant qu'on n'a pas trop de code, c'est parfait, simple et léger, mais quand le code s'accumule on aurait envie de pouvoir l'organiser plus efficacement.
D'autre part on voudrait aussi des fois effectuer d'autres actions : utiliser Babel pour coder en ES6, utiliser Pug dans notre template, utiliser Sass pour notre style, minifier notre JavaScript en production…
Le développement rapide du codage côté client a vu l'apparition d'une multitude d'outils : Grunt, Gulp, Browserify, Webpack, Lint… On finit par s'y perdre ! On a des tasks runners, des bundlers, et autres… Il est grand temps qu'une normalisation voie le jour…
Je ne vais pas clarifier tout ça dans ce tutoriel, ce n'est pas le but. On va juste voir un plugin bien pratique de vue.js : vue-loader. Ce plugin permet :
- d'écrire chaque composant dans un fichier avec le template, le script et le style ;
- d'écrire le code JavaScript en ES6 ;
- d'utiliser Webpack avec simplicité ;
- d'offrir un serveur de développement avec mise à jour en temps réel des modifications ;
- de générer les fichiers de productions…
Le fondement de tout ça c'est Webpack, alors pour ceux qui ne connaissent pas je vais en parler un peu.
I-A. Webpack▲
Webpack est un module bundler (je ne trouve pas de traduction satisfaisante). Le but premier est de pouvoir organiser le code (css, images, polices, JavaScript…) en modules pour simplifier le développement. Il a tendance à détrôner Grunt et Gulp dans cette tâche.
En gros, on crée des modules interdépendants, Webpack traite tout ça pour les transformer en fichiers statiques tout prêts pour le Web :
Webpack est capable de charger à peu près n'importe quoi à partir du moment où on lui explique comment faire (en pratique il faut un loader dédié). En plus il est assez intelligent pour pouvoir séparer le résultat en plusieurs paquets pour améliorer les performances.
Le seul reproche qu'on peut faire à Webpack c'est qu'il n'est pas vraiment facile à prendre en main, mais une fois qu'on le connait bien on peut vraiment tout faire !
Dans le contexte de ce tutoriel, on va voir que le plugin vue-loader a bien préparé le terrain et que l'utilisation en est grandement facilitée. Si vous voulez vraiment vous lancer dans Webpack, il y a le tutoriel officiel qui est bien fait.
I-B. Installer vue-loader▲
On va installer un outil en ligne de commande (vue-cli) pour pouvoir ensuite utiliser vue-loader.
Mais à la base il vous faut node.js. Si vous ne l'avez pas, alors commencez par l'installer globalement, il vous servira pour bien d'autres choses !
Le gestionnaire de dépendances de node.js est npm. C'est l'équivalent de composer, mais pour JavaScript. Lui aussi il vous le faut, mais vous pouvez l'installer en même temps que node.js.
Une fois que vous avez tout ça, vous pouvez installer le plugin avec ces commandes dans votre console :
2.
3.
4.
5.
npm install -g vue-cli (
1
)
vue init webpack-simple test (
2
)
cd test (
3
)
npm install (
4
)
npm run dev (
5
)
Voyons cela de plus près :
- (1) on commence par installer le plugin avec npm ;
- (2) on initialise le plugin dans le dossier test, acceptez les valeurs par défaut ;
- (3) on se positionne dans le dossier test ;
- (4) on installe toutes les dépendances (ça peut être long, ça crée un dossier node_modules bien garni), vous devriez vous retrouver avec tout ça :
- (5) on lance le serveur de développement (en localhost:8080).
Si tout se passe bien, votre navigateur préféré s'ouvre avec ça :
Il ne vous reste plus qu'à coder votre application !
Nota : j'utilise ici le template simplifié webpack-simple, mais il existe aussi la version complète webpack avec plein de choses détaillées sur la page github.
I-C. État des lieux▲
Pour fonctionner, Webpack a besoin d'un fichier de configuration webpack.config.js. Vous en trouvez un à la racine avec ce code :
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.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
var path =
require
(
'path'
)
var webpack =
require
(
'webpack'
)
module.
exports =
{
entry
:
'./src/main.js'
,
output
:
{
path
:
path.resolve
(
__dirname,
'./dist'
),
publicPath
:
'/dist/'
,
filename
:
'build.js'
},
module
:
{
rules
:
[
{
test
:
/
\.
vue$/,
loader
:
'vue'
,
options
:
{
// vue-loader options go here
}
},
{
test
:
/
\.
js$/,
loader
:
'babel'
,
exclude
:
/
node_modules/
},
{
test
:
/
\.(
png|
jpg|
gif|
svg)$/,
loader
:
'file'
,
options
:
{
name
:
'[name].[ext]?[hash]'
}
}
]
},
resolve
:
{
alias
:
{
'vue$'
:
'vue/dist/vue'
}
},
devServer
:
{
historyApiFallback
:
true,
noInfo
:
true
},
devtool
:
'#eval-source-map'
}
if (
process.
env.
NODE_ENV ===
'production'
) {
module.
exports.
devtool =
'#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.
exports.
plugins = (
module.
exports.
plugins ||
[]
).concat
([
new webpack.DefinePlugin
({
'process.env'
:
{
NODE_ENV
:
'"production"'
}
}
),
new webpack.
optimize.UglifyJsPlugin
({
compress
:
{
warnings
:
false
}
}
),
new webpack.LoaderOptionsPlugin
({
minimize
:
true
}
)
]
)
}
On ne va pas détailler tout ça, mais au moins le minimum :
- entry : là on définit le fichier qui est le point d'entrée de l'application : src/main.js ;
- output : là on définit le fichier de sortie, ça ne sert pas pour le développement, mais pour la distribution : dist/build.js ;
- module : là on met les modules dont on va avoir besoin, les loaders, on a par défaut vue, babel et file.
Pour le moment on va se contenter de ça.
Les fichiers de développement de notre application sont dans le dossier src (source) :
Un fichier qui contient un composant a comme suffixe vue, ici on n'en a qu'un : App.vue. Voici son code :
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.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
<template>
<div
id
=
"app"
>
<img
src
=
"./assets/logo.png"
>
<h1>
{{
msg }}
</h1>
<h2>
Essential Links</h2>
<ul>
<li><a
href
=
"https://vuejs.org"
target
=
"_blank"
>
Core Docs</a></li>
<li><a
href
=
"https://forum.vuejs.org"
target
=
"_blank"
>
Forum</a></li>
<li><a
href
=
"https://gitter.im/vuejs/vue"
target
=
"_blank"
>
Gitter Chat</a></li>
<li><a
href
=
"https://twitter.com/vuejs"
target
=
"_blank"
>
Twitter</a></li>
</ul>
<h2>
Ecosystem</h2>
<ul>
<li><a
href
=
"http://router.vuejs.org/"
target
=
"_blank"
>
vue-router</a></li>
<li><a
href
=
"http://vuex.vuejs.org/"
target
=
"_blank"
>
vuex</a></li>
<li><a
href
=
"http://vue-loader.vuejs.org/"
target
=
"_blank"
>
vue-loader</a></li>
<li><a
href
=
"https://github.com/vuejs/awesome-vue"
target
=
"_blank"
>
awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name
:
'app'
,
data
(
) {
return {
msg
:
'Welcome to Your Vue.js App'
}
}
}
</script>
<style>
#app
{
font-family:
'Avenir'
,
Helvetica,
Arial,
sans-serif
;
-webkit-font-smoothing:
antialiased;
-moz-osx-font-smoothing:
grayscale;
text-align:
center
;
color:
#2c3e50
;
margin-top:
60
px;
}
h1,
h2 {
font-weight:
normal
;
}
ul {
list-style-type:
none
;
padding:
0
;
}
li {
display:
inline-block
;
margin:
0
10
px;
}
a {
color:
#42b983
;
}
</style>
On a trois blocs :
- template : pour le template HTML ;
- script : pour le code JavaScript, on peut utiliser ES6 parce que par défaut babel est activé, c'est pour ça qu'il est prévu un export dans le code du composant ;
- style : pour le style CSS.
vue-loader sera chargé d'extraire ces blocs, de transformer le code avec les loaders prévus, et enfin de les assembler en un module qui pourra être pris en charge par Webpack.
On a vu que le point d'entrée est main.js :
On importe Vue évidemment et aussi le seul composant App. Ne vous inquiétez pas de la syntaxe avec le render, on verra ça peut-être dans un autre chapitre.
Au niveau de index.html on a du classique :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
<!
DOCTYPE html
>
<html
lang
=
"en"
>
<head>
<meta
charset
=
"utf-8"
>
<title>
test</title>
</head>
<body>
<div
id
=
"app"
></div>
<script
src
=
"/dist/build.js"
></script>
</body>
</html>
I-D. Le panier encore revisité▲
Maintenant qu'on a toute l'intendance, on va à nouveau coder le panier qu'on a vu lors du précédent tutoriel, mais cette fois à la mode vue-loader.
On va conserver inchangé le fichier main.js.
I-D-1. index.html▲
On va adapter index.html essentiellement pour intégrer Bootstrap :
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"
>
<meta
http-equiv
=
"X-UA-Compatible"
content
=
"IE=edge"
>
<meta
name
=
"viewport"
content
=
"width=device-width, initial-scale=1"
>
<title>
Test vue.js</title>
<link
rel
=
"stylesheet"
href
=
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
>
<link
rel
=
"stylesheet"
href
=
"https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css"
>
<link
rel
=
"stylesheet"
href
=
"https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/united/bootstrap.min.css"
>
</head>
<body>
<div
class
=
"container"
>
<br>
<div
id
=
"app"
></div>
</div>
<script
src
=
"/dist/build.js"
></script>
</body>
</html>
I-D-2. Le composant Panier▲
On va créer un fichier Panier.vue pour le composant du panier :
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.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
<template>
<div
class
=
"panel panel-primary"
>
<div
class
=
"panel-heading"
>
Panier</div>
<table
class
=
"table table-bordered table-striped"
>
<thead>
<tr>
<th
class
=
"col-sm-4"
>
Article</th>
<th
class
=
"col-sm-2"
>
Quantité</th>
<th
class
=
"col-sm-2"
>
Prix</th>
<th
class
=
"col-sm-2"
>
Total</th>
<th
class
=
"col-sm-1"
></th>
<th
class
=
"col-sm-1"
></th>
</tr>
</thead>
<tbody>
<tr
v-for
=
"(item, index) in panier"
>
<td>
{{
item.article }}
</td>
<td>
{{
item.quantite }}
</td>
<td>
{{
item.prix }}
€</td>
<td>
{{
(
item.quantite *
item.prix)
.toFixed(
2
)
}}
€</td>
<td><button
class
=
"btn btn-info btn-block"
@
click
=
"modifier(index)"
><i
class
=
"fa fa-edit fa-lg"
></i></button></td>
<td><button
class
=
"btn btn-danger btn-block"
@
click
=
"supprimer(index)"
><i
class
=
"fa fa-trash-o fa-lg"
></i></button></td>
</tr>
<tr>
<td
colspan
=
"3"
></td>
<td><strong>
{{
total }}
€</strong></td>
<td
colspan
=
"2"
></td>
</tr>
<editeur
:
article
=
"article"
@
add
=
"ajouter"
></editeur>
</tbody>
</table>
</div>
</template>
<script>
import Editeur from './Editeur.vue'
export default {
props
:
[
'panier'
],
data
:
function (
) {
return {
article
:
{
article
:
''
,
quantite
:
0
,
prix
:
0
}
}
},
computed
:
{
total
:
function (
) {
let total =
0
;
for(
let el of this.
panier) {
total +=
el.
prix *
el.
quantite
}
return total.toFixed
(
2
)
}
},
methods
:
{
modifier
:
function(
index) {
this.
article =
this.
panier[
index]
this.
panier.splice
(
index,
1
)
},
supprimer
:
function(
index) {
this.
panier.splice
(
index,
1
)
},
ajouter
:
function(
input) {
this.
panier.push
(
input)
this.
article =
{
article
:
''
,
quantite
:
0
,
prix
:
0
}
}
},
components
:
{
Editeur
}
}
</script>
Je n'ai rien changé au template.
Pour le JavaScript on sait qu'il faudra importer le composant Editeur :
D'autre part je profite de la mise à disposition de ES6 pour voir si babel fonctionne bien :
I-D-3. Le composant Editeur▲
De la même manière, j'ai créé le fichier Editeur.vue pour le composant Editeur :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
<template>
<tr>
<td><input
type
=
"text"
class
=
"form-control"
v-model
=
"input.article"
ref
=
"modif"
placeholder
=
"Article"
></td>
<td><input
type
=
"text"
class
=
"form-control"
v-model
=
"input.quantite"
placeholder
=
"Quantité"
></td>
<td><input
type
=
"text"
class
=
"form-control"
v-model
=
"input.prix"
placeholder
=
"Prix"
></td>
<td
colspan
=
"3"
><button
class
=
"btn btn-primary btn-block"
@
click
=
"ajouter()"
>
Ajouter</button></td>
</tr>
</template>
<script>
export default {
props
:
[
'article'
],
computed
:
{
input
:
function(
) {
return this.
article;
}
},
methods
:
{
ajouter
:
function(
) {
this.
$emit
(
'add'
,
this.
input);
}
}
}
</script>
Là je n'ai rien changé.
I-D-4. Le composant principal▲
Pour terminer, il faut modifier le composant App.vue :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
<template>
<div
id
=
"app"
>
<panier
:
panier
=
"panier"
></panier>
</div>
</template>
<script>
import Panier from './Panier.vue'
export default {
name
:
'application'
,
data
(
) {
return {
panier
:
[
{
article
:
"Cahier"
,
quantite
:
2
,
prix
:
'5.30'
},
{
article
:
"Crayon"
,
quantite
:
4
,
prix
:
'1.10'
},
{
article
:
"Gomme"
,
quantite
:
1
,
prix
:
'3.25'
}
]
}
},
components
:
{
Panier
}
}
</script>
On importe le composant Panier.
À la fin vous devez avoir ces fichiers :
Et normalement le panier à l'écran :
Si ce n'est pas le cas, relisez l'article pour voir le code que vous avez raté ! Ou lisez les informations fournies par les outils de développement du navigateur (je rappelle qu'il y a une extension pour vue.js dans Chrome).
I-E. Jouer avec les templates et les styles▲
I-E-1. Les templates▲
On va voir qu'on peut vraiment faire beaucoup de choses, par exemple utiliser un outil de template comme Pug. On va commencer par l'installer avec npm :
npm install pug --save-dev
Ensuite, changez ainsi le code du template du composant Editeur.vue :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
<
template lang
=
"
pug
"
>
tr
td
input(type="text" class="form-control" v-model="input.article" ref="modif" placeholder="Article")
td
input(type="text" class="form-control" v-model="input.quantite" placeholder="Quantité")
td
input(type="text" class="form-control" v-model="input.prix" placeholder="Prix")
td(colspan="3")
button(class="btn btn-primary btn-block" @click="ajouter()") "Ajouter"
<
/template
>
On déclare le système de template utilisé avec un attribut dans la balise template. Ensuite on l'utilise dans le code.
Si tout passe bien le bon code HTML doit être généré. Pratique non ?
I-E-2. Le style▲
On peut aussi ajouter du style au composant Editeur.vue. Là aussi on peut utiliser un préprocesseur comme Sass. On va d'abord l'installer :
npm install sass-loader node-sass --save-dev
Et ensuite, ajouter un bloc de style :
2.
3.
4.
5.
6.
<style lang
=
"
sass
"
>
$fond:
lightblue;
.bleu
{
background-color:
$fond !important;
}
</
style>
Il faut quand même préciser où agit la classe dans le template :
tr
(
class
=
"bleu"
)
Avec ce résultat :
Le CSS a été correctement généré !
I-F. La production▲
Le but c'est quand même de générer les fichiers de production. Là c'est tout simple :
npm run build
Voyons le dossier dist :
Si vous ouvrez le fichier build.js vous verrez que le code a été « uglifié » et minimisé. Mais maintenant est-ce que ça fonctionne ? Il suffit d'ouvrir index.html dans votre navigateur. Il faudra peut-être changer la référence du fichier build.js si vous n'êtes pas avec un hôte virtuel :
<script
src
=
"dist/build.js"
></script>
Normalement tout fonctionne bien, même le style ajouté à l'éditeur, le tout dans un fichier de 580 Ko !
Si vous voulez voir le code produit sans la compression, commentez ces lignes dans webpack.config.js :
Si vous voulez voir ce que ça donne, je l'ai mis en ligne ici.