Vue 3 Composition API: Ref ou Reativo





Agora, enquanto escrevo este artigo, estamos nos aproximando do lançamento do Vue 3. Na minha opinião, o mais interessante é observar como outros desenvolvedores o perceberão e o usarão. Tive a oportunidade de brincar com o Vue 3 nos últimos meses, mas sei que existem aqueles que não o fizeram.



A inovação mais significativa na nova versão é a API de composição. Ele fornece uma abordagem alternativa para a criação de componentes e é muito diferente da API de opções existente. Não é difícil para mim admitir que, quando o vi pela primeira vez, não o entendi. No entanto, à medida que foi aplicado, o significado começou a emergir. Você pode não estar reescrevendo todo o seu aplicativo usando a API de composição, mas este artigo lhe dará a oportunidade de pensar sobre como a criação e composição do componente funcionarão.







Fiz algumas apresentações recentemente e uma dúvida comum é quando uso Ref e quando uso Reactive para declarar uma propriedade reativa. Não obtive uma boa resposta e demorei algumas semanas a encontrar a resposta, e este artigo é o resultado da minha pesquisa.



Também gostaria de observar que o acima exposto é a minha opinião e, por favor, não pense que é necessário fazer “apenas desta forma e não de outra forma”. Pretendo usar Ref e Reactive dessa maneira até que alguém aconselhe o contrário ou até que eu mesmo descubra uma abordagem melhor. Acho que leva algum tempo para entender qualquer nova tecnologia, e então podem aparecer técnicas comprovadas.



Antes de continuar, suponho que você tenha se familiarizado pelo menos brevemente com a API de composição e entendido em que ela consiste. Este artigo enfoca as diferenças entre ref e reativo, não o mecanismo de composição da API.



Reatividade Vue 2



Para mantê-lo atualizado, vou dar uma olhada rápida em como criar propriedades reativas em um aplicativo Vue 2. Quando você deseja que o Vue 2 rastreie as alterações nas propriedades, você precisa declará-las no objeto retornado pela função de dados.



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  export default {
    data() {
      return {
        title: "Hello, Vue!"
      };
    }
  };
</script>


Sob o capô do Vue 2, Object.defineProperty () é chamado para cada propriedade para criar um getter e um setter para controlar as alterações. Esta é a explicação mais simples do processo e quero transmitir a ideia: não há mágica nisso. Você não pode criar propriedades reativas em qualquer lugar e esperar que o Vue rastreie as alterações nelas. Você precisa definir propriedades reativas na função de dados.



REF e REACTIVE



Ao trabalhar com a API de opções, precisamos seguir algumas regras ao declarar propriedades reativas, o mesmo com a API de composição. Você não pode simplesmente criar uma propriedade e esperar reatividade. No exemplo a seguir, declarei a propriedade title e a função setup () a retorna, disponibilizando-a para o modelo.



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  export default {
    setup() {
      let title = "Hello, Vue 3!";
      return { title };
    }
  };
</script>




Isso funcionará, mas a propriedade do título não será reativa. Essa. se alguém alterar o título, essas alterações NÃO serão refletidas na Câmara. Por exemplo, se você mudar o título após 5 segundos, o código abaixo NÃO mudará a página inicial.



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  export default {
    setup() {
      let title = "Hello, Vue 3!";

      setTimeout(() => {
        title = "THIS IS A NEW TITLE";
      }, 5000);

      return { title };
    }
  };
</script>




Podemos importar ref e usá-lo para tornar a propriedade reativa. Nos bastidores, o Vue 3 criará um Proxy .



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  import { ref } from "vue";

  export default {
    setup() {
      const title = ref("Hello, Vue 3!");

      setTimeout(() => {
        //   ,   .value ...
        //   
        title.value = "New Title";
      }, 5000);

      return { title };
    }
  };
</script>




Quero esclarecer que falando em Ref e Reativo, acho que há dois casos diferentes. O primeiro é criar um componente como no exemplo acima e você precisa de propriedades reativas. A segunda é quando você cria funções que permitem que a composição seja usada em componentes e funções. Discutiremos os dois cenários neste artigo.



REF



Ao criar propriedades de tipos simples, ref () é sua primeira escolha. Não é uma bala de prata, mas vale a pena começar. Deixe-me lembrá-lo de sete tipos primitivos em JavaScript:



  • Corda
  • Número
  • BigInt
  • boleano
  • Símbolo
  • Nulo
  • Indefinido




import { ref } from "vue";

export default {
  setup() {
    const title = ref("");
    const one = ref(1);
    const isValid = ref(true);
    const foo = ref(null);
  }
};




No exemplo anterior, nosso título é do tipo String, portanto, para tornar a propriedade reativa, selecionamos ref (). Se o código abaixo está causando algumas perguntas, não se preocupe, eu tive as mesmas perguntas.



import { ref } from "vue";

export default {
  setup() {
    const title = ref("Hello, Vue 3!");

    setTimeout(() => {
      title.value = "New Title";
    }, 5000);

    return { title };
  }
};




Por que estamos usando const se o título vai mudar? Por que não usar let? Se você imprimir o título no console, poderá esperar ver Hello, Vue 3 !, mas em vez disso, ele exibirá um objeto:



{_isRef: true}
value: (...)
_isRef: true
get value: ƒ value()
set value: ƒ value(newVal)
__proto__: Object




A função ref () receberá um argumento e retornará um objeto ref reativo e mutável. O objeto Ref possui uma propriedade, valor, que se refere ao argumento. Isso significa que se você quiser obter ou alterar o valor, terá que usar title.value, e como este é um objeto que não mudará, declarei que é const.



Chamando REF



A próxima pergunta é por que não chamamos title.value no modelo?



<template>
  <h1>{{ title }}</h1>
</template>




Quando Ref é retornado como uma propriedade no contexto de renderização (o objeto retornado pela função setup ()) e é referenciado no modelo, Ref retorna automaticamente o valor. Você não precisa adicionar .value no modelo.



As propriedades calculadas funcionam da mesma maneira, dentro da função setup (), refira-se a elas como .value.




REATIVO



Acabamos de ver como ref () pode ser usado para tornar reativas propriedades de tipos simples. E se quisermos criar um objeto reativo? Nós ainda poderíamos usar ref (), mas sob o capô o Vue usará reativo (), então vou ficar com reativo ().



Por outro lado, reactive () não funcionará com tipos primitivos. A função reactive () pega um objeto e retorna o proxy reativo do original. Isso é equivalente a .observable () no Vue 2, e o nome da função foi alterado para evitar confusão com os observáveis ​​no RxJS.



import { reactive } from "vue";

export default {
  setup() {
    const data = reactive({
      title: "Hello, Vue 3"
    });

    return { data };
  }
};




A principal diferença é como nos referimos ao objeto reativo no modelo. No exemplo anterior, data é um objeto que contém uma propriedade title. Você precisará se referir a ele no modelo como este - data.title:



<template>
  <h1>{{ data.title }}</h1>
</template>

<script>
  import { ref } from "vue";

  export default {
    setup() {
      const data = ref({
        title: "Hello, Vue 3"
      });

      return { data };
    }
  };
</script>




Diferença entre REF e REACTIVE em um componente



Com base no que discutimos, a resposta parece sugerir-se? Usamos ref () para tipos simples e reativo () para objetos. Quando comecei a criar componentes, descobri que nem sempre é esse o caso, e a documentação diz:



A diferença entre usar ref e reativo pode ser, até certo ponto, comparável a como você escreve lógica de programa padrão em JavaScript.




Eu ponderei essa frase e cheguei às seguintes conclusões. Conforme o aplicativo cresceu, obtive as seguintes propriedades:



export default {
  setup() {
    const title = ref("Hello, World!");
    const description = ref("");
    const content = ref("Hello world");
    const wordCount = computed(() => content.value.length);

    return { title, description, content, wordCount };
  }
};




Em JavaScript, eu entenderia que todas essas são propriedades da minha página. Nesse caso, eu os agruparia em um objeto de página, por que não fazer o mesmo agora.



<template>
  <div class="page">
    <h1>{{ page.title }}</h1>
    <p>{{ page.wordCount }}</p>
  </div>
</template>

<script>
  import { ref, computed, reactive } from "vue";

  export default {
    setup() {
      const page = reactive({
        title: "Hello, World!",
        description: "",
        content: "Hello world",
        wordCount: computed(() => page.content.length)
      });

      return { page };
    }
  };
</script>




Esta é minha abordagem para Ref ou Reativo, mas seria bom ter sua opinião. Você está fazendo o mesmo? Talvez esta não seja a abordagem certa? Por favor comente.



Lógica de composição



Você não pode errar ao usar ref () ou reativo () em seus componentes. Ambas as funções criarão dados reativos e, se você entender como acessá-los na função setup () e em modelos, não há problema.



Quando você começa a escrever funções de composição, precisa entender a diferença. Eu uso exemplos da documentação RFC, pois alguns ilustram bem as nuances.



Você precisa criar uma lógica para rastrear a posição do mouse. Você também deve ser capaz de usar essa mesma lógica em qualquer componente quando necessário. Você cria uma função de composição que rastreia as coordenadas xey e as retorna para o código do cliente.



import { ref, onMounted, onUnmounted } from "vue";

export function useMousePosition() {
  const x = ref(0);
  const y = ref(0);

  function update(e) {
    x.value = e.pageX;
    y.value = e.pageY;
  }

  onMounted(() => {
    window.addEventListener("mousemove", update);
  });

  onUnmounted(() => {
    window.removeEventListener("mousemove", update);
  });

  return { x, y };
}




Se você quiser usar essa lógica em um componente e chamar essa função, desestruture o objeto retornado e retorne as coordenadas xey para o modelo.



<template>
  <h1>Use Mouse Demo</h1>
  <p>x: {{ x }} | y: {{ y }}</p>
</template>

<script>
  import { useMousePosition } from "./use/useMousePosition";

  export default {
    setup() {
      const { x, y } = useMousePosition();
      return { x, y };
    }
  };
</script>




Isso funcionará, mas se, depois de olhar para a função, você decidir refatorar e retornar um objeto de posição em vez de x e y:



import { ref, onMounted, onUnmounted } from "vue";

export function useMousePosition() {
  const pos = {
    x: 0,
    y: 0
  };

  function update(e) {
    pos.x = e.pageX;
    pos.y = e.pageY;
  }

  // ...
}




O problema com essa abordagem é que o cliente da função de composição deve sempre ter uma referência ao objeto retornado para que a reatividade persista. Isso significa que não podemos desestruturar o objeto ou aplicar o operador de propagação:



//  
export default {
  setup() {
    //  !
    const { x, y } = useMousePosition();
    return {
      x,
      y
    };

    //  !
    return {
      ...useMousePosition()
    };

    //     
    //   `pos`  ,      x  y : `pos.x`  `pos.y`
    return {
      pos: useMousePosition()
    };
  }
};




Mas isso não significa que não possamos usar reativo neste caso. Existe uma função toRefs () que converte um objeto reativo em um objeto simples, cada propriedade da qual é o ref da propriedade correspondente do objeto original.



function useMousePosition() {
  const pos = reactive({
    x: 0,
    y: 0
  });

  // ...
  return toRefs(pos);
}

// x & y  ref!
const { x, y } = useMousePosition();




Portanto, existem alguns aspectos a serem considerados ao criar funções de composição. Se você entender como o código do cliente funciona com suas funções, tudo ficará bem.



TOTAL



Quando comecei a usar a API de composição, fiquei intrigado com a questão de quando aplicar ref () e quando reativo (). Talvez eu ainda não esteja fazendo certo e, até que alguém me diga isso, continuarei com essa abordagem. Espero ter ajudado a esclarecer alguns dos problemas e gostaríamos de ouvir seus comentários.



Boa codificação



All Articles