¿Qué son los archivos .vue?

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.

Componentes SFC Vue

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).

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.

Por esta razón, en Vue por defecto 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 los 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="./Component.js"></script>
<style src="./Component.css"></style>

Observa que en este caso tendríamos tres archivos diferentes por componente: Component.vue donde tendríamos la etiqueta <template> con el contenido HTML del mismo y las referencias a los otros dos archivos: Component.js para el Javascript y Component.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.

Pero de momento podemos añadir contenido HTML simple, del que ya conocemos:

<template>
  <div class="container">
    <h1>Mi App.vue principal</h1>
  </div>
</template>

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.

Soporte de plantillas Pug

Además, si lo preferimos, podemos utilizar el sistema de plantillas Javascript PugJS en lugar de escribir HTML directamente, lo que nos permite escribir etiquetas de forma mucho menos verbose, y utilizar algunas características dinámicas.

Si lo queremos utilizar, necesitaremos instalar las siguientes dependencias de desarrollo:

$ npm install -D pug pug-plain-loader

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 Button.vue que se encuentra en la carpeta components:

<script>
import Button from "./components/Button.vue";

export default {
  name: "App",
  components: {
    Button
  }
}
</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/Button.vue". La @ es un alias que utiliza Vue para hacer referencia a la carpeta src. Esto permite «normalizar» las rutas para que sean parecidas siempre.

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 <Button>):

<template>
  <div>
    <Button />
  </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.

Si estamos utilizando Vue 2, el aspecto de nuestro componente será algo parecido a esto:

<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import Button from "./components/Button.vue";

@Component({
  components: {
    HelloWorld
  }
})
export default class App extends Vue {
  // Opciones de Vue 2
}
</script>

Como podemos ver, importamos un decorador Component desde vue-property-decorator y utilizamos una sintaxis basada en Clases, extendiendo desde la clase Vue que también importamos.

Esta sintaxis se simplifica bastante en Vue 3, donde solo es necesario hacer lo siguiente:

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  name: "App",
});
</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.

Soporte de preprocesadores

Además, es posible utilizar el atributo lang para indicar que lo que escribimos en la etiqueta no es CSS nativo sino procesado con algún tipo de preprocesador, como por ejemplo, PostCSS. Las opciones posibles son postcss, sass, scss, less o stylus.

<style lang="postcss" scoped>
  .container {
    background: steelblue;

    & .item {
      background: hotpink;
      color: white;
      padding: 5px;
    }
  }
</style>

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 otro componente, no afectarán como suele suceder en el CSS nativo global.

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.

Manz
Publicado por Manz

Docente, divulgador informático y freelance. Autor de Emezeta.com, es profesor en la Universidad de La Laguna y dirige el curso de Programación web FullStack y Diseño web FrontEnd de EOI en Tenerife (Canarias). En sus ratos libres, busca GIF de gatos en Internet.