A lenda da estrutura do poder

Recentemente, a tendência de “desaparecer estruturas” está ganhando popularidade, cuja locomotiva, sem dúvida, pode ser considerada SvelteJS - uma estrutura de construção e um compilador javascript de baunilha.

Apesar de conceitualmente o Svelte ser muito simples e ainda mais fácil de usar, muitos desenvolvedores estão se perguntando qual é o recurso essencial dessa estrutura e toda a abordagem? Por que isso não é um "outro framework javascript"?

Neste artigo, falarei sobre uma das muitas superpotências Svelte que podem facilitar seriamente sua vida.

Vamos descobrir, mas primeiro vou lhe contar uma lenda ...



A lenda da estrutura do poder


Alguns frameworks foram criados pelos caras do Google e do Facebook, outros - caras legais, mas todos sob a "atenção" de Rich Harris .

Nove estruturas foram criadas para humanos, sete parecem ser para anões. Três outras estruturas (react, vue, angular) eram para elfos.

Depois de criar as estruturas e implementá-las em milhares de projetos, Rich Harris criou pessoal e secretamente uma estrutura ...

Uma estrutura para governar todos eles,
Uma estrutura para encontrá-los,
Uma estrutura para trazer todos eles
E juntos ligá-los.

- O Senhor das Estruturas

O problema


Estou certo de que muitos de vocês que estão engajados com seriedade e há muito tempo no desenvolvimento front-end enfrentaram repetidamente o problema de escolher ferramentas para o seu projeto atual e / ou próximo.
A variedade de todos os tipos de pacotes, utilitários, baleias, estruturas, bibliotecas e outras soluções está fora de escala como nunca antes. E o mais importante, todo esse movimento continua a acelerar.

Tudo isso, de uma maneira ou de outra, se aplica à escolha da estrutura. Provavelmente não se engane se eu assumir que apenas algumas equipes e empresas modernas iniciam novos projetos sem usar nenhuma estrutura js. Obviamente, quando se trata de aplicativos da web modernos, não apenas de sites. E tudo ficaria bem se muito do seu projeto não dependesse disso.


Julgue por si mesmos, a composição e as características da equipe dependem amplamente da estrutura que você escolher, e todo o processo de caça certamente depende. Às vezes, o orçamento e os cronogramas dependem disso. Em suma brrr.

Mas os problemas reais começam, se em algum lugar no meio do projeto, você percebe que fez uma escolha incorreta. Algo não cresceu junto e girou. A estrutura exigiu um pouco mais de tempo para dominar, uma equipe um pouco maior, que se mostrou um pouco menos rápida, um pouco inadequada para seus objetivos ou estilo de desenvolvimento, etc. E o mais importante, agora seu projeto está 100% vinculado a essa estrutura e você não pode simplesmente pegar e reescrevê-lo em outra coisa.

Ainda mais ofensivamente, quando, no entanto, tendo concluído o projeto com sucesso, você entende que, em geral, não está muito feliz. E, provavelmente, não gostaríamos de escrever o próximo projeto na mesma estrutura. Portanto, todas as soluções "reutilizadas" pelas quais estamos nos esforçando podem ser jogadas no cano.

Na verdade, o inferno com ele, com um código comercial que implementa uma tarefa comercial específica, funciona bem. Mas você escreveu um "% insert%% legal com blackjack e meninas com baixa responsabilidade social", e queria usá-lo em seu próximo projeto, mas essa infecção está intimamente ligada à estrutura atual da qual você já está vendo um tipo.

Outra variação do mesmo problema - imagine que você é uma empresa grande, como a Yandex. Você tem um huilion de projetos, mesmo alguns conhecidos apenas por alguns funcionários, e cada projeto já experimentou tudo o que descrevi acima. O problema é que todos esses projetos se assemelham às diferentes estruturas que eles selecionaram inicialmente.

E aqui está sua liderança maravilhosa: decidiu competir com o Google Material Design e enviar-lhe uma cruzada para as interfaces variadas de seus projetos, a fim de levá-las a um denominador comum. Designers espertos já estão desenhando novos botões e seletores e rabiscando milhares de páginas de diretrizes para o novo kit de interface do usuário único de seus componentes. Camaradas Hurrah!

Não a vida, mas um conto de fadas, certo? Resta apenas descobrir como extrair todos esses novos componentes em todos os projetos que você já conseguiu escrever em todas as estruturas possíveis. Se houver realmente muito tempo e dinheiro e houver um desejo estético e, mais importante, a crença de que "tudo precisa ser unificado", você poderá colocar algumas dezenas de equipes para reescrever tudo novamente, por exemplo, no React. Isso está correto, porque a merda chata em que você escreveu nos últimos 2-3 anos já é moralmente obsoleta, mas o React será para sempre. Bem, bem)

Existe outro caminho. Você pode escrever um novo e maravilhoso kit de interface do usuário em uma estrutura, criar uma biblioteca de componentes reutilizáveis ​​e, em seguida, simplesmente usar esse kit de interface do usuário em todos os seus projetos. Parece legal? Claro, mas ainda resta um problema - o tempo de execução.

Se o seu projeto foi escrito em Angular (~ 500Kb) e você decidiu escrever um kit de interface do usuário no React (~ 98Kb), arraste cada projeto em uma estrutura, outra estrutura e mesmo com várias dependências, o próprio kit da UI é direto digamos que não parece ótimo.

Solução


Para nos ajudar a criar os próprios quadros "desaparecidos", sem tempo de execução. A principal condição aqui é que eles sejam o mais isolados possível em termos de seu ecossistema e tenham mecanismos de integração externa e APIs correspondentes.

Um ótimo exemplo dessa estrutura é o SvelteJS, sobre o qual alguns artigos já foram escritos em Habré .

Então, imagine a situação em que temos um aplicativo no React. Talvez estejamos cansados ​​e queremos nos livrar dele, mas reescrever tudo de uma vez é um luxo inadmissível. Ou talvez algumas partes do aplicativo exijam melhorias ou refatoração. Bem, ou decidimos criar uma biblioteca de componente único, e agora vamos escrever todos os componentes no Svelte e usá-los em todos os projetos. Apresentado? Sim, claro que não, ninguém tem essa fantasia. Vamos dar uma olhada em um exemplo real.


Isenção de responsabilidade
Imediatamente, quero chamar sua atenção para o fato de não ser um desenvolvedor do React e da última vez em que "senti" o React em 2015. Portanto, suponho que a maneira como escrevi parte do exemplo React possa prejudicar os sentimentos de acreditar em reatistas. Lamento não julgar rigorosamente, especialmente porque o significado do artigo não muda disso.


Portanto, a tarefa é implementar o componente Svelte pronto no aplicativo React, sem alterar o próprio componente e sem incluir um tempo de execução adicional no aplicativo. Por exemplo, utilizarei o componente que pesquisa usuários do GitHub, que escrevi para o artigo anterior "Como pesquisar usuários no GitHub sem o React + RxJS 6 + Recompose" .

O código para este componente pode ser exibido no REPL e o código de amostra deste artigo para o repositório .

Criar aplicativo de reação


Primeiro, crie um novo projeto React usando a ferramenta padrão de fato - create-react-app :

npx create-react-app my-app cd my-app npm start 

Ok, se você for para a porta 3000, ela parece funcionar.

Personalizar Svelte


Se você não sabe nada sobre o Svelte, direi isso, no contexto da tarefa Svelte, este é apenas mais um passo do seu coletor (webpack / rollup / gupl / grunt / etc), que permitirá escrever componentes no formato SFC e compilá-los em baunilha javascript.

Na comunidade Svelte, o Rollup é mais preferido, o que não é surpreendente, pois eles têm um autor - Rich Harris. No entanto, como o CRA usa o webpack, configuraremos o Svelte por meio dele. Para fazer isso, primeiro você precisa transferir as configurações do webpack de react-scripts para o projeto, para que possamos alterá-las. Isso é feito usando o comando interno:

 npm run eject 


Até onde eu sei, essa não é uma abordagem kosher, mas, por exemplo, esta é a opção mais conveniente.

Agora que as configurações do webpack estão na raiz do projeto, você pode instalar o Svelte:

 npm i --save-dev svelte svelte-loader 


Preste atenção ao sinalizador --save-dev , lembre-se de que sim, não há tempo de execução.))))

O toque final, você precisa conectar o carregador apropriado nas configurações:

  { test: /\.svelte$/, use: { loader: 'svelte-loader', } }, 


Em geral, é comum na comunidade Svelte gravar arquivos de componentes com a extensão .html , porque o componente Svelte é um arquivo HTML válido. No entanto, para evitar possíveis colisões, em alguns casos, é melhor usar o formato de arquivo .svelte personalizado.

Assim fizemos, agora todos os arquivos .svelte incluídos no projeto serão interceptados por este carregador e compilados por Svelte.

Escrevendo um componente Svelte


Primeiro, é melhor configurar o editor de código, por exemplo, para que aplique a sintaxe html realçada a arquivos com a extensão correspondente. Algo assim é feito no VS Code:

  "files.associations": { "*.svelte": "html" } 

Agora crie uma pasta ./src/svelte_components/ e lá a pasta do próprio componente. Depois disso, simplesmente transferimos todos os arquivos do exemplo REPL para esta pasta, fornecendo simultaneamente uma nova extensão .svelte e chamamos o arquivo App.html Widget.svelte.

O resultado deve ser algo como isto:


No mesmo local, criamos o arquivo index.js no qual teremos o código de integração Svelte e React.

Integrar


Talvez agora você queira saber o que é mágica? A mágica é que já fizemos toda a mágica. Magicamente, não é?

Sério, agora podemos usar os componentes Svelte em nosso aplicativo React como construtores JS completamente comuns, o que significa que o código de integração com o Svelte não será diferente da integração com qualquer outro standalone. A documentação do React ainda contém uma seção dedicada a isso: Integração com outras bibliotecas .

O código de integração pode ser assim:

 import React, { PureComponent } from 'react'; import Widget from './Widget.svelte'; export default class extends PureComponent { componentDidMount() { const { username } = this.props; this.widget = new Widget({ target: this.el, data: { username } }); } componentWillUnmount() { this.widget.destroy(); } render() { return ( <div ref={el => this.el = el}></div> ); } } 

No total, simplesmente empacotamos o código do nosso componente complexo Svelte em um componente React muito simples, que simplesmente cria uma nova instância do componente Svelte, transferindo o elemento mount e os dados dos acessórios ao criá-lo. Além disso, não esquecemos de desinstalar o componente Svelte no gancho componentWillUnmount .

A única coisa que ainda não fizemos é que eles não sincronizaram os valores do estado do componente. Ou seja, se o componente pai lançou outros adereços no componente wrapper, eles devem ser aplicados ao componente Svelte. Por outro lado, se os dados foram alterados dentro do componente Svelte, eles devem ser revertidos.

Para fazer isso, assumimos que o componente React de nível superior transmitirá o retorno de chamada onChange, que devemos extrair quando ocorrerem alterações internas, e esperaremos alterações nas propriedades do componente wrapper no gancho componentWillReceiveProps . Vamos fazer isso:

  componentDidMount() { ... this.widget.on('state', ({ current: { username }, changed }) => { if (changed.username) { this.props.onChange({ username }); } }); } componentWillReceiveProps({ username }) { this.widget.set({ username }); } 

Aqui, usamos o evento interno do estado , que é acionado toda vez que o estado do componente Svelte é alterado. Um objeto que contém o estado atual do componente ( atual ), o estado anterior ( anterior ) e a lista de propriedades alteradas ( alteradas ) é transferido para o retorno de chamada. Dessa forma, simplesmente verificamos se o nome de usuário foi alterado e, se houver, chamamos o retorno de chamada onChange.

No gancho componentWillReceiveProps , configuramos o novo nome de usuário usando o método set () interno.

Além dos componentes internos, o Svelte pode implementar eventos e métodos personalizados. São esses recursos interessantes que permitem descrever a interface do componente e é bastante conveniente organizar a comunicação com o "mundo exterior".

Use


Agora vamos tentar usar nosso widget diretamente no aplicativo React. Para fazer isso, edite o arquivo App.js gerado pelo iniciador:

 import React, { Component } from 'react'; import './App.css'; import GithubWidget from './svelte_components/GithubWidget'; class App extends Component { constructor() { super(); this.state = { username: '' }; } handleChange = (state) => { this.setState({ ...state }); } render() { return ( <div className="App"> <header className="App-header"> <h1>Github Widget for: {this.state.username}</h1> <GithubWidget onChange={this.handleChange} username={this.state.username} /> </header> </div> ); } } export default App; 

Em resumo, nós o usamos como um componente React regular. E, como resultado, obtemos:


Já não é ruim, certo?) Observe que o valor do nome de usuário que inserimos no campo de texto do widget é imediatamente encaminhado para o aplicativo React.

Vamos finalizar


Vamos agora ensinar nosso widget a pesquisar e exibir não apenas o cartão de usuário do GitHub, mas também o cartão de repositório.

Primeiro, você precisa criar um novo componente Repo.svelte, que irá desenhar a placa do repositório. Para simplificar, apenas copiei o modelo e os estilos do User.svelte e os adaptei aos dados do repositório. No entanto, teoricamente, este é um componente separado.

Em seguida, você precisa ensinar o componente de controle Widget.svelte a alternar esses dois tipos de cartões rapidamente. Além disso, você precisa ensiná-lo a receber solicitações diferentes para o usuário e o repositório.

Usaremos um campo para entrada e determinaremos o tipo de dados pela presença de "/" no valor. Ou seja, se você precisar procurar o usuário, insira o nome de usuário e, se o repositório, insira o nome de usuário e, em seguida, "/" e o nome do repositório.

À primeira vista, parece bastante confuso, mas no Svelte a solução terá literalmente 5-6 linhas de código. Primeiro, vamos conectar um novo componente e método de API, que envolvemos em debounce:

 ... import Repo from './Repo.svelte'; ... import { getUserCard, getRepoCard } from './api.js'; ... const getRepo = debounce(getRepoCard, 1000); 

Em seguida, crie uma propriedade computada que determinará que tipo de cartão devemos mostrar:

 computed: { ... repo: ({ username }) => username.includes('/'), ... } 

Agora adicione a opção de solicitações à API:

 computed: { ... card: ({ username, repo }) => username && (repo ? getRepo : getUser)(username), ... } 

E, finalmente, alternando os componentes da placa, dependendo do tipo:

 computed: { ... Card: ({ repo }) => repo ? Repo : User, ... } 

Além disso, para substituir componentes dinamicamente, precisamos usar uma tag Svelte especial, que desenha o componente cujo valor é passado para este atributo:

 <svelte:component this={Card} {...card} /> 


Isso funciona. Você já reparou? Já estamos escrevendo no Svelte dentro do aplicativo React! )))

Agora vamos ensinar nosso widget a ocultar o campo de entrada e tentar inserir o nome de usuário não dentro do widget, mas dentro do aplicativo React. Importa como nossa lógica de negócios obterá esse valor.

Introduzimos uma nova propriedade de pesquisa cujo valor padrão é false. Dependendo dessa propriedade, o campo de entrada será exibido ou não. Por padrão, portanto, não haverá campo.

 {#if search} <input bind:value=username placeholder="username or username/repo"> {/if} ... <script> export default { ... data() { return { username: '', search: false }; }, ... }; </script> 

Agora no App.js, criaremos um campo de entrada no lado React do aplicativo e escreveremos o processamento correspondente para o evento de entrada:

  ... handleUsername = (e) => { this.setState({ username: e.target.value }); } ... <h1>Github Widget for: {this.state.username}</h1> <input value={this.state.username} onChange={this.handleUsername} className="Username" placeholder="username or username/repo" /> 

E também copie-o para a pasta com o widget aqui, como um girador svg no Svelte:

 <svg height={size} width={size} style="animation-duration:{speed}ms;" class="svelte-spinner" viewbox="0 0 32 32" > <circle role="presentation" cx="16" cy="16" r={radius} stroke={color} fill="none" stroke-width={thickness} stroke-dasharray="{dash},100" stroke-linecap="round" /> </svg> <script> export default { data() { return { size: 25, speed: 750, color: 'rgba(0,0,0,0.4)', thickness: 2, gap: 40, radius: 10 }; }, computed: { dash: ({radius, gap}) => 2 * Math.PI * radius * (100 - gap) / 100 } }; </script> <style> .svelte-spinner { transition-property: transform; animation-name: svelte-spinner_infinite-spin; animation-iteration-count: infinite; animation-timing-function: linear; } @keyframes svelte-spinner_infinite-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } </style> 

E vamos aplicá-lo no widget para que fique muito bonito:

 ... {#await card} <Spinner size="50" speed="750" color="#38b0ee" thickness="2" gap="40" /> {:then card} ... 

Na minha opinião, acabou não muito ruim:


A cartola com fundo preto e um campo de entrada é um aplicativo React, o bloco branco abaixo é um widget Svelte. Estas são as tortas. )))

Repositório

Conclusões


O Svelte é uma ótima ferramenta para o desenvolvimento de aplicativos da Web modernos baseados em componentes. Além disso, você pode escrever de forma rápida e conveniente componentes e widgets de interface do usuário autônomos reutilizáveis, que podem ser usados ​​em qualquer aplicativo Web, mesmo em conjunto com outras estruturas. Também é ótimo para microfrontas .

Svelte é perfeito para você se:


  1. Você deseja iniciar um novo projeto e não sabe qual estrutura escolher para isso.
  2. Você tem um projeto, funciona e é melhor não tocá-lo. Novos componentes e módulos, você pode escrever no Svelte e integrar-se perfeitamente ao código existente.
  3. Você já possui um projeto, mas está parcial ou completamente desatualizado e / ou requer uma refatoração séria, até uma reescrita completa. Você pode começar a reescrevê-lo em partes. Nesse caso, você não precisa criar configurações complexas. Você acabou de pegar um componente, reescrevê-lo no Svelte e envolver o novo componente com o antigo. No entanto, o restante do aplicativo nem está ciente das alterações.
  4. Você tem vários projetos em diferentes bases de código e, ao mesmo tempo, gostaria de ter um único kit de interface do usuário e usá-lo em qualquer um desses projetos. Escreva um kit de interface do usuário no Svelte e use-o em qualquer lugar. Isso é legal.

Deseja descobrir casos mais interessantes? Participe do nosso canal Telegram !

ATUALIZAÇÃO: obrigado justboris pela pergunta correta. Continuando o exemplo:

 import React, { PureComponent } from 'react'; import Widget from './Widget.svelte'; export default class extends PureComponent { componentDidMount() { ... this.widget = new Widget({ target: this.el, data: { username }, slots: { default: this.slot } }); ... } ... render() { return ( <div ref={el => this.el = el}> <div ref={el => this.slot = el}> {this.props.children} </div> </div> ); } } 


 <GithubWidget onChange={this.handleChange} username={this.state.username}> <p>Hello world</p> </GithubWidget> 

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


All Articles