Mais leve e mais rápido - Um guia para o Svelte Framework

Publicados: 2022-03-11

Os aplicativos da Web estão se tornando mais populares a cada dia. Eles são um mundo em crescimento que as pessoas escolhem por sua simplicidade, velocidade e disponibilidade entre plataformas. Os aplicativos de página única (SPAs) tiveram um papel importante nesse processo. Frameworks como Angular, Vue.js e React ajudam os desenvolvedores a entregar a melhor experiência do usuário em um curto período, deixando o código suportável e extensível. Essas ferramentas permaneceram por muito tempo mais populares no campo, com muitas vantagens sobre os pacotes recém-criados. Parecia um oligopólio no mundo SPA. No entanto, um grupo de desenvolvedores voltados para o futuro visando esse mercado poderia entrar com um concorrente sério - Svelte.

Svelte é uma nova abordagem para construir interfaces de usuário. Vamos mergulhar e explorar o que o torna tão novo criando um formulário de login comum.

Arquitetura

O Svelte foi projetado para ser mais rápido do que qualquer outra biblioteca. Ele é alcançado mudando a etapa de carregamento de uma estrutura para a construção de um DOM virtual. Em vez de usar uma ferramenta durante o processo de execução, ela é compilada para Vanilla JS na fase de construção para que a aplicação não exija dependências para iniciar.

Esbelto Outras bibliotecas SPA (React, Vue.js, Angular, etc.)

1. Abra um site
2. Renderize a página usando JS puro

1. Abra um site
2. Aguarde até que o código para construir um DOM virtual seja carregado
3. Renderize a página usando a biblioteca

A tabela acima descreve por que a Svelte é uma vencedora absoluta em desempenho de startups. Isso não é obtido por qualquer tipo de otimização, mas usando o compilador JavaScript do navegador disponível em vez de um compilador lateral.

Instalação

A instalação Svelte é incrivelmente fácil, tornando seu uso muito agradável. O primeiro passo é baixar o template do projeto:

 npx degit sveltejs/template svelte-login-form

Completar o comando acima significa que temos um modelo de projeto Svelte. Está vazio no momento e os pacotes NPM necessários ainda não estão instalados. Vamos consertar isso.

 cd svelte-login-form npm install

Agora o aplicativo está pronto para iniciar usando o seguinte comando:

 npm run dev

Estrutura

Qualquer componente Svelte pode conter as seguintes seções:

  • Roteiro
  • Estilo
  • Modelo

Vejamos o exemplo no arquivo src/App.svelte .

 <script> export let name; </script> <style> h1 { color: purple; } </style> <h1>{name}</h1>

O código acima contém exatamente três seções:

  1. script tag, que é um bloco JavaScript opcional com as declarações de variáveis ​​e funções que devem ser usadas dentro do componente.

  2. style tag, que é outro bloco opcional. É muito parecido com uma tag de estilo HTML comum, exceto por uma diferença importante. As regras descritas dentro deste bloco têm como escopo apenas este componente. Aplicar um estilo a um elemento p não afetará todos os parágrafos da página. É fantástico, pois você não precisa criar nomes de classes e nunca substituirá acidentalmente outra regra.

  3. O último e único bloco necessário é um bloco de modelo - neste caso, uma tag h1 . É uma apresentação/visualização do seu componente. Ele está fortemente vinculado aos blocos de estilo e script, pois eles determinam como a visualização será estilizada e como ela se comportará.

Svelte é uma biblioteca que tenta trazer modularidade para o jogo front-end. Ele mantém essa modularidade não apenas na separação de diferentes componentes, mas também no isolamento da lógica, visão e modelo.

Voltando ao formulário de login que estamos construindo, vamos criar um novo arquivo LoginForm.svelte dentro da pasta src com o seguinte conteúdo:

 <style> form { background: #fff; padding: 50px; width: 250px; height: 400px; display: flex; flex-direction: column; justify-content: center; align-items: center; box-shadow: 0px 20px 14px 8px rgba(0, 0, 0, 0.58); } label { margin: 10px 0; align-self: flex-start; font-weight: 500; } input { border: none; border-bottom: 1px solid #ccc; margin-bottom: 20px; transition: all 300ms ease-in-out; width: 100%; } input:focus { outline: 0; border-bottom: 1px solid #666; } button { margin-top: 20px; background: black; color: white; padding: 10px 0; width: 200px; border-radius: 25px; text-transform: uppercase; font-weight: bold; cursor: pointer; transition: all 300ms ease-in-out; } button:hover { transform: translateY(-2.5px); box-shadow: 0px 1px 10px 0px rgba(0, 0, 0, 0.58); } h1 { margin: 10px 20px 30px 20px; font-size: 40px; } </style> <form> <h1></h1> <label>Email</label> <input name="email" placeholder="[email protected]" /> <label>Password</label> <input name="password" type="password" placeholder="password" /> <button type="submit">Log in </button> </form>

É um componente de estilo idiota que tornaremos mais inteligente mais tarde. Para ver este componente em nosso site devemos renderizá-lo dentro do componente raiz - App. Vamos editar o src/App.svelte para que fique assim:

 <script> import LoginForm from "./LoginForm.svelte"; </script> <style> section { height: 100vh; width: 100%; display: flex; justify-content: center; align-items: center; background: linear-gradient(to right, #cd76e2, #e358ab); } </style> <section> <LoginForm /> </section>

Se tudo foi feito corretamente e o aplicativo ainda está em execução, nosso formulário aparecerá em localhost:5000 . Vamos aumentar nossas habilidades Svelte tornando o formulário mais inteligente.

Tornando-se Stateful

Qualquer componente no Svelte pode ter seu estado. O estado é uma variável especial ou um grupo de variáveis ​​especiais que podem ser usadas dentro do template. Por que eu digo “especial”? Sempre que tal variável é alterada, o template é notificado sobre isso e renderiza o conteúdo com o estado mais recente. Isso permite que o aplicativo reaja às interações do usuário muito rapidamente.

Declararemos variáveis ​​de estado de e- email e password onde os valores de formulário para os campos apropriados serão armazenados. Isso significa que nossas variáveis ​​de e- email e password estarão sempre sincronizadas com os valores do formulário, então estaremos prontos para enviar esses valores a qualquer momento sem medo de ter diferenças entre os valores de envio e os valores reais no formulário.

 <script> let email = ""; let password = ""; let isLoading = false; const handleSubmit = () => { isLoading = true; // Simulate network request setTimeout(() => { isLoading = false; // Authorize the user }, 1000); }; </script> <style> /* Style is unchanged */ </style> <form on:submit|preventDefault={handleSubmit}> <h1></h1> <label>Email</label> <input name="email" placeholder="[email protected]" bind:value={email} /> <label>Password</label> <input name="password" type="password" bind:value={password} /> {#if isLoading}Logging in...{:else}Log in {/if} </form>

As variáveis ​​de estado parecem variáveis ​​JavaScript comuns, mas para torná-las sincronizadas com os valores do formulário (associá-las aos campos do formulário), é necessário usar a diretiva bind:value . Há também algumas coisas desconhecidas:

  • on:submit|preventDefault é um atalho para prevenir o comportamento de eventos padrão. É mais confortável desta forma do que ter que escrever e.preventDefault() todas as vezes.

  • {#if isLoading}Logging in...{:else}Log in {/if} é uma parte da sintaxe de modelo do Svelte. Como não há JS no bloco de template, existe uma sintaxe especial para usar ifs, loops, etc.

Finalmente, vamos usar as opções disponíveis usando o estado para adicionar validação ao nosso formulário. Isso pode ser feito criando outra variável de estado errors , que será preenchida com erros quando o formulário for enviado com valores inválidos.

 <script> let email = ""; let password = ""; let isLoading = false; let errors = {}; const handleSubmit = () => { errors = {}; if (email.length === 0) { errors.email = "Field should not be empty"; } if (password.length === 0) { errors.password = "Field should not be empty"; } if (Object.keys(errors).length === 0) { isLoading = true; // Simulate network request setTimeout(() => { isLoading = false; // Authorize the user }, 1000); } }; </script> <style> // Previous styles unchanged .errors { list-style-type: none; padding: 10px; margin: 0; border: 2px solid #be6283; color: #be6283; background: rgba(190, 98, 131, 0.3); } </style> <form on:submit|preventDefault={handleSubmit}> <h1></h1> <label>Email</label> <input name="email" placeholder="[email protected]" bind:value={email} /> <label>Password</label> <input name="password" type="password" bind:value={password} /> <button type="submit"> {#if isLoading}Logging in...{:else}Log in {/if} </button> {#if Object.keys(errors).length > 0} <ul class="errors"> {#each Object.keys(errors) as field} <li>{field}: {errors[field]}</li> {/each} </ul> {/if} </form> 
Erro no formulário de login

O formulário está quase completo. A única coisa que resta é uma mensagem de sucesso na autenticação bem-sucedida.

Vamos criar uma variável de estado para rastrear envios bem-sucedidos que é false por padrão. Após o envio bem-sucedido de um formulário, o valor dessa variável deve ser definido como true .

 let isSuccess = false;

A função que trata do envio do formulário também deve ser alterada para seguir a lógica de alternar isSuccess após uma operação bem-sucedida.

 const handleSubmit = () => { errors = {}; if (email.length === 0) { errors.email = "Field should not be empty"; } if (password.length === 0) { errors.password = "Field should not be empty"; } if (Object.keys(errors).length === 0) { isLoading = true; // Simulate network request setTimeout(() => { isLoading = false; isSuccess = true; // Authorize the user }, 1000); } };

Essa modificação faz com que o formulário entre em estado de sucesso assim que o envio for concluído.

Mas se você verificar seu servidor de desenvolvimento, não encontrará nenhuma alteração no comportamento do formulário. Alteramos o código, mas ainda não tocamos no modelo. Precisamos adicionar instruções ao modelo para mostrar uma mensagem de sucesso quando um usuário fizer login com sucesso. A sintaxe do modelo do Svelte nos permite implementar isso facilmente:

 <form on:submit|preventDefault={handleSubmit}> {#if isSuccess} <div class="success"> <br /> You've been successfully logged in. </div> {:else} <h1></h1> <label>Email</label> <input name="email" placeholder="[email protected]" bind:value={email} /> <label>Password</label> <input name="password" type="password" bind:value={password} /> <button type="submit"> {#if isLoading}Logging in...{:else}Log in {/if} </button> {#if Object.keys(errors).length > 0} <ul class="errors"> {#each Object.keys(errors) as field} <li>{field}: {errors[field]}</li> {/each} </ul> {/if} {/if} </form>

Resumo com Propriedades

Resolvemos tudo sobre o estado do componente interno. Agora é hora de passar pelas dependências externas chamadas propriedades, ou “props”. Props são entradas ou argumentos passados ​​para o componente para descrever ao componente o que deve aparecer ou como o componente deve se comportar.

A declaração de uma propriedade é semelhante ao estado, exceto pela palavra-chave export .

 <script> export let answer; </script> <p>The answer is {answer}</p>
 <script> import Nested from './Nested.svelte'; </script> <Nested answer={42}/>

É tudo sobre as propriedades. Declare e passe - tudo que você precisa saber para usar adereços.

Mas como essas propriedades se aplicam ao componente de formulário de login? Props podem tornar nosso formulário de login mais genérico extraindo a função de envio em uma propriedade. Ele permitirá que você use este componente com qualquer ação de envio necessária (solicitação a um servidor de teste, solicitação a um servidor real, etc.). Essa prop será chamada submit e será uma função que retornará uma promessa resolvida se a ação de envio for bem-sucedida e uma promessa rejeitada se houver um erro. Vamos declarar a prop pelo exemplo dado acima:

 export let submit;

O manipulador de envio dentro do formulário de login também deve ser editado para usar a nova propriedade de submit .

 const handleSubmit = () => { errors = {}; if (email.length === 0) { errors.email = "Field should not be empty"; } if (password.length === 0) { errors.password = "Field should not be empty"; } if (Object.keys(errors).length === 0) { isLoading = true; submit({ email, password }) .then(() => { isSuccess = true; isLoading = false; }) .catch(err => { errors.server = err; isLoading = false; }); } };

O componente parece estar pronto. No entanto, se você retornar ao formulário e tentar enviá-lo, notará que o estado do botão não mudou desde o carregamento. Além disso, há uma exceção no console: Uncaught TypeError: submit is not a function . Claro, declaramos o prop, mas esquecemos de passá-lo. Vamos declarar uma função no componente app e passá-la para o formulário de login.

 const submit = ({ email, password }) => new Promise((resolve, reject) => setTimeout(resolve, 1000));
 <section> <LoginForm submit={submit} /> </section>

Agora o formulário está funcionando conforme o esperado. Ele pode mostrar erros e informar ao usuário se o login foi bem-sucedido.

Sucesso no formulário de login

Compartilhamento de contexto

Parece que tudo o que é necessário para construir um aplicativo está listado. Com as propriedades e o estado interno, estamos prontos. Isso é apenas parcialmente verdade, no entanto. Esses dois pontos gerais permitem projetar SPAs de alta complexidade. No entanto, se você tentar compartilhar dados entre muitos componentes diferentes, será muito difícil.

O exemplo mais simples é ter uma variável de user globalmente acessível. Muitos componentes devem mudar seu comportamento em relação ao usuário, dependendo da função do usuário, idade, status, etc. No entanto, não é DRY repetir-nos passando o usuário para cada componente do aplicativo usando props.

O Svelte tem uma solução para isso: a API de contexto.

A API de contexto fornece um mecanismo para os componentes 'conversarem' entre si sem passar dados e funções como props ou despachar muitos eventos. É um recurso avançado, mas útil.

Vamos adicionar o contexto do usuário ao formulário de login que estamos criando. Crie um arquivo userContext.js dentro da pasta src com o seguinte conteúdo:

 export const key = "userContext"; export const initialValue = null;

key é um identificador exclusivo para o contexto, pois um aplicativo pode ter um número ilimitado de contextos diferentes que devem permanecer acessíveis. initialValue é apenas um valor padrão para o contexto antes de ser definido.

O próximo passo é adicionar o contexto ao nosso aplicativo. Navegue até o arquivo App.svelte e adicione 2 instruções de importação:

 import { onMount, setContext } from "svelte"; import { key as userContextKey, initialValue as userContextInitialValue } from "./userContext";

Observando o código acima, você pode se perguntar o que estamos importando do pacote svelte . onMount é uma função auxiliar que requer uma função de retorno de chamada como argumento. Este retorno de chamada será executado quando o componente atual estiver sendo montado (no início do carregamento do componente). setContext é uma função setter para um contexto. Requer a chave para o contexto e um novo valor como seus argumentos.

Vamos usar a função onMount para definir o valor padrão para o contexto:

 onMount(() => { setContext(userContextKey, userContextInitialValue); });

E modifique a função submit para definir o contexto do usuário:

 const submit = ({ email, password }) => new Promise((resolve, reject) => { setTimeout(() => { setContext(userContextKey, { name: "Foo", lastName: "Bar", email: "[email protected]" }); resolve(); }, 1000); });

É isso. Um envio bem-sucedido mudará o contexto do usuário para um objeto de usuário falso que pode ser acessado por um getContext :

 <script> import { getContext } from 'svelte'; import { key as userContextKey } from "./userContext"; const user = getContext(key); </script>

Resumo

Svelte é uma ferramenta poderosa capaz de alto desempenho e com uma API flexível. Além do básico abordado neste post, o Svelte tem os seguintes recursos prontos para uso:

  • Declarações e declarações reativas
  • Aguardar blocos de modelo
  • Vinculação de dimensão
  • Uma loja global como Redux
  • Auxiliares de animação e transição
  • Um auxiliar de depuração

Resumindo, Svelte é uma ótima biblioteca que atende a todas as necessidades de construção de SPAs e muito mais. Pode competir com os maiores players do mercado e até vencer. O que poderia usar agora, porém, é o suporte na comunidade de desenvolvedores front-end.

Nota: Todo o código neste artigo pode ser encontrado no repositório GitHub teimurjan/svelte-login-form . A demonstração do formulário de login está disponível aqui.