Vous l’attendiez, vous en rêviez, voici la troisième et dernière partie de cette introduction au framework Vue.js ! Cette fois encore, nous allons travailler sur la merveilleuse application que nous avons ébauchée ensemble et qui nous permet, je vous le rappelle, de gérer facilement une collection d’URLs d’images ou de vidéos, et de copier ces dernières dans le presse-papiers en un clic pour pouvoir harceler votre entourage !
Aujourd’hui, nous allons transformer notre proof of concept en une application construite et industrialisée dans les règles de l’art. Sans perdre une minute de plus, entrons si vous le voulez bien dans le vif du sujet !
Si notre unique fichier JavaScript a rempli son office jusqu’ici, il est pour
nous grand temps de passer à l’étape suivante : du code correctement découplé,
avec notamment un fichier .vue
par composant. L’ennui, c’est que la
compilation de ces fichiers en code compréhensible par le navigateur est
potentiellement chronophage à mettre en place à la main, à grands coups de
configuration Webpack et Babel. Fort heureusement, l’écosystème Vue.js
comprend un outil en ligne de commande permettant de construire un projet avec
tout ce qu’il faut en un tournemain. Commençons donc par l’installer :
$ npm install -g vue-cli
Puis exécutons-le directement dans le dossier de notre application, et laissons-nous guider :
$ vue init webpack .
? Generate project in current directory? Yes
? Project name memebox
? Project description Meme collection displayer with easy copypasting
? Author Tom Panier <tpanier@synbioz.com>
? Vue build standalone
? Install vue-router? No
? Use ESLint to lint your code? No
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) npm
vue-cli · Generated "memebox".
To get started:
npm run dev
Documentation can be found at https://vuejs-templates.github.io/webpack
Que s’est-il passé ? Nous constatons que beaucoup de fichiers ont été créés, et
que notre index.html
, notamment, a été remplacé. Pour l’heure, lançons, comme
il nous est indiqué, npm run dev
(ou npm start
, ce qui revient au même) :
DONE Compiled successfully in 3147ms
I Your application is running here: http://localhost:8080
Rendons-nous donc sur http://localhost:8080
:
Effectivement, notre application a « disparu » au profit d’une page de garde, certes élégante, mais peu utile en pratique ! Pas de panique, nous allons voir ensemble comment la remettre sur pied.
Lors de la partie 2 de ce tutoriel,
nous avions mis en place un composant Meme
destiné à isoler la logique
d’un élément de notre grille d’images et de vidéos. Dans cette nouvelle
configuration, ce dernier va donc fort logiquement être placé dans un fichier
autonome, à savoir src/components/Meme.vue
:
<template>
<div
class="meme-container"
:class="{ 'meme-container-clicked': clicked }"
@click="copy"
@mouseleave="hover"
>
<iframe
v-if="isEmbed"
class="meme"
:src="previewUrl"
frameborder="0"
allowfullscreen
></iframe>
<div
v-else
class="meme"
:style="{ backgroundImage: 'url(' + previewUrl + ')' }"
></div>
</div>
</template>
<script>
export default {
props: { url: String },
data: () => ({ clicked: false }),
computed: {
isEmbed() {
return /youtu/.test(this.url);
},
previewUrl() {
return getEmbedUrl(this.url) || this.url;
}
},
methods: {
copy() {
copyToClipboard(this.url);
this.clicked = true;
},
hover() {
this.clicked = false;
}
}
};
</script>
Un composant monofichier (en anglais, SFC) est ainsi constitué de deux balises élémentaires :
<template>
, contenant comme de raison le squelette du composant (équivalent
du champ éponyme dans notre ancien fichier app.js
)<script>
, qui doit exposer un module ES6 contenant le reste de la
configuration du composant (là aussi, c’est strictement identique à ce que nous
avions précédemment écrit)Il est également possible de déclarer une balise <style>
pour injecter du CSS
(ou des langages préprocessés dérivés de ce dernier, d’ailleurs).
Il ne reste qu’un détail à régler : notre code fait référence à nos deux
fonctions getEmbedUrl
et copyToClipboard
, qui ne sont pour le moment pas
disponibles dans ce contexte ! Nous pourrions les insérer au début de la
balise <script>
, mais n’avions-nous pas décidé de faire les choses
proprement ? Créons donc deux fichiers dans le dossier src
, respectivement
getEmbedUrl.js
:
export default function getEmbedUrl(url) {
let ytId = url.match(/youtu\.be\/([a-zA-Z0-9_-]+)/);
ytId = ytId || url.match(/youtube\.com.*(\?|&)v=([a-zA-Z0-9_-]+)/);
return ytId
? "https://www.youtube.com/embed/" + ytId.pop()
: null;
}
Et copyToClipboard.js
:
export default function copyToClipboard(text) {
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand("copy");
document.body.removeChild(textArea);
}
Il ne nous reste plus qu’à importer ces deux nouveaux modules là où nous en avons besoin, à savoir dans notre composant :
import getEmbedUrl from "../getEmbedUrl";
import copyToClipboard from "../copyToClipboard";
export default {
// ...
};
Si nous ôtons d’app.js
le code que nous venons de déplacer, et excluons le
tableau où nous stockons nos URLs, il ne reste pas grand-chose :
new Vue({
el: "#app",
data: { urls },
components: { Meme }
});
Il s’agit, en somme, du point d’entrée de notre application ; nous pouvons
retrouver du code très similaire dans le fichier src/main.js
généré par
vue-cli
:
import Vue from "vue";
import App from "./App";
Vue.config.productionTip = false;
new Vue({
el: "#app",
render: h => h(App)
});
Ouvrons maintenant le fichier src/App.vue
, référencé ci-dessus (et ôtons-en
au passage la balise <style>
, inutile dans notre cas) :
<template>
<div id="app">
<img src="./assets/logo.png" />
<HelloWorld />
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
components: { HelloWorld }
};
</script>
Ce composant (car c’en est bien un), référencé directement par le point
d’entrée, constitue la racine des vues de notre application : son rendu est
effectué en premier, et de lui découle le rendu de tous les autres composants.
Modifions-le un brin, en reprenant le code qui se trouvait précédemment dans
notre index.html
:
<template>
<div id="app" class="memebox">
<meme
v-for="url in urls"
:key="url"
:url="url"
/>
</div>
</template>
<script>
import Meme from "./components/Meme";
export default {
components: { Meme }
};
</script>
Le composant déclarant lui-même ses dépendances (en l’occurrence, le composant
Meme
), nous n’avons pas besoin de modifier le code du point d’entrée !
Il reste toutefois, là encore, un problème de référence : la fameuse variable
urls
censée contenir notre précieuse collection…
Il est temps de dire au revoir pour de bon à notre fichier app.js
: tout son
contenu a été déplacé, hormis le tableau contenant les URLs des images et
vidéos à afficher sur l’application. Pour le remplacer, créons à la racine du
projet un fichier urls.json
:
[
"http://sarakha63-domotique.fr/wp-content/uploads/2017/03/Shut-up-and-take-my-money.jpg",
"https://www.youtube.com/watch?v=XMdoGqcnBoo"
]
Ensuite, chargeons-le directement comme un module au sein du composant racine,
à savoir src/App.vue
, et référençons son contenu comme un membre de data
,
là encore tel que nous le faisions dans index.html
précédemment :
import urls from "../urls";
import Meme from "./components/Meme";
export default {
data: () => ({ urls }),
components: { Meme }
};
Notez que data
doit être une fonction qui retourne un objet, afin d’éviter
que plusieurs instances d’un même composant partagent leurs données.
Vous pouvez dès lors supprimer app.js
, ainsi que le dossier src/assets
et
le fichier src/components/HelloWorld.vue
. Si vous avez laissé tourner la
commande npm run dev
tout à l’heure, retournez sur votre navigateur, et…
L’application générée par vue-cli
dispose en effet du hot reloading : en
développement, toute modification dans le code est automatiquement répercutée
dans le navigateur, et ce en conservant l’état courant !
Comme vous le constatez, il ne manque guère que le CSS à notre application pour
fonctionner de nouveau comme avant ; plusieurs choix s’offrent à nous,
notamment la balise <style>
des composants que j’ai mentionnée plus haut,
mais nous allons opter ici pour la simplicité et référencer directement
app.css
dans index.html
.
Déplacez donc le fichier dans le dossier static
, prévu à cet effet :
$ mv app.css static/
Et ajoutez cette ligne à l’intérieur de la balise <head>
:
<link rel="stylesheet" href="/static/app.css" />
Bingo !
Ce serait dommage de versionner urls.json
: votre collection ne sera pas
celle d’un·e autre utilisateur·rice. Un pattern simple à appliquer dans un
tel cas est le suivant :
urls.json.dist
urls.json
au .gitignore
, pour éviter que lui-même soit versionnéNous allons améliorer un brin le rendu de notre page, afin d’éviter qu’un
élément se retrouve seul en dernière ligne dans certains cas.
Ajoutez le code suivant à app.css
:
.meme-container:nth-last-child(2):nth-child(10n) { // 10 éléments par ligne
min-width: 50%;
}
Je vous propose une ultime retouche : le support des URLs de la forme
https://www.youtube.com/embed/
, y compris avec des paramètres en query
string. Cela aura un avantage incommensurable : le fait de pouvoir préciser
un début et une fin à la vidéo YouTube partagée !
Voici un exemple pas piqué des hannetons.
Afin de pouvoir nager définitivement dans le bonheur, voici la modification à
apporter à src/getEmbedUrl.js
:
// Avant :
let ytId = url.match(/youtu\.be\/([a-zA-Z0-9_-]+)/);
// Après :
let ytId = url.match(/(youtu\.be|youtube\.com\/embed)\/([a-zA-Z0-9_-]+)/);
Nous en avons fini ! Notre application est désormais fonctionnelle et
construite selon les standards de l’industrie — en tout cas, c’est un bon
début : nous pourrions envisager de tester unitairement nos méthodes et
composants, ou encore d’automatiser les vérifications syntaxiques et
stylistiques grâce à ESLint (dans les deux cas, je le rappelle, vue-cli
nous
mâche le travail de mise en place des outils nécessaires). Nous explorerons
certainement ces possibilités lors de prochains articles.
Si cela vous intéresse, vous pouvez retrouver sur Github l’application terminée.
Quant à moi, je vous dis à très bientôt pour de nouvelles aventures avec Vue.js !
L’équipe Synbioz.
Libres d’être ensemble.
Nos conseils et ressources pour vos développements produit.