En un proyecto Vue, lo habitual suele ser utilizar los denominados Componentes SFC (Single File Component), o lo que es lo mismo, componentes en un único fichero. Se trata de una variación de un fichero .html
(realmente no es HTML, pero se parece mucho) especialmente diseñada para cubrir las demandas de los componentes de un framework de frontend.
Estos ficheros tienen la extensión .vue
y generalmente se encuentran en la carpeta src/components
del proyecto, salvo una excepción, el archivo App.vue
, que es el componente principal de la aplicación y se encuentra en la carpeta src
, fuera de components
.
¿Por qué ficheros .vue
?
Antes de la aparición de esta funcionalidad de Vue, muchos frameworks Javascript basados en componentes utilizaban (incluso aún hoy en día se utilizan) una separación por tecnologías, donde para un determinado componente .js
, le acompaña también un fichero de estilos .css
(o de algún preprocesador relacionado como .scss
).
Con el tiempo, el proyecto crece, y el número de componentes también, por lo que tener separado un componente en diferentes archivos multiplica el número total de archivos y (aunque esto puede ser muy subjetivo) hace más complejo el manejar y cambiar entre archivos a la hora de codificar.
Además, obligarte a tener componentes lo suficientemente pequeños como para que sean manejables puede ser una buena práctica para obligarte a no complicar demasiado el componente, y si lo hace, dividirlo en subcomponentes más pequeños.
Por esta razón, en Vue se suele utilizar un sólo fichero
.vue
para los componentes, el cuál incluye todas las tecnologías relacionadas en él. No obstante, si lo prefieres, puedes separarlos en archivos diferentes, como veremos más adelante.
Estructura de archivos .vue
Como habíamos comentado anteriormente, un archivo .vue
parece un archivo .html
, pero realmente sólo lo parece, ya que su finalidad es hacer la curva de aprendizaje de Vue mucho más suave.
Dicho componente o archivo .vue
tendrá una estructura similar a la siguiente:
<template>
<!-- Código HTML -->
</template>
<script>
// Código Javascript
</script>
<style>
/* Código CSS */
</style>
Como vemos, en un sólo fichero podemos escribir las 3 tecnologías principales del frontend: HTML, Javascript y CSS. Sin embargo, cada una de estas etiquetas tiene algunas particularidades propias de Vue, que permiten añadir más potencia a los componentes SFC. Analizaremos de cuales se trata en los siguientes apartados de este artículo.
Separación de tecnologías
Como comentamos, las diferentes tecnologías se incluyen por defecto en el mismo archivo .vue
, pero si prefieres tenerlo en archivos separados, puedes hacerlo de la siguiente forma:
<template>
<!-- Código HTML -->
</template>
<script src="./BaseComponent.js"></script>
<style src="./BaseComponent.css"></style>
Observa que en este caso tendríamos tres archivos diferentes por componente: BaseComponent.vue
donde tendríamos la etiqueta <template>
con el contenido HTML del mismo y las referencias a los otros dos archivos: BaseComponent.js
para el Javascript y BaseComponent.css
para los estilos CSS.
Es decisión del desarrollador decidir si los archivos
.vue
tendrán todo en un solo fichero o se separarán el.css
y el.js
(o ambos) en diferentes archivos. En la práctica, lo habitual es mantener todo en el mismo archivo.vue
.
Los templates de Vue (HTML)
La etiqueta <template>
representa la plantilla que contiene el contenido HTML del componente. Realmente tiene varios añadidos, puesto que se pueden realizar bucles, condicionales y otras acciones directamente desde las etiquetas HTML con unas directivas de Vue, que veremos más adelante.
De momento podemos añadir contenido HTML simple, del que ya conocemos y aprendimos en nuestra página LenguajeHTML:
<template>
<div class="container">
<h1>Mi App.vue principal</h1>
</div>
</template>
Soporte para Vue 2
Ten en cuenta que en el ejemplo anterior sólo estamos utilizando un elemento <div>
. Si estamos utilizando Vue 2 y añadimos más elementos hermanos a este (al mismo nivel, fuera de container pero dentro de template), nos aparecerá un error similar al siguiente:
Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.
Esto es una limitación de Vue 2, ya que dentro de la etiqueta <template>
sólo puede existir un elemento hijo. Dicho de otra forma, en el ejemplo anterior todo el contenido HTML debería estar dentro del <div>
con clase container
, para sólo tener un elemento raíz en <template>
.
Existen tres formas de solucionar este problema:
- Introducir todo el contenido dentro de un elemento
<div>
contenedor. - Utilizar el plugin vue-fragments, que permite solucionarlo.
- Utilizar Vue 3, en el cuál no tenemos esta limitación y podemos añadir múltiples hijos.
Usando Pug con Vue
Además, si lo preferimos, podemos utilizar el sistema de plantillas Javascript Pug, lo que nos permite escribir etiquetas de forma mucho menos verbose, y utilizar algunas características dinámicas.
Si lo queremos utilizar, necesitaremos instalar la propia librería:
$ npm install -D pug
Una vez hecho, añadimos el atributo lang="pug"
en la etiqueta <template>
. El siguiente ejemplo sería el equivalente a la plantilla <template>
que escribimos en el código HTML anterior:
<template lang="pug">
.container
h1 Mi App.vue principal!
</template>
Con esto estaremos preparados para escribir código Pug (antiguamente llamado Jade).
Más adelante hablaremos en mayor profundidad sobre los templates de Vue, su etiquetado dinámico (que permite bucles, condicionales y otras estructuras) y sus directivas.
Los scripts de Vue (Javascript)
Una de las partes más interesantes de los archivos .vue
es la parte de Javascript, donde creamos una etiqueta <script>
que contendrá todo el Javascript de Vue relacionado con el componente.
La estructura mínima de esta parte es muy similar a lo que vemos a continuación:
<script>
export default {
name: "App"
}
</script>
Básicamente, lo que hacemos es exportar por defecto un objeto que son las opciones del componente Vue (Vue API Option). Como mínimo, este objeto de opciones tendrá la propiedad name
con el nombre que le hemos dado al componente. En este caso le he puesto App
porque estoy creando el fichero App.vue
. A medida que avancemos en nuestro componente, iremos añadiendo nuevas propiedades al objeto que irán ampliando su funcionalidad.
Por ejemplo, en el siguiente fragmento de código vemos como lo ampliamos para cargar otro componente externo llamado BaseButton.vue
que se encuentra en la carpeta components
:
<script>
import BaseButton from "./components/BaseButton.vue";
export default {
name: "App",
components: {
BaseButton
}
}
</script>
Lo primero sería importar el componente utilizando import
y haciendo referencia al archivo .vue
del componente. Observa que como estoy en el archivo App.vue
que se encuentra en la carpeta src
debo hacer referencia a la ruta desde donde estoy.
Es muy común encontrarse los imports utilizando una ruta que comienza por
@
, como por ejemplo,"@/components/BaseButton.vue"
. La@
es un alias que utiliza Vue para hacer referencia a la carpetasrc
. Esto permite «normalizar» las rutas para que sean más sencillas de escribir e interpretar.
Luego, en el objeto que exportamos, añadimos una propiedad components
que contendrá un objeto con todos los componentes que hayamos importado y queramos utilizar en nuestro archivo .vue
.
Una vez hecho esto, sólo tendríamos que modificar nuestro <template>
y añadir la etiqueta HTML del componente que hemos creado (en este caso <BaseButton>
):
<template>
<div>
<BaseButton />
</div>
</template>
Así se cargará todo el contenido de dicho componente de la misma forma que hemos hecho con el componente actual, lo que permite reutilizar código muy facilmente.
Ten en cuenta que estamos usando la API de objetos de Vue (la clásica, que se puede utilizar tanto en Vue 2 como en Vue 3). Más adelante abordaremos la API de composición que se introduce en Vue 3 e indicada para hacer componentes mucho más escalables y reutilizables.
Usando TypeScript con Vue
En muchos casos nos podría interesar utilizar TypeScript en lugar de Javascript en nuestros componentes .vue
. En primer lugar, añadiremos el atributo lang="ts"
a nuestra etiqueta <script>
, indicando que utilizaremos TypeScript.
En Vue 3, sólo tendremos que añadir lo siguiente:
<script lang="ts">
import { defineComponent } from "vue";
import BaseButton from "./components/BaseButton.vue";
export default defineComponent({
name: "App",
components: {
BaseButton
}
});
</script>
A través de defineComponent
, envolvemos el objeto de opciones de Vue, haciendo la sintaxis mucho más sencilla y parecida a lo que hacemos con Vue y Javascript vanilla.
Más adelante hablaremos en mayor profundidad sobre los componentes de Vue y sus diferentes APIs, entre las que se encuentran la API de objetos y la API de composición.
Los estilos de Vue (CSS)
Por último (pero no por ello menos importante) tenemos la etiqueta <style>
de los archivos .vue
. En ella podremos indicar los estilos CSS implicados con el componente en cuestión, por lo que serán mucho más fáciles de encontrar y mantener con el tiempo.
En el caso de App.vue
tendremos una suerte de estilos globales o generales, que no tienen cabida en otros componentes, pero habitualmente en la etiqueta <style>
solo tendremos los estilos CSS relacionados con el componente en cuestión y sus partes.
<style>
.container {
background: steelblue;
}
</style>
Soporte de preprocesadores
Es posible utilizar el atributo lang
para indicar que lo que escribimos en la etiqueta no es CSS nativo sino transformado con algún tipo de preprocesador, como por ejemplo: sass
, scss
, less
o stylus
.
<style lang="scss" scoped>
$color: steelblue;
.container {
background: $color;
}
</style>
Recuerda que para que esto funcione correctamente, primero tendrás que instalar la dependencia con NPM, utilizando el siguiente comando: npm install sass
.
Si no se indica ningún atributo
lang
, solo podrás escribir en CSS nativo, o en PostCSS si incluyes un fichero de configuraciónpostcss.config.js
en el raíz del proyecto.
Límite de alcance CSS con scoped
Algo muy interesante en Vue es que podemos añadir el atributo scoped
a la etiqueta <style>
y estaremos definiendo que los estilos CSS de la etiqueta sólo se aplicarán al componente, por lo que si tenemos estilos con las mismas clases (por ejemplo) en otros componentes, no les afectarán.
Por otro lado, también se puede añadir el atributo module
en la etiqueta <style>
(incluso tener múltiples etiquetas por componente) para crear un módulo CSS. Esto nos permitirá hacer referencia a estilos CSS desde Javascript de una forma directa y sencilla. Profundizaremos en ello más adelante.
En próximos capítulos hablaremos en mayor profundidad sobre los estilos CSS en componentes de Vue, así como ver varios ejemplos de sus características particulares.