Skip to content
En esta página

Vue.js

Mi primer intento con frameworks: React

React es el framework de frontend más conocido y, por lejos, el más usado. Es por eso que suele ser la primera elección cuando un desarrollador web está empezando su camino de aprendizaje, o al menos así lo fue en mi caso.

Al principio React no parecía tan difícil: cada componente es una función que retorna una porción de HTML (o, mejor, dicho de JSX) que se renderiza en la vista con los valores que le pasemos entre llaves:

js
import React, { useState } from 'react'

const AprenderFramework = () => {
  const [framework, setFramework] = useState('React')
  return (
    <h1>Aprendiendo a usar {framework}</h1>
  )
}
export default AprenderFramework

useState() es una función (o, mejor dicho, un hook) que React usa internamente para manejar el estado del componente. El estado son los datos que, al cambiar, disparan un nuevo renderizado y actualizan la vista. Para eso hay que usar un array desestructurado (en este caso, [framework, setFramework]) en el que el primer término es el estado y el segundo un setter que cambia ese estado, mientras que el parámetro que toma useState() es el valor inicial del estado.

Hasta ahí todo bien 👍️ De hecho esto se parece a los template literals de JavaScript nativo:

js
const aprenderFramework = framework => {
  return `
    <h1>Aprendiendo a usar ${framework}</h1>
  `
}
root.innerHTML = aprenderFramework('Vanilla JavaScript 😅️')

Pero a medida que uno avanza en el aprendizaje de React uno empieza a encontrarse con cosas como esta:

ts
import React, {
  useCallback,
  useEffect,
  useLayoutEffect,
  useReducer,
  useRef,
  useState,
  Dispatch,
  Reducer,
  ReducerState,
  ReducerAction,
} from 'react'

import { useAbortSignal } from './useAbortSignal';
const useIsomorphicLayoutEffect = (
  typeof window !== 'undefined' && 
  !/ServerSideRendering/.test(window.navigator && 
  window.navigator.userAgent)
) ? useLayoutEffect : useEffect;
export function useReducerAsync<R extends Reducer<any, any>, I,
  AsyncAction extends { type: string },
  ExportAction extends AsyncAction | ReducerAction<R> = AsyncAction | ReducerAction<R>
  >( reducer: R,
     initializerArg: I | ReducerState<R>,
     initializer: unknown,
     asyncActionHandlers?: AsyncActionHandlers<R, AsyncAction>,
): [ReducerState<R>, Dispatch<ExportAction>] {
  const signal = useAbortSignal();
  const handlers = (asyncActionHandlers || initializer) as AsyncActionHandlers<R, AsyncAction>;
  const [state, dispatch] = useReducer(
    reducer,
    initializerArg as any,
    asyncActionHandlers && initializer as any,
  );
  const lastState = useRef(state);
  useIsomorphicLayoutEffect(() => {
    lastState.current = state;
  }, [state]);
  const getState = useCallback((() => lastState.current), []);
  const wrappedDispatch = useCallback((action: AsyncAction | ReducerAction<R>) => {
    const { type } = (action || {}) as { type?: AsyncAction['type'] };
    const handler = (
      (type && handlers[type]) || null
    ) as (typeof action extends AsyncAction ? (s: {
      dispatch: Dispatch<AsyncAction | ReducerAction<R>>;
      getState: () => ReducerState<R>;
      signal: AbortSignal;
    }) => (a: typeof action) => Promise<void> : null);
    if (handler) {
      handler({ dispatch: wrappedDispatch, getState, signal })(action as AsyncAction);
    } else {
      dispatch(action as ReducerAction<R>);
    }
  }, [handlers, getState, signal]);
  return [state, wrappedDispatch];
}

Admito que el ejemplo es un poco tramposo porque TypeScript le agrega un nivel de complejidad adicional al código. Pero el uso de TS en React es cada vez más frecuente y es algo que se espera que un desarrollador de React pueda manejar.

Y aunque es cierto que en Vue y en Svelte también se usa mucho TypeScript, en el caso de React el uso de TS es especialmente agobiante porque React ya de por sí es complejo.

Segundo intento: Vue.js

Luego de mi intento frustrado con React probé con Vue y fue un gran alivio 🥵️

En Vue todo está más ordenado. La vista (<template>) y la lógica (<script>) están claramente separadas y no confusamente mezcladas como en React:

html
<template>

  <h1>Aprendiendo a usar {{ framework }}</h1>

</template>

<script>
export default {

  data: () => ({ 
    framework: 'Vue'
  })
}
</script>

Incluso en componentes más complejos la lógica de Vue es mucho más clara:

html
<template>
  <div>
    <h1>Lista de tareas</h1>
    <div>
      <InputTodo
        v-model="inputTodo" 
        @add-todo= "addTodo"
      />
      <ul>     
        <TodoItem
          v-for="(todo, index) in todos" :key="index"
          :todo="todo.text"
          :completed="todo.completed"
          :index="index"
          @update-todo="updateTodo"
          @delete-todo="deleteTodo"
        />
      </ul>
    </div>
  </div>
</template>

<script>

import InputTodo from '@/components/InputTodo.vue'
import TodoItem from '@/components/TodoItem.vue'

export default {

  components: {
    InputTodo, TodoItem
  },
  
  data: () => ({
    inputTodo: '', 
    todos: []
  }),

  methods: {
    addTodo() {
      if (this.inputTodo) {
        this.todos.push({
          text: this.inputTodo, 
          completed: false
        })
        this.inputTodo = ''  
      }
    },
    updateTodo({index, newTodo, completed}) {
      if (newTodo) {
        this.todos.splice(index, 1, {
          text: newTodo,
          completed
        })
      } else {
        this.deleteTodo(index)
      }
    },
    deleteTodo(index) {
      this.todos.splice(index, 1)
    }
  }
}
</script>

Si es la primera vez que ven código de Vue esto les puede parecer confuso. Pero nada más compárenlo con el ejemplo de React de acá arriba. Incluso sin saber nada de Vue uno puede hacerse más o menos una idea de qué es cada cosa:

components: Los componentes hijos que voy a usar en este componente.
data: Los datos que generan reactividad, o sea, que la vista se actualice cuando cambian.
methods: Los métodos para cambiar los datos (addTodo, updateTodo, deleteTodo).

Ni siquiera es necesario usar métodos adicionales para evitar renderizaciones innecesarias en los componentes hijos (como el hook useCallback de React), en Vue sólo se actualiza lo que cambia.

Y tampoco es necesario usar métodos complejos para manejar el estado del componente, como el hook useReducer() de React. En Vue el estado son simplemente las propiedades del objeto data.

De todas formas, React tiene algunos puntos a favor. Por ejemplo, le exige al programador tener un conocimiento más profundo de JavaScript. En React el código JavaScript está más "al desnudo". En cambio en Vue hay mucha "magia" que ocurre por detrás de la escena, fuera de la vista del programador.

Pero aprender React como primer framework de frontend puede ser una pesadilla (o por lo menos así lo fue para mí 🥵️).

Luego, lo que aprendí en Vue me ayudó a entender mejor React porque, aunque la implementación de uno y otro es muy distinta, los problemas que ambos intentan resolver son los mismos:

  1. Cómo generar reactividad.

  2. Cómo actualizar el DOM de manera eficiente (virtual DOM).

  3. Cómo descomponer la interfaz de usuario en componentes individuales pero interconectados.

  4. Cómo navegar entre distintas páginas de la aplicación sin el costo de llamadas adicionales al servidor (SPA routing).

  5. Cómo manejar el estado global de la aplicación: con Redux en React o con Vuex (o Pinia) en Vue.

Y de esta forma pude volver a intentar con React sin tanta frustración.

Mi tercer framework: Svelte

En los últimos años empecé a interesarme por Svelte, un framework super liviano, rápido y relativamente fácil de aprender (incluso más fácil que Vue).

Pero a pesar de lo mucho que me gusta Svelte tengo que admitir que Svelte se aleja demasiado de la forma normal en que funciona JavaScript y eso puede generar confusión en quienes estén empezando su camino en frontend. Svelte tiene tantas cosas particulares que lo diferencian de cualquier otro framework que, muy probablemente, si hubiese empezado por Svelte en lugar de Vue, intentar luego aprender React me habría resultado imposible.

Así que si Vue es el primer framework de frontend que están aprendiendo, van por buen camino 🙂️