Menu
Foto por: Casey Horner

Hasta el infinito y más allá con Nuxt y TypeScript

Author image

Escrito por

Juan Manuel Castillo

Continuando con el artículo que ya escribimos sobre Vue supervitaminado con TypeScript, ahora es el turno de Nuxt.

Si no sabes que es Nuxt o no lo utilizas, te está faltando tiempo para hacerlo. Según su página oficial se define como:

“Nuxt is a progressive framework based on Vue.js to create modern web applications. It is based on Vue.js official libraries (vue, vue-router and vuex) and powerful development tools (webpack, Babel and PostCSS). Nuxt goal is to make web development powerful and performant with a great developer experience in mind.”

Si te has quedado igual no te preocupes, lo importante es que Nuxt habilita el renderizado en el lado del servidor (SSR) de nuestras SPA's con Vue y agiliza o simplifica el flujo de trabajo en diversos aspectos con plugins, creación mágica de rutas, i18n, etc.

Entre las posibilidades de Nuxt, la que puede considerarse que marca la diferencia es la habilidad de indexado del contenido por parte de los buscadores como si se hubiera desarrollado con WordPress, Drupal, Nest u otra pieza de software usada comúnmente para crear webs. Esto es importante ya que nos permite crear SPA's que tengan visibilidad en Internet, primordial si esta puede ser encontrada por el cliente final en buscadores como por ejemplo un marketplace de viviendas.

Esto que anteriormente era una quimera ahora abre un abanico de posibilidades muy interesante, especialmente para el área de negocio. Abre la opción de plantear soluciones con un stack tecnológico moderno que usen arquitecturas desacopladas.

Un ejemplo muy cercano es nuestra propia página (incluyendo este blog) el cual se ha desarrollado, entre otras tecnologías, con Nuxt. Si te interesa el tema, ya escribimos el artículo Cómo montamos este blog Nuxt y markdown.

En este contexto, vamos a ponernos manos a la obra para integrar TypeScript con un proyecto básico en Nuxt.

Instalación

Si quieres comenzar a usar TypeScript, debes saber que sólo podrás hacerlo si usas la versión 2.10 o posterior de Nuxt. Si usas una versión anterior, actualiza Nuxt pero ¡ojo!, en la versión 2.9 hubo cambios en la organización de paquetes por lo que debes migrar el código.

Teniendo una versión de Nuxt válida con la que trabajar, podemos pasar a instalar las dependencias que necesitaremos. Recuerda que sólo las necesitamos durante el desarrollo.

npm install @nuxt/typescript-build --save-dev

Como dependencia, incluye @nuxt/types que contiene las definiciones de tipos de datos para TypeScript.

Especificamos en la configuración de Nuxt nuestro builder.

// nuxt.config.js
export default {
  buildModules: ['@nuxt/typescript-build']
}

Y tal y como ya hicimos para configurar Vue con TypeScript creamos nuestro fichero de configuración para éste.

// tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "lib": [
      "esnext",
      "esnext.asynciterable",
      "dom"
    ],
    "esModuleInterop": true,
    "allowJs": true,
    "sourceMap": true,
    "strict": true,
    "noEmit": true,
    "baseUrl": ".",
    "paths": {
      "~/*": [
        "./*"
      ],
      "@/*": [
        "./*"
      ]
    },
    "types": [
      "@types/node",
      "@nuxt/types"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}

Estas son algunas de las opciones, si quieres profundizar, tienes todas las opciones del compilador disponibles en el anterior enlace.

Para poder usar TypeScript en la configuración de Nuxt (nuxt.config.js), módulos locales y serverMiddlewares necesitamos instalar el paquete (opcional) @nuxt/typescript-runtime.

npm install @nuxt/typescript-runtime

Una vez instalado debemos modificar los comandos de npm del proyecto (package.json) para que en lugar de llamar a nuxt lo hagan a nuxt-ts.

Si usas Nuxt 2.5 o posterior no necesitarás nuxt-ts, sólo es necesario para versiones anteriores por lo que teniendo instalado el paquete node-js y podrás escribir el código directamente en TypeScript.

{
  ...
  "scripts": {
    "dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server",
    "build": "nuxt-ts build",
    "start": "cross-env NODE_ENV=production node server/index.js",
    "generate": "nuxt-ts generate",
    "lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
  },
  "dependencies": {
    ...
  },
  "devDependencies": {
    ...
  }
}

Componente Hola mundo

Pongamos en marcha un ejemplo muy básico para usar TypeScript con Vue. En la carpeta de nuestro ejemplo con un fichero package.json inicializado y sin paquetes procedemos a instalar nuxt, @nuxt/typescript-runtime y @nuxt/typescript-build

npm install nuxt @nuxt/typescript-runtime

npm install @nuxt/typescript-build --save-dev

Una vez instalados dichos paquetes, podemos modificar el fichero package.json para que los comandos hagan uso de nuxt-ts en lugar de nuxt.

{
  ...
  "scripts": {
    "dev": "nuxt-ts",
    "build": "nuxt-ts build",
    "start": "nuxt-ts start",
    "generate": "nuxt-ts generate"
  },
  "dependencies": {
    "nuxt": "^2.10.2",
    "@nuxt/typescript-runtime": "^0.2.2"
  },
  "devDependencies": {
    "@nuxt/typescript-build": "^0.3.2"
  }
}

Y creamos el fichero de configuración de TypeScript tsconfig.json como ya es constumbre.

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "lib": [
      "esnext",
      "esnext.asynciterable",
      "dom"
    ],
    "esModuleInterop": true,
    "allowJs": true,
    "sourceMap": true,
    "strict": true,
    "noEmit": true,
    "baseUrl": ".",
    "paths": {
      "~/*": [
        "./*"
      ],
      "@/*": [
        "./*"
      ]
    },
    "types": [
      "@nuxt/types"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}

Creamos el fichero de configuración de Nuxt nuxt.config.js.

export default {
  modules: ['@nuxt/typescript-build']
}

Y por último creamos el template pages/index.vue que renderizará nuestra página con el mensaje ¡Hola mundo!

<template>
    <div>
        <h1>
        {{ message }}
        </h1>
    </div>
</template>

<script lang="ts">
    import Vue from 'vue'

    export default Vue.extend({
        data () {
            const message: string = 'Hola mundo'

            return {
                message
            }
        }
    })
</script>

Recuerda especificar en el lenguaje del script que estamos utilizando en la etiqueta script.

...
<script lang="ts">
...
</script>
...

Con esta ya podemos proceder a ejecutar el proyecto como haríamos normalmente con Vue o Nuxt.

npm run dev

Con este ejemplo, hemos montado un componente Hola Mundo muy rápido.

Middlewares

Si lo que necesitas es crear un Middleware no te preocupes, puedes usar TypeScript también con ellos. Un ejemplo básico sería como el que sigue.

import { Middleware } from '@nuxt/types'

const authMiddleware: Middleware = (context) => {
  // do something amazing
}

export default authMiddleware

Según el código anterior, entre los tipos que nos aporta TypeScript para Nuxt, únicamente hemos importado el tipo Middleware de y lo hemos tipado con dicha clase. El código que será acorde a la sintaxis normal de TypeScript.

Si el middleware es en el lado del servidor el código prácticamente no cambiaría, sería tal y como sigue.

import { ServerMiddleware } from '@nuxt/types'

const filterRequestMiddleware: ServerMiddleware = function (req, res, next) {
  // do something with: req, res, next
}

export default filterRequestMiddleware

Atentiendo nuevamente al código anterior, las únicas diferencias son que ahora importamos y usamos ServerMiddleware y que nuestro middleware en lugar de recibir el contexto recibirá req, res y next (si has tocado Node te será familiar).

Plugins

Si queremos inyectar plugins en nuestros componentes Vue, podríamos hacerlo de la siguiente manera.

declare module 'vue/types/vue' {
  interface Vue {
    $saySomething(message: string): void
  }
}

Vue.prototype.$saySomething = (message: string) => {
    alert(message)
}

Y lo usamos como cualquier otro plugin en Vue.

<template>
  ...
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  mounted () {
    this.$saySomething('Hello world!!')
  }
})
</script>

Si por el contrario, necesitamos inyectar nuestro plugin en el contexto cambiaría un poco con respecto al anterior código.

import { Plugin } from '@nuxt/types'

declare module '@nuxt/types' {
  interface Context {
    $saySomething(message: string): void
  }
}

const saySomethingPlugin: Plugin = (context) => {

  context.$saySomething = (message: string) => {
    alert(message)
  }

}

export default saySomethingPlugin

Como puedes comprobar, hemos importado el Plugin y creado un plugin tipado, el cual recibe el contexto. Aquí ya podemos hacer lo que necesitemos, por ejemplo, inyectar nuestra función saySomething.

Ya tenemos inyectado el plugin en el contexto, ahora podemos usarlo por ejemplo en el método asyncData.

<template>
  ...
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  ...
  asyncData (context) {
    context.$saySomething('Hello world!')
  }
  ...
})
</script>

Store

Normalmente usarás Vuex junto con Vue. Para estos casos, puedes hacerlo mediante clases o con Vanilla. Usaremos un ejemplo con esta última opción ya que de hacerlo basándonos en clases tiene algo más de complicación y necesitarías el paquete vuex-module-decorators, dejamos el uso de decorators y Nuxt para un futuro post.

Dicho esto, un ejemplo de fichero /store/index.ts sería:

import { GetterTree, ActionTree, MutationTree } from 'vuex' \ \ export const state = () => ({ \ title: 'Example item list!' as String,

itemList: [] as String[] \ }) \ \ export type RootState = ReturnType

export const mutations: MutationTree = { \ CHANGE_TITLE: (state, newTitle: string) => {

state.title = newTitle

} \ } \ \ export const getters: GetterTree = { \ title: state => state.title, \ } \ \ export const actions: ActionTree = { \ ClearDuplicateItems({ commit }) { \ let uniqueItemList = [...Array.from(new Set(itemList))]

itemList = uniqueItemList \ }, \ }

El uso del store sería exactamente igual que hasta ahora.

<template>
  ...
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  mounted () {
    console.info(this.$store.title)
  }
})
</script>

Es importante tener en cuenta que $store no está tipado ya que Vuex no nos ofrece esa opción, puedes tiparlo en el momento de usarlo tú mismo (no te lo recomiendo) o usar un paquete que tipa $store adecuadamente llamado nuxt-typed-vuex.

Conclusiones

Como puedes comprobar, a estas alturas no es una excusa desarrollar con JavaScript sin tipar tu código, como ya vimos en Vue supervitaminado con TypeScript y en este artículo, ya tenemos muchas opciones para integrar TypeScript y aprovechar todas las mejoras que nos ofrece.

Si aún así no te animas al no conocer TypeScript es cuestión de dedicarle un poco más de tiempo únicamente, 5 minutos para un onboarding rápido o si prefieres directamente ver la documentación puedes hacerlo con el Handbook comenzando por los tipos básicos. También puedes esperar a que escriba un artículo al respecto ;).

Sin más, espero que la lectura te haya sido amena y de utilidad… al menos que haya despertado el gusanillo por mejorar.

Referencias

Nuxt TypeScript

Nuxt: The context

TypeScript