La utilidad de separar nuestra aplicación en componentes es que cada uno de ellos puede guardarse en su propio fichero y así no tenemos un fichero con demasiado código. A estos ficheros que contienen un componente se les llama Single File Component (SFC).
Como vimos en la unidad anterior, en el fichero en que definimos el componente exportamos un objeto con las opciones del componente (el segundo parámetro del app.component()
):
export default{
props: ['todo'],
template:
`<li @dblclick="delTodo">
<label>
<input type="checkbox" v-model="todo.done">
<del v-if="todo.done">
</del>
<span v-else>
</span>
</label>
</li>`,
methods: {
delTodo() {
alert('Quiero borrar "' + this.todo.title + '"');
}
}
}
Y donde queramos usarlo (puede ser en otro componente o en la instancia raíz de Vue) debemos:
El fichero main.js
de nuestra aplicación de la Lista de tareas quedaba:
import TodoList from './TodoList.js'
import TodoAdd from './TodoAdd.js'
import TodoDellAll from './TodoDellAll.js'
var myApp=Vue.createApp({
components: {
TodoList,
TodoAdd,
TodoDellAll,
}
})
Recuerda que para que el navegador entienda la sentencia import
debemos indicar que el script que lo contiene es de tipo module:
<script type="module" src="main.js"></script>
Podéis ver aquí cómo quedará nuestra aplicación de ejemplo con los componentes separados en ficheros:
Aunque puede usarse Vue como hemos visto, enlazándolo directamente en el index.html lo más habitual es crear un nuevo proyecto para la aplicación que vamos a desarrollar usando npm y Vite. Esto:
Para crear un nuevo proyecto ejecutamos:
npm init vue@latest
Al ejecutar este comando se nos pregunta el nombre del proyecto a crear y si queremos usar o no determinadas herramientas (más adelante veremos qué es cada una, de momento decimos que No) y se creará el directorio para el mismo con el package.json del proyecto en su interior con su configuración.
Lo primero que haremos es entrar al directorio del proyecto e instalar las dependencias (npm install
) y a continuación ejecutar:
npm run dev
Este script de Vite funciona como ya vismo en el bloque de Javascript: compila el código, muestra si hay errores, lanza un servidor web en el puerto 5173 y carga el proyecto en el navegador (http://localhost:5173). Si cambiamos cualquier fichero del directorio src recompila y recarga la página automáticamente. La página generada es:
El proyecto creado usa Vite, que es un bundler más eficiente que webpack (que es el que se usaba en versiones anteriores de Vue) a la hora de gestionar nuestro código tanto en desarrollo como en producción.
Se ha creado la carpeta con el nombre del proyecto y dentro el scaffolding para nuestro proyecto:
Los principales ficheros y directorios creados son:
package.json
: configuración del proyecto (nombre, autor, …) y dependenciasvite.config.js
: configuración de Viteindex.html
: html con un div donde se cargará la appnode_modules
: librerías de las dependenciaspublic
: lugar donde dejar elementos estáticos que no pasarán por vite. Podemos poner imágenes, CSS, etc y lo referenciaremos de forma ABSOLUTA como si estuviera en /
src
: todo nuestro código
assets/
: nuestros CSS, imágenes, etc. Elementos que vite procesará y optimizarácomponents/
: carpeta que contendrá los ficheros .vue de los diferentes componentes
HelloWorld.vue
: componente de ejemplo llamado por App.vuerouter/
: carpeta con los ficheros del router si usamos vue-_routerstore/
: carpeta con los ficheros del store si usamos pinia o vuexviews
: si usamos vue-router aquí pondremos los componentes que constituyen una vista de la aplicaciónApp.vue
: es el componente principal y constituye nuestra página de inicio del proyecto. Aquí cargaremos la cabecera, el menú,… y los diferentes componentesmain.js
: JS principal que crea la instancia de Vue que carga el componente principal llamado App.vue y lo renderiza en #appAquí se configura nuestra aplicación:
npm install nombre-del-paquete -S
(la opción -S la añade a package.json como dependencia de producción).npm install nombre-del-paquete -D
(la opción -D la añade a package.json pero como dependencia de desarrollo).Fichero index.html: Simplemente tiene el <div> app que es el que contendrá la aplicación.
Fichero main.js:
import { createApp } from 'vue'
import App from './App.vue'
import './assets/main.css'
createApp(App).mount('#app')
Es el fichero JS principal. Importa la utilidad createApp de la librería Vue y el componente App.vue. Crea la instancia de Vue con el componente definido en App.vue y lo renderiza en el elemento #app.
Fichero App.vue:
Es el componente raíz de la aplicación, el que contiene el layout de la página. Se trata de un SFC (Single File Component) y lo que contiene dentro de la etiqueta <template> es lo que se renderizará en el div app que hay en index.html. Si contiene algún otro componente se indica aquí dónde renderizarlo (en este caso
En el siguiente apartado explicaremos qué es un SFC y qué partes lo forman. De momento veamos qué contiene cada sección:
template
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<TheWelcome />
</main>
</template>
Muestra la imagen del logo (las imágenes y otros ficheros como ficheros .css se guardan dentro de /src/assets/) y los subcomponentes HelloWorld y TheWelcome.
script
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
Importa y registra el componente HelloWorld que se muestra en el template. Está en forma de Composition API. En forma de Options API sería:
<script>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
export default {
name: 'app',
components: {
HelloWorld,
TheWelcome
}
}
</script>
style
Aquí se definen los estilos de este componente. Como la etiqueta SÍ tiene el atributo scoped (<style scoped>
) significa que los estilos aquí definidos se aplicarán SÓLO a este componente, no a sus subcomponentes.
Fichero components/HelloWorld.vue: Es el componente que muestra el texto que aparece bajo la imagen. Recibe como parámetro el título a mostrar. Veamos qué contiene cada sección:
template
<template>
<div class="greetings">
<h1 class="green"></h1>
<h3>
You’ve successfully created a project with
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>.
</h3>
</div>
</template>
Muestra el msg recibido como parámetro y varios apartados con listas.
script
<script setup>
defineProps({
msg: {
type: String,
required: true
}
})
</script>
Recibe el parámetro msg que es de tipo String. En sintaxis Options API sería:
<script>
export default {
name: 'HelloWorld',
props: {
msg: {
type: String,
required: true
}
}
}
</script>
style
También tiene el atributo scoped (<style scoped>
) por lo que los estilos aquí definidos se aplicarán sólo a este componente.
Normalmente trabajaremos con algún gestor de versiones como git. Para subir nuestro proyecto al repositorio lo creamos (en GitHub, GitLab o donde queramos) y ejecutamos desde la carpeta del proyecto:
git init
git add .
git remote add origin https://github.com/mi-usuario/mi-proyecto
git commit -m "Primer commit"
git push -u origin main
Cuando nuestra aplicación esté lista para subir a producción ejecutaremos el script:
npm run build
Este comando genera los JS y CSS para subir a producción dentro de la carpeta dist. El contenido de esta carpeta es lo único que debemos subir a nuestro servidor de producción.
Guardar los componentes en ficheros .js
como hicimos en el tema anterior genera varios problemas:
Por tanto eso puede ser adecuado para proyectos muy pequeños pero no lo es cuando estos empiezan a crecer.
La solución es guardar cada componente en un único fichero (SFC), que tendrá extensión .vue. Estos ficheros contienen 3 secciones diferentes:
Aunque esto va contra la norma de tener el HTML, JS y CSS en ficheros separados en realidad sí están separados en diferentes secciones y tenemos la ventaja de tener en un único fichero todo lo que necesita el componente.
La mayoría de editores soportan estos ficheros instalándoles algún plugin, (como Volar para Visual Studio Code) por lo que el resaltado de las diferentes partes es correcto. Además Vite nos permite usar ES2015 o posterior y los preprocesadores más comunes (SASS, Pug/Jade, Stylus, …) y ya se se traducirá automáticamente el código a ES5, HTML5 y CSS3.
Veamos en detalle cada una de las secciones del SFC.
Aquí incluiremos el HTML que sustituirá a la etiqueta del componente. Recuerda que en las versiones anteriores a Vue3 dentro sólo puede haber un único elemento HTML (si queremos poner más de uno los incluiremos en otro que los englobe).
Si el código HTML a incluir en el template es muy largo podemos ponerlo en un fichero externo y vincularlo en el template, así nuestro SFC queda más pequeño y legible:
<template src="./myComp.html">
</template>
Respecto al lenguaje, podemos usar HTML (la opción por defecto) o PUG que es una forma sencilla de escribir HTML. Lo indicamos como atributo de <template>:
<template lang="pug">
...
Aquí definimos y exportamos el componente, que será un objeto con diferentes propiedades. Si utiliza subcomponentes hay que importarlos antes de definir el objeto y registrarlos dentro de este.
Entre las propiedades que puede tener el objeto están:
Aquí pondremos estilos CSS que se aplicarán al componente. Podemos usar CSS, SASS o PostCSS. Si queremos importar ficheros de estilo con @import
deberíamos guardarlos dentro de la carpeta assets de nuestra aplicación.
Si la etiqueta incluye el atributo scoped estos estilos se aplicarán únicamente a este componente (y sus descendientes) y no a todos los componentes de nuestra aplicación. Si tenemos estilos que queremos que se apliquen a toda la aplicación y otros que son sólo para el componente y sus descendientes pondremos 2 etiquetas <style>, una sin el atributo scoped y otra con él.
La forma más común de asignar estilos a elementos es usando clases. Para conseguir que su estilo cambie fácilmente podemos asignar al elemento clases dinámicas que hagan referencia a variables del componente. Ej.:
<template>
<p :class="[decoration, {weight: isBold}]">Hi!</p>
</template>
<script>
export default {
data() {
return {
decoration: 'underline',
isBold: true
}
}
}
</script>
<style lang="css">
.underline { text-decoration: underline; }
.weight { font-weight: bold; }
</style>
El párrafo tendrá la clase indicada en la variable decoration
(en este caso underline) y además como el valor de isBold
es verdadero tendrá la clase weight. Hacer que cambien las clases del elemento es tan sencillo como cambiar el valor de las variables.
Podemos ver las diferentes maneras de asignar clases a los elementos HTML en la documentación de Vue.
Igual que vimos en la etiqueta <template>, si el código de los estilos es demasiado largo podemos ponerlo en un fichero externo que vinculamos a la etiqueta con el atributo src.
Además de estos 3 bloques un SFC puede tener otros bloques definidos por el programador para, por ejemplo, incluir la documentación del componente o sus test unitarios:
<custom1 src="./unit-test.js">
Aquí podríamos incluir la documentación del proyecto
</custom1>
Si queremos usar un nuevo paquete en nuestra aplicación lo instalaremos con npm:
npm install nombre-paquete
Este comando además de instalar el paquete en node-modules lo añade a las dependencias del package.json. La opción --save
o -S
lo añadirá como dependencia de producción y --dev
o -D
como dependencia de desarrollo. Si no ponemos nada se añade como una dependencia de producción. Ej.:
npm install -S axios
Para usarlo en nuestros componentes debemos importarlo y registrarlo tal y como se indique en su documentación. Lo normal es hacerlo en el main.js (o en algún fichero JS que importemos en main.js como en el caso de los plugins) si queremos poderlo usar en todos los componentes.
Si el paquete que queremos instalar se encuentra como plugin el proceso es más sencillo ya que sólo es necesario usar app.use(myPlugin, { /* opciones opcionales */})
en el fichero main.js
.
Podemos utilizar Bootstrap 5 directamente en Vue ya que esta versión no necesita de la librería jQuery.
Para usarlo simplemente lo instalaremos como una dependencia de producción y después lo añadimos al fichero src/main.js
:
import "bootstrap/dist/css/bootstrap.css"
Recuerda que siempre es conveniente importar Bootstrap antes de importar nuestro propio CSS (antes de la línea import './assets/main.css'
). Si necesitamos algún componente de Bootstrap que utilice javascript importaríamos también su javascript en el fichero main.js pero en este caso después de montar la aplicación vue:
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import "bootstrap/dist/css/bootstrap.css"
import './assets/main.css'
createApp(App).mount('#app')
import "bootstrap/dist/js/bootstrap.js"
Para usar los iconos de Bootstrap 5 podemos instalar el paquete bootstrap-icons o bien importarlos en el CSS desde su CDN, tal y como se explica en la documentación de Bootstrap. Una vez hecho ya podemos incluir los iconos en etiquetas <i>.
Por ejemplo, si importamos el CSS incluiremos en el <style> del componente App.vue:
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css");
y donde queramos incluir el icono de la papelera, por ejemplo, incluimos:
<i class="bi bi-trash"></i>
Respecto a los componentes de Bootstrap, para que funcionen sólo tenemos que usar los atributos data-bs-
(recuerda que muchos de estos componenetes necesitan su javascript por lo que deberemos importarlo como se ha explicado antes). Por ejemplo para hacer un botón colapsable haremos:
<button
class="btn btn-primary"
data-bs-target="#collapseTarget"
data-bs-toggle="collapse">
Bootstrap collapse
</button>
<div class="collapse py-2" id="collapseTarget">
This is the toggle-able content!
</div>
En lugar de usar atributos data-bs- podemos envolver los componentes bootstrap en componentes Vue como se explica en muchas páginas, como Using Bootstrap 5 with Vue 3.
Creamos un nuevo fichero en /src/components (o en alguna subcarpeta dentro) con extensión .vue. Donde queramos usar ese componente debemos importarlo y registrarlo como ya hemos visto:
import CompName from './CompName.vue'
export default {
...
components: {
'comp-name': CompName
}
...
}
Y ya podemos incluir el componente en el HTML:
<comp-name ...> ... </comp-name>
Podemos seguir depurando nuestro código, poniendo puntos de interrupción y usando todas las herramientas que nos proporciona la consola mientras estamos en modo de depuración (si hemos abierto la aplicación con npm run dev
).
Si estamos usando webpack no podemos ver nuestro código directamente sino que nuestros fichero se localizan dentro del apartado webpack:
Recordad que si hemos instalado las Vue DevTools tenemos una nueva pestaña en la consola desde la que podemos ver todos nuestros componentes con sus propiedades y datos:
Recordemos que la aplicación que estamos desarrollando tiene los componentes:
Para transformar esto en SFC simplemente crearemos un fichero para cada uno de estos componentes. Nuestro anterior index.html será el <template> del componente principal App.vue, que en un sección <script> deberá importar y registrar cada uno de los componentes usados en el template (todo-list, todo-add y todo-del-all).
Solución: