Realmente escribiendo vue

Logotipo


inb4: Esta no es otra "configuración" de un nuevo proyecto con el tutorial Vue y TypeScript. ¡Profundicemos en temas más complejos!


typescript es asombroso. Vue es asombroso. Sin duda, mucha gente intenta agruparlos . Pero, debido a diferentes razones, es difícil escribir realmente su aplicación Vue . Veamos cuáles son los problemas y qué se puede hacer para resolverlos (o al menos minimizar el impacto).


TLDR


Tenemos esta maravillosa plantilla con Nuxt , Vue , Vuex y jest completamente tipeados. Simplemente instálelo y todo estará cubierto para usted. Ve a los documentos para obtener más información.


Y como dije, no voy a guiarte a través de la configuración básica por tres razones:


  1. Hay muchos tutoriales existentes al respecto
  2. Hay muchas herramientas para comenzar con un solo clic, como Nuxt y vue-cli con el complemento typescript
  3. Ya tenemos wemake-vue-template donde cada parte de la configuración de la que voy a hablar ya está cubierta

Tipificación de componentes


La primera expectativa rota cuando comienza a trabajar con Vue y el typescript y después de haber escrito los componentes de su clase es que las etiquetas <template> y <style> todavía no se escriben. Déjame mostrarte un ejemplo:


 <template> <h1 :class="$style.headr"> Hello, {{ usr }}! </h1> </template> <script lang="ts"> import Vue from 'vue' import Component from 'vue-class-component' import { Prop } from 'vue-property-decorator' @Component({}) export default class HelloComponent extends Vue { @Prop() user!: string } </script> <style module> .header { /* ... */ } </style> 

He hecho dos errores tipográficos aquí: {{ usr }} lugar de {{ user }} y $style.headr lugar de $style.header . ¿El typescript me salvará de estos errores? No, no lo hará.


¿Qué se puede hacer para solucionarlo? Bueno, hay varios hacks.


Escribiendo la plantilla


Se puede usar Vetur con la opción vetur.experimental.templateInterpolationService para verificar sus plantillas. Sí, esto es solo una verificación basada en el editor y todavía no se puede usar dentro del CI. Pero, el equipo de Vetur está trabajando duro para proporcionar una CLI que permita esto. Rastree el problema original en caso de que esté interesado.


vetur


La segunda opción son dos pruebas de instantáneas de escritura con jest . Capturará muchos errores basados ​​en plantillas. Y es bastante barato en el mantenimiento.


Por lo tanto, la combinación de estas dos herramientas le brinda una agradable experiencia de desarrollador con comentarios rápidos y una forma confiable de detectar errores dentro del CI.


Estilos de escritura


Escribir css-module s también está cubierto por varias herramientas externas:



La idea principal de estas herramientas es buscar css-module y luego crear archivos de declaración .d.ts partir de ellos. Entonces sus estilos estarán completamente escritos. Todavía no está implementado para Nuxt o Vue , pero puede Nuxt este problema para avanzar.


Sin embargo, personalmente no uso ninguna de estas herramientas en mis proyectos. Pueden ser útiles para proyectos con bases de código grandes y muchos estilos, pero estoy bien con solo instantáneas.


Las guías de estilo con pruebas de regresión visual también ayudan mucho. @storybook/addon-storyshots es un buen ejemplo de esta técnica.


Vuex


La próxima gran cosa es Vuex . Tiene cierta complejidad de diseño incorporada para escribir:


 const result: Promise<number> = this.$store.dispatch('action_name', { payload: 1 }) 

El problema es que 'action_name' podría no existir, tomar otros argumentos o devolver un tipo diferente. Eso no es algo que esperes para una aplicación completamente tipada.


¿Cuáles son las soluciones existentes?


clase vuex


vuex-class es un conjunto de decoradores para permitir un fácil acceso desde sus componentes basados ​​en clase a los Vuex internos de Vuex .


Pero no está tipificado como seguro ya que no puede interferir con los tipos de estado, captadores, mutaciones y acciones.


clase vuex


Por supuesto, puede anotar manualmente tipos de propiedades.


clase vuex anotada


Pero, ¿qué vas a hacer cuando cambie el tipo real de tu estado, captadores, mutaciones o acciones? Tendrá una discrepancia de tipo oculto.


vuex-simple


Ahí es donde vuex-simple nos ayuda. En realidad, ofrece una forma completamente diferente de escribir su código Vuex y eso es lo que lo hace seguro. Echemos un vistazo:


 import { Action, Mutation, State, Getter } from 'vuex-simple' class MyStore { // State @State() public comments: CommentType[] = [] // Getters @Getter() public get hasComments (): boolean { return Boolean(this.comments && this.comments.length > 0) } // Mutations @Mutation() public setComments (payload: CommentType[]): void { this.comments = updatedComments } // Actions @Action() public async fetchComments (): Promise<CommentType[]> { // Calling some API: const commentsList = await api.fetchComments() this.setComments(commentsList) // typed mutation return commentsList } } 

Más tarde, este módulo escrito puede registrarse dentro de su Vuex la Vuex manera:


 import Vue from 'vue' import Vuex from 'vuex' import { createVuexStore } from 'vuex-simple' import { MyStore } from './store' Vue.use(Vuex) // Creates our typed module instance: const instance = new MyStore() // Returns valid Vuex.Store instance: export default createVuexStore(instance) 

Ahora tenemos una instancia de Vuex.Store 100% nativa y toda la información de tipo incluida. Para usar esta tienda escrita en el componente, podemos escribir solo una línea de código:


 import Vue from 'vue' import Component from 'nuxt-class-component' import { useStore } from 'vuex-simple' import MyStore from './store' @Component({}) export default class MyComponent extends Vue { // That's all we need! typedStore: MyStore = useStore(this.$store) // Demo: will be typed as `Comment[]`: comments = typedStore.comments } 

Ahora hemos escrito Vuex que se puede usar de forma segura dentro de nuestro proyecto.
Cuando cambiamos algo dentro de la definición de nuestra tienda, se refleja automáticamente en los componentes que usan esta tienda. Si algo falla, lo sabemos lo antes posible.


También hay diferentes bibliotecas que hacen lo mismo pero tienen API diferentes. Elige lo que más te convenga.


Llamadas a la API


Cuando tenemos Vuex correctamente configurado, necesitamos llenarlo con datos.
Echemos un vistazo a nuestra definición de acción nuevamente:


 @Action() public async fetchComments (): Promise<CommentType[]> { // Calling some API: const commentsList = await api.fetchComments() // ... return commentsList } 

¿Cómo podemos saber que realmente devolverá una lista de CommentType y no un solo number o un montón de instancias de AuthorType ?


No podemos controlar el servidor. Y el servidor podría realmente romper el contrato. O simplemente podemos pasar la instancia de api incorrecta, hacer un error tipográfico en la URL, o lo que sea.


¿Cómo podemos estar seguros? ¡Podemos usar la escritura en tiempo de ejecución! Déjame presentarte io-ts :


 import * as ts from 'io-ts' export const Comment = ts.type({ 'id': ts.number, 'body': ts.string, 'email': ts.string, }) // Static TypeScript type, that can be used as a regular `type`: export type CommentType = ts.TypeOf<typeof Comment> 

Que hacemos aqui


  1. Definimos una instancia de ts.type con campos que necesitamos verificar en tiempo de ejecución cuando recibimos una respuesta del servidor
  2. Definimos un tipo estático para ser utilizado en la anotación sin ninguna placa adicional

Y luego podemos usarlo en nuestras llamadas a la api :


 import * as ts from 'io-ts' import * as tPromise from 'io-ts-promise' public async fetchComments (): Promise<CommentType[]> { const response = await axios.get('comments') return tPromise.decode(ts.array(Comment), response.data) } 

Con la ayuda de io-ts-promise , podemos devolver una Promise en un estado fallido si la respuesta del servidor no coincide con un tipo ts.array(Comment) . Realmente funciona como una validación.


 fetchComments() .then((data) => /* ... */ .catch(/* Happens with both request failure and incorrect response type */) 

Además, la anotación de tipo de retorno está sincronizada con el método .decode . Y no puedes poner tonterías al azar allí:


io-ts


Con la combinación de tiempo de ejecución y comprobaciones estáticas, podemos estar seguros de que nuestras solicitudes no fallarán debido a la falta de coincidencia de tipos.
Pero, para estar 100% seguro de que todo funciona, recomendaría usar pruebas basadas en contratos: eche un vistazo al pact como ejemplo. Y supervise su aplicación con Sentry .


Enrutador Vue


El siguiente problema es que this.$router.push({ name: 'wrong!' }) No funciona de la manera que queremos.


Diría que sería ideal que el compilador nos advirtiera que nos estamos dirigiendo a la dirección incorrecta y que esta ruta no existe.
Pero no es posible. Y no se puede hacer mucho: hay muchas rutas dinámicas, expresiones regulares, fallbacks, permisos, etc. que eventualmente pueden romperse. La única opción es probar cada una de this.$router llamadas this.$router en su aplicación.


vue-test-utils


Hablando de pruebas, no tengo excusas para no mencionar @vue/test-utils que también tiene algunos problemas con la escritura.


Cuando intentemos probar nuestro nuevo componente brillante con la propiedad typedStore , descubriremos que en realidad no podemos hacerlo de acuerdo con el typescript :


vue-test-utils


¿Por qué sucede esto? Ocurre porque la llamada mount() no sabe nada sobre el tipo de su componente, porque todos los componentes tienen un tipo VueConstructor<Vue> de forma predeterminada:


vue-constructor


De ahí provienen todos los problemas. Que se puede hacer
Puede usar vuetype para producir los YouComponent.vue.d.ts YouComponent.vue.d.ts que le indicarán a sus pruebas el tipo exacto del componente montado.


También puede realizar un seguimiento de este problema para el progreso.


Pero no me gusta esta idea. Estas son pruebas, pueden fallar. No es gran cosa.
Es por eso que me (wrapper.vm as any).whatever a (wrapper.vm as any).whatever enfoque. Esto me ahorra bastante tiempo para escribir pruebas. Pero estropea un poco la experiencia del desarrollador.


Toma tu propia decisión aquí:


  • Usa vuetype todo el tiempo
  • Aplíquelo parcialmente a los componentes más importantes con la mayor cantidad de pruebas y actualícelo regularmente
  • Use any como reserva

Conclusión


El nivel promedio de soporte de typescript en el ecosistema Vue aumentó en los últimos años:


  • Nuxt introdujo por primera vez nuxt-ts y ahora envía las compilaciones ts por defecto
  • Vue@3 tendrá soporte mejorado de typescript
  • Más aplicaciones y complementos de terceros proporcionarán definiciones de tipo

Pero, la producción está lista en este momento. ¡Estas son solo cosas para mejorar! Escribir código Vue seguridad de tipo realmente mejora su experiencia de desarrollador y le permite concentrarse en las cosas importantes mientras deja el trabajo pesado al compilador.


¿Cuáles son tus hacks y herramientas favoritas para escribir aplicaciones Vue ? Discutámoslo en la sección de comentarios.

Source: https://habr.com/ru/post/458632/


All Articles