Commentaires
Comme dit précédemment, on préfèrera utiliser les Single File Components (SFC) plutôt que la syntaxe vue juste avant quand on souhaite manipuler les composants.

La syntaxe


Les SFC sont des fichiers qui ont une syntaxe particulière et qui permet de renseigner tous les éléments qu'un composant peut posséder :


  • Du JS (nos data, CP, watchers, methods...)

  • Du HTML (le template)

  • Du CSS pour le style


Ces fichiers ont une extension propre en .vue et ont la syntaxe suivante :

{"language":"text/html","content":"<template>\n\t<!-- C'est ici que je mets mon HTML -->\n</template>\n<script>\n\t// Mon JS est ici, évidemment\n</script>\n<style>\n\t/** Le CSS ! **/\n</style>","filename":"Composant.vue"}

Dans la partie script on importera tout ce dont on a besoin (d'autres composants, une librairie) puis exportera le code de notre composant. Il est important de respecter cet ordre : template, script puis style (ou script, template puis style) comme indiqué dans le style guide (Single file component top level element order recommended) pour des questions de lisibilité.

{"language":"text/html","content":"<template>\n\t<!-- C'est ici que je mets mon HTML -->\n</template>\n<script>\n\t// Des import si besoin\n\texport default {\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\t...\n\t\t\t}\n\t\t},\n\t\tmethods:{\n\n\t\t},\n\t\tcreated() {\n\t\t\t\n\t\t}\n\t}\n</script>\n<style>\n\t/** Le CSS ! **/\n</style>","filename":"Composant.vue"}

Arthur, l'apprenti développeurJe pense avoir compris mais peut-on voir un exemple plus concret ? Par exemple en reprenant le slider ?
Oui, regarde voici comment ça se passe :

{"language":"text/html","content":"<template>\n <div>\n <button @click=\"slide(-1)\">Aller à gauche</button>\n <button @click=\"slide(1)\">Aller à droite</button>\n <template v-for=\"(slide, key) in slides\" :key=\"key\">\n <div>Key : {{ key }} Index : {{ index }}</div>\n <div v-if=\"index == key\">\n {{ slide }}\n </div>\n </template>\n </div>\n</template>\n\n<script>\n export default {\n data() {\n return {\n slides:[\n 'Flower',\n 'Animal',\n 'Space'\n ],\n index:0\n }\n },\n methods:{\n slide(operation) {\n // Car % n'est pas le modulo mathématique en JS\n // on doit faire un code un peu compliqué\n // pour gérer les nombres négatifs\n this.index = (((this.index + operation)%this.slides.length)+this.slides.length)%this.slides.length\n }\n }\n }\n</script>","filename":"Slider.vue"}

Arthur, l'apprenti développeurAh bah c'est super simple. Et très clair ! Mais du coup maintenant que j'ai mon fichier Slider.vue, j'en fais quoi ?
Comme on l'a vu au début et comment tu peux peut être l'imaginer, on peut mettre des composants dans des composants. Pour ça, il suffit de les importer !

Utiliser un SFC


Pour utiliser un SFC, il nous faut tout simplement l'importer et l'utiliser. Si c'est notre composant principal, on peut créer notre application Vue depuis ce dernier avec createApp. C'est déjà ce qu'on faisait avant avec notre root component ! Le root component est par défaut un composant.
Imaginons donc que nous avons un fichier main.js (ce qui est le cas par défaut avec vue-cli) que l'on inclut dans notre index.html. Il ressemblera à :

{"language":"text/javascript","content":"import { createApp } from 'vue'\n\nimport Slider from './components/Slider.vue'\n\ncreateApp(Slider).mount('#app')","filename":"main.js"}

Arthur, l'apprenti développeurcreateApp() ? On ne fait pas Vue.createApp() d'habitude ?
Si tout à fait ! Mais d'habitude on n'importait pas createApp non plus, on avait accès à tout Vue grâce au CDN. Là on importe que ce dont on a besoin depuis vue, par exemple la méthode createApp() ;)

Arthur, l'apprenti développeurOk je comprends.
Parfait. Là on vient de voir comment utiliser notre composant en tant que root component. Comme dit avant, on peut aussi l'utiliser au sein d'un autre composant. Ce sera généralement le cas. D'autant plus si tu utilises vue-cli ou vue-vite, par défaut tu as un composant principal nommé App.vue et tu appelles tous les autres composants dedans.
Pour préciser au sein d'un composant que ce dernier utilise d'autres composants on va utiliser l'objet components. Voici donc un exemple réutilisant le fichier App.vue par défaut de vue-cli :

{"language":"text/html","content":"<template>\n <img alt=\"Vue logo\" src=\"./assets/logo.png\">\n <slider></slider>\n</template>\n\n<script>\nimport Slider from './components/Slider.vue'\n\nexport default {\n name: 'App',\n components: {\n Slider\n }\n}\n</script>\n\n<style>\n#app {\n font-family: Avenir, Helvetica, Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-align: center;\n color: #2c3e50;\n margin-top: 60px;\n}\n</style>\n","filename":"App.vue"}

{"language":"text/javascript","content":"import { createApp } from 'vue'\nimport App from './App.vue'\n\ncreateApp(App).mount('#app')","filename":"main.js"}


Utiliser des preprocesseurs


Les SFC ne se limitent pas à l'utilisation simple de HTML et CSS natifs. On peut utiliser des préprocesseurs comme PUG pour le HTML ou SASS (ou LESS, ou Stylus...) pour le CSS. On doit simplement préciser la "langue" dans notre balise, comme suit pour PUG :

{"language":"text/html","content":"<template lang=\"pug\">\nimg(alt=\"Vue logo\" src=\"./assets/logo.png\")\nSlider\n</template>\n\n<script>\nimport Slider from './components/Slider.vue'\n\nexport default {\n name: 'App',\n components: {\n Slider\n }\n}\n</script>\n\n<style>\n#app {\n font-family: Avenir, Helvetica, Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-align: center;\n color: #2c3e50;\n margin-top: 60px;\n}\n</style>\n","filename":"App.vue"}

Arthur, l'apprenti développeurJe ne connais pas PUG, mais je me renseignerai. Cependant, ton code fonctionne, tu n'as rien besoin de faire d'autre ?
Oui et non. En fait, tu peux utiliser PUG uniquement SI tu as installé le "loader de PUG" qui permet à ton bundler (webpack par exemple (rappel : vue-cli se base sur webpack)) de lire ce genre de format et de le compiler. Ainsi, n'oublie pas d'avoir bien installé ton loader comme habituellement avant de l'utiliser dans tes SFC. Par exemple pour sass, il faut installer sass-loader etc. On aurait donc le code suivant si on voulait aussi utiliser SASS :

{"language":"text/html","content":"<template lang=\"pug\">\nimg(alt=\"Vue logo\" src=\"./assets/logo.png\")\nSlider\n</template>\n\n<script>\nimport Slider from './components/Slider.vue'\n\nexport default {\n name: 'App',\n components: {\n Slider\n }\n}\n</script>\n\n<style lang=\"sass\">\n#app {\n font-family: Avenir, Helvetica, Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-align: center;\n color: #2c3e50;\n margin-top: 60px;\n}\n</style>","filename":"App.vue"}

Arthur, l'apprenti développeurOk je comprends. Je peux donc utiliser SASS, LESS, Stylus ou même TailwindCSS ou Bootstrap ?
Tout à fait, dès que tu as leur loader ou leur plugin. Je te conseille de te renseigner sur comment les installer avec ton bundler/compilateur (webpack, vue-cli, vue-vite...) car cela change pour chacun.
Attention cependant, pour TailwindCSS, on n'utilisera pas lang="tailwind" mais lang="postcss" pour utiliser les @apply classiques :

{"language":"text/html","content":"<template>\n <img alt=\"Vue logo\" src=\"./assets/logo.png\" />\n <Slider />\n</template>\n\n<script>\nimport Slider from './components/Slider.vue'\n\nexport default {\n name: 'App',\n components: {\n Slider\n }\n}\n</script>\n\n<style lang=\"postcss\">\n#app {\n @apply bg-red-500;\n}\n</style>","filename":"App.vue"}

Tu remarqueras par ailleurs qu'on peut écrire la balise Slider de manière auto-fermante ou orpheline (comme les img, les meta...). On peut toujours le faire à partir du moment où on ne précise rien comme contenu au sein de notre balise (pas de <slider>Du contenu </slider>). On verra ce cas d'utilisation plus tard.

Arthur, l'apprenti développeurD'accord c'est noté pour la balise slider orpheline. Bon par contre, je ne connais pas très bien TailwindCSS donc je ne comprends pas tout ça, mais si je m'y mets au moins je sais comment l'utiliser au sein de SFC !
Tout à fait, ça sert à ça !

Arthur, l'apprenti développeurDu coup on a fini avec les SFC ?
Pas exactement, il faudrait qu'on voit autre chose concernant le style.

Un point sur le style


On a vu qu'on pouvait utiliser des préprocesseurs CSS pour le style au sein de nos SFC. Cependant, comment sont gérés ces styles au sein de notre page ? Comment sont-ils appliqués sur le HTML ?

Arthur, l'apprenti développeurC'est vrai ça, comment ?
En l'état actuel, une balise <style></style> est créée et contient notre code #app{...}. Tu peux vérifier en faisant "Inspecter l'élément" sur ton navigateur. C'est d'ailleurs pour ça que même si on n'a pas la div id="app" dans notre composant App.vue, celle-ci est bien stylisée. Cela peut poser vite des problèmes...
Imaginons le code suivant :

{"language":"text/html","content":"<template>\n <div>\n <button @click=\"slide(-1)\">Aller à gauche</button>\n <button @click=\"slide(1)\">Aller à droite</button>\n <template v-for=\"(slide, key) in slides\" :key=\"key\">\n <div>Key : {{ key }} Index : {{ index }}</div>\n <div v-if=\"index == key\">\n {{ slide }}\n </div>\n </template>\n </div>\n</template>\n\n<script>\n export default {\n ...\n }\n</script>\n\n<style>\n\tdiv{\n\t\tcolor:blue;\n\t}\n</style>","filename":"Slider.vue"}

{"language":"text/html","content":"<template>\n <div>\n <img alt=\"Vue logo\" src=\"./assets/logo.png\">\n </div>\n <Slider />\n</template>\n\n<script>\nimport Slider from './components/Slider.vue'\n\nexport default {\n name: 'App',\n components: {\n Slider\n }\n}\n</script>\n\n<style>\n#app {\n font-family: Avenir, Helvetica, Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-align: center;\n color: #2c3e50;\n margin-top: 60px;\n}\n\ndiv{\n color:red;\n}\n</style>\n","filename":"App.vue"}

Arthur, l'apprenti développeurAh oui en effet, on va avoir avoir un problème...
Oui. Ce serait bien mieux de faire en sorte que le style de notre composant est une portée "locale", c'est à dire qu'il n'agisse que sur les éléments HTML de notre composant. C'est possible grâce à scoped ou grâce aux modules.

Arthur, l'apprenti développeurC'est quoi ça encore ?
Le scoped CSS, que je traduirais en Français par "CSS à portée limitée", permet d'appliquer du CSS uniquement sur le HTML du composant en question. Grâce à PostCSS, utilisé par vue-cli (mais qu'on peut utiliser avec les autres outils de build), le code suivant :

{"language":"text/html","content":"<template>\n\t<div class=\"container\">...</div>\n</template>\n<style scoped>\n\t.container{\n\t\tborder:1px solid black;\n\t}\n</style>","filename":"Component.vue"}

Sera transformé lors de la compilation en code suivant :

{"language":"text/html","content":"<template>\n\t<div class=\"container\" data-v-f3f2eg8>...</div>\n</template>\n<style scoped>\n\t.container[data-v-f3f2eg8]{\n\t\tborder:1px solid black;\n\t}\n</style>","filename":"Component.vue"}

Ainsi, la portée du CSS est limitée à tous les éléments qui auront l'attribut personnalisé data correspondant, et donc uniquement aux éléments HTML du composant ;). Mission réussie !

Petite note : Vue déconseille d'utiliser le style à portée limitée avec les sélecteurs CSS d'éléments (button{}, div{}...) pour des questions d'optimisation. Voici le lien vers cette note : [les sélecteurs d'éléments avec le scoped CSS]

Arthur, l'apprenti développeurC'est noté ! Du coup j'utilise le scoped CSS quand je veux que mon CSS ait une portée "locale" ?
Tout à fait !

Les modules CSS ont le même but mais j'avouerais que je les trouve plus complexes à utiliser et les retrouve moins souvent. Si tu es intéressé : https://vue-loader.vuejs.org/guide/css-modules.html#usage

Arthur, l'apprenti développeurEntendu, je regarderai ça, mais je pense que le scoped CSS sera déjà utile. J'ai une question cependant : a-t-on un projet, un "fil rouge" à suivre pour ce cours ? J'aime bien, ça permet de mettre en pratique tout de suite !
Oui ! Le but va être de créer un site de recueil d'avis artistiques.

Arthur, l'apprenti développeurArtistiques ? On va noter des oeuvres d'art ?
Presque. On va noter des photos prises bien loin de nous, par des professionnels nommés Curiosity ou encore Spirit...
Rendez-vous au prochain chapitre pour en savoir plus ;) J'ai terminé cette partie
Demander de l'assistance