Vue.js2 : on s'organise !
Dimanche 23 octobre 2016 17:18
Dans tout les exemples qu'on a vus dans les articles précédents 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 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 voit le jour...
Je ne vais pas clarifier tout ça dans cet article, 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...
Webpack
Webpack est un modules 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 repproche qu'on peut faire à Webpack c'est qu'il est pas vraiment facile à prendre en main, mais une fois qu'on le connait bien on peut vraiment tout faire ! Dans le contexte de cet article 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.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 :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).
Etat des lieux
Pour fonctionner Webpack a besoin d'un fichier de configuration webpack.config.js. Vous en trouvez un à la racine avec ce code :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
<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: 60px; } h1, h2 { font-weight: normal; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } 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.
import Vue from 'vue' import App from './App.vue' new Vue({ el: '#app', render: h => h(App) })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 article. Au niveau de index.html on a du classique :
<!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>
Le panier encore revisité
Maintenant qu'on a toute l'intendance on va à nouveau coder le panier qu'on a vu lors des précédents articles mais cette fois à la mode vue-loader. On va conserver inchangé le fichier main.js.index.html
On va adapter index.html essentiellement pour intégrer Bootstrap :<!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>
Le composant Panier
On va créer un fichier Panier.vue pour le composant du panier :<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 :
import Editeur from './Editeur.vue' ... components: { Editeur }D'autre part je profite de la mise à disposition de ES6 pour voir si babel fonctionne bien :
let total = 0; for(let el of this.panier) { total += el.prix * el.quantite }
Le composant Editeur
De la même manière j'ai créé le fichier Editeur.vue pour le composant Editeur :<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é.
Le composant principal
Pour terminer il faut modifier le composant App.vue :<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. A 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).
Jouer avec les templates et les styles
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-devEnsuite changez ainsi le code du template du composant Editeur.vue :
<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 ?
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-devEt ensuite ajouter un bloc de style :
<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é !
La production
Le but c'est quand même de générer les fichiers de production. Là c'est tout simple :npm run buildVoyons 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 :
new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }), new webpack.LoaderOptionsPlugin({ minimize: true })Si vous voulez voir ce que ça donne je l'ai mis en ligne ici.
Par bestmomo
Aucun commentaire