<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Deusiel da Cunha de Souza - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Aprenda a codificar - de graça. Tutoriais de programação em Python, JavaScript, Linux e muito mais. ]]>
        </description>
        <link>https://www.freecodecamp.org/portuguese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Deusiel da Cunha de Souza - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/portuguese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Wed, 06 May 2026 14:33:11 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/portuguese/news/author/deusiel/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Por que a raiz de um domínio não pode ser um CNAME — e outras definições sobre o DNS ]]>
                </title>
                <description>
                    <![CDATA[ Este artigo usará a pergunta acima para explorar DNS, dig, registros A, registros CNAME e registros ALIAS/ANAME da perspectiva de um iniciante. Vamos começar. Primeiro, alguns conceitos  * Domain Name System (DNS): o sistema geral para converter um nome de domínio    inteligível para humanos (como "example.com") ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/por-que-a-raiz-de-um-dominio-nao-pode-ser-um-cname-e-outras-definicoes-sobre-o-dns/</link>
                <guid isPermaLink="false">63874c69d7295905e93497e5</guid>
                
                    <category>
                        <![CDATA[ Tecnologia ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Deusiel da Cunha de Souza ]]>
                </dc:creator>
                <pubDate>Tue, 03 Jan 2023 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/12/1_YNkO-BfTsVJYxslNrNn8LA.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/why-cant-a-domain-s-root-be-a-cname-8cbab38e5f5c/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Why a domain’s root can’t be a CNAME — and other tidbits about the DNS</a>
      </p><p>Este artigo usará a pergunta acima para explorar <code>DNS</code>, <code>dig</code>, registros <code>A</code>, registros <code>CNAME</code> e registros <code>ALIAS/ANAME</code> da perspectiva de um iniciante. Vamos começar.</p><h3 id="primeiro-alguns-conceitos">Primeiro, alguns conceitos</h3><ul><li><strong>Domain Name System</strong> (DNS): o sistema geral para converter um nome de domínio inteligível para humanos (como "example.com") em um endereço IP (93.184.216.34). O endereço IP é de um servidor (geralmente, um servidor da web) onde são armazenados os arquivos necessários para exibir uma página da web.</li><li><strong>Servidor DNS</strong> (também conhecido como servidor de nomes): usa o software do DNS para armazenar informações sobre endereços de domínio. Existem vários níveis — aqueles pertencentes a cada provedor de serviços de internet, root (13 no total em todo o mundo), domínios de primeiro nível (em inglês, "TLD" – por exemplo, ".com") e servidores DNS de nível de domínio.</li><li><strong>Nome de domínio</strong>: o domínio (example) combinado com o TLD (.com). O termo 'domínio' é frequentemente usado como sinônimo do nome de domínio, embora sejam diferentes. Quando você compra um "domínio" de uma empresa de registro ou revendedor, você compra os direitos de um nome de domínio específico (como "example.com") e quaisquer subdomínios que desejar criar (meu-site.example.com, mail.example.com etc.).</li></ul><h3 id="fluxo-de-consulta-de-alto-n-vel">Fluxo de consulta de alto nível</h3><p>O fluxo de alto nível do que acontece quando você digita "example.com" em seu navegador pode ser simplificado para remover os saltos para os servidores ISP, root e DNS de TLDs, conforme vemos abaixo:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/12/-Yu9MR65z19xx2TDl-6phT7soy3g3KNgjArX.png" class="kg-image" alt="-Yu9MR65z19xx2TDl-6phT7soy3g3KNgjArX" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/12/-Yu9MR65z19xx2TDl-6phT7soy3g3KNgjArX.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/12/-Yu9MR65z19xx2TDl-6phT7soy3g3KNgjArX.png 800w" sizes="(min-width: 720px) 720px" width="800" height="162" loading="lazy"><figcaption>Simplificação do fluxo de uma requisição DNS. Mais detalhes podem ser consultados <a href="http://www.uxworld.com/?p=384">aqui</a> (texto em inglês).</figcaption></figure><p>Um domínio normalmente tem dois ou mais servidores de nome, contendo registros relacionados ao nome de domínio (como "example.com").</p><p>Muitos tipos de registros podem ser armazenados, a maioria dos quais pode ter várias entradas por tipo:</p><ul><li><code>A</code>: Registros de endereço que mapeiam o nome de domínio para um endereço IP</li><li><code>CNAME</code>: Registro de Nome Canônico. Usado para apelidar um nome de domínio (ou nome de subdomínio) para outro. Veremos isso em mais detalhes posteriormente.</li><li><code>MX</code>: registros do Mail eXchange que informam aos agentes de entrega de e-mail onde eles devem entregar seu e-mail</li><li><code>TXT</code>: registros de texto flexíveis, para armazenar strings para uma variedade de usos</li><li><code>SOA</code>: Registro Singular de Início de Autoridade mantido no nível superior do domínio. Contém informações específicas necessárias sobre o domínio, como por exemplo, seu servidor de nomes principal</li></ul><p>Quando seu dispositivo envia uma consulta que chega a um servidor de nomes, o servidor procura no nó de registro do domínio um registro <code>A</code> e o endereço IP armazenado associado (example.com: 93.184.216.34). Esse endereço é, então, retornado ao dispositivo para ser usado para enviar uma solicitação ao servidor da web correto para recuperar a página da web ou o recurso solicitado.</p><h3 id="usando-o-dig-">Usando o 'dig'</h3><p><code>dig</code> (<strong>explorador de informações de domínio </strong>– do inglês Domain <em>Information Groper</em>) é uma ferramenta de linha de comando para consultar servidores DNS. Esse comando geralmente é usado para solução de problemas ou, como agora, para entender mais sobre a configuração de um sistema.</p><p><code>$ dig example.com</code> resulta em uma resposta longa impressa no terminal, <a href="https://www.madboa.com/geek/dig/#understanding-the-default-output">cuja saída padrão é detalhada aqui</a> (texto em inglês). Dela, temos interesse apenas em ver a <code>SEÇÃO DE RESPOSTAS</code>(do inglês, <code>ANSWER SECTION</code>).</p><pre><code>;; ANSWER SECTION:
example.com.       72703      IN     A       93.184.216.34</code></pre><p>Podemos ver que <code>example.com</code> retorna um registro <code>A</code> de <code>93.184.216.34</code>. Às vezes, os domínios terão mais de um registro <code>A</code>, se mais de um servidor da web puder fornecer as informações necessárias.</p><p>Tem mais! Se tentarmos alguns outros exemplos, logo veremos que outro registro comum aparece: <code>CNAME</code>.</p><p><code>$ dig www.skyscanner.net</code>:</p><pre><code>;; ANSWER SECTION:
www.skyscanner.net. 169 IN CNAME www.skyscanner.net.edgekey.net.
www.skyscanner.net.edgekey.net. 5639 IN CNAME e11316.a.akamaiedge.net.
e11316.a.akamaiedge.net. 20 IN A 23.217.6.192</code></pre><pre><code>www.skyscanner.net.edgekey.net. 5639 IN CNAME e11316.a.akamaiedge.net.</code></pre><pre><code>e11316.a.akamaiedge.net. 20 IN A 23.217.6.192</code></pre><p>Usar o sinalizador <code>+short</code> nos permite ver claramente o caminho formado:</p><p><code>$ dig <a href="http://www.skyscanner.net" rel="noopener">www.skyscanner.net</a> +short</code></p><pre><code>www.skyscanner.net.edgekey.net.
e11316.a.akamaiedge.net.
23.217.6.192</code></pre><h3 id="cname">CNAME</h3><p>Um registro <code>CNAME</code> permite que um nome de domínio seja usado como um apelido para outro domínio canônico (verdadeiro).</p><p>Quando o servidor DNS retorna um registro <code>CNAME</code>, ele não o retorna ao <em>client</em>. Em vez disso, ele procurará novamente o nome de domínio retornado e, por sua vez, retornará o endereço IP do registro <code>A</code>. Essa cadeia pode continuar com muitos níveis de <code>CNAME</code> profundos, mas sofre pequenos impactos de desempenho de várias pesquisas antes do armazenamento em cache.</p><p>Um exemplo simples disso pode ser se você tiver um servidor onde guarda todas as suas fotos. Você pode acessá-lo normalmente através de <code>photos.example.com</code>. No entanto, você também pode querer permitir o acesso via <code>photographs.example.com</code>. Uma maneira de tornar isso possível é adicionar um registro <code>CNAME</code> que aponte <code>photographs</code> para <code>photos</code>. Isso significa que, quando alguém visitar <code>photographs.example.com</code>, receberá o mesmo conteúdo que <code>photos.example.com</code>.</p><p>Usando a consulta <code>$ dig photography.example.com</code>, veríamos:</p><pre><code>photographs.example.com    IN   CNAME photos.example.com
photos.example.com         IN   A     xx.xxx.x.xxx</code></pre><p>É importante observar que o <code>CNAME</code> é aquela parte do lado direito. O lado esquerdo é o nome do apelido ou rótulo.</p><p>Outro uso comum é para o subdomínio <code>www</code>. Depois de comprar <code>example.com</code>, você provavelmente também desejará que os usuários que digitem <code>www.example.com</code> vejam o mesmo conteúdo.</p><p>Vale a pena notar aqui que <code>example.com</code> pode ser chamado de ápice, raiz ou nome de domínio puro.</p><p>Uma opção seria configurar outro registro <code>A</code>, apontando para o mesmo endereço IP de <code>example.com</code>. Isso é totalmente válido e é o que o site real <code>example.com</code> faz, mas não escala bem. O que acontece se você precisar atualizar o endereço IP para o qual <code>example.com</code> aponta? Você também precisaria atualizá-lo para o subdomínio <code>www</code> e qualquer outro que possa usar.</p><p>Se um registro <code>CNAME</code> foi usado como apelido para <code>www.example.com</code> para apontar para <code>example.com</code>, apenas o domínio raiz teria que ser atualizado, já que todos os outros nós apontam para ele.</p><h3 id="limita-es-do-cname">Limitações do CNAME</h3><p>Na época em que os padrões de DNS foram escritos, algumas regras foram estabelecidas para reger seu uso. <a href="https://tools.ietf.org/html/rfc1912">RFC 1912</a> e <a href="https://tools.ietf.org/html/rfc2181">RFC 2181</a> estabelecem que:</p><ul><li>Os registros <code>SOA</code> e <code>NS</code> são obrigatórios no domínio raiz</li><li>Os registros <code>CNAME</code> só podem existir como registros únicos e não podem ser combinados com nenhum outro registro de recurso (com exceção dos registros DNSSEC <code>SIG</code>, <code>NXT</code> e <code>KEY RR</code>)</li></ul><p>Isso exclui um <code>CNAME</code> de ser usado no domínio raiz, pois isso faria com que as duas regras se contradissessem.</p><p>O importante aqui é que se trata de uma limitação contratual, não técnica. É possível usar um <code>CNAME</code> na raiz, mas pode resultar em erros inesperados, pois está quebrando o contrato de comportamento esperado.</p><p>Um exemplo disso é <a href="https://blog.cloudflare.com/introducing-cname-flattening-rfc-compliant-cnames-at-a-domains-root/">relatado pela Cloudflare</a>, descrevendo problemas que eles encontraram com os servidores de e-mail do Microsoft Exchange depois de usar um <code>CNAME</code> em seu domínio raiz:</p><blockquote>Os domínios geralmente designam os servidores que lidam com seus e-mails por meio do que é conhecido como registro MX. O problema era que os servidores Exchange… podiam pegar o CNAME no registro raiz e não respeitar adequadamente o CNAME definido no registro MX. Você não pode realmente culpar o Exchange. <strong>Eles estavam operando sob as suposições estabelecidas pela especificação do DNS.</strong></blockquote><p>Aqui, você vê a desvantagem que pode aparecer em vários softwares de servidor ou bibliotecas. Como existe um padrão para que um <code>CNAME</code> seja o <strong>único registro</strong> em um nó, <strong>nenhum outro registro é procurado</strong>. Todos os outros registros serão ignorados silenciosamente, sem aviso ou mensagens de erro. Mesmo que um registro MX tenha sido definido para receber e-mail, o MX será ignorado como se não existisse porque o <code>CNAME</code> é avaliado primeiro. O mesmo é verdadeiro se houvesse um registro <code>A</code>: o <code>CNAME</code> teria precedência e o registro <code>A</code> não seria lido.</p><h3 id="a-internet-moderna">A internet moderna</h3><p>Então, por que isso é um problema? Por que você desejaria usar um <code>CNAME</code> para seu domínio raiz? Certamente, esse é o fim do caminho ao procurar o endereço IP do servidor da web que hospeda seu conteúdo?</p><p>No cenário moderno da internet, esse não é mais o caso. O mundo é muito diferente de quando os padrões de DNS foram escritos. </p><p>Você pode optar por usar um provedor de plataforma como serviço (do inglês, PaaS), como o <a href="https://www.heroku.com/">Heroku</a>, e armazenar conteúdo em seus servidores da web. Você controla o conteúdo, mas não a infraestrutura. O provedor de PaaS faz o trabalho pesado da manutenção da rede. Eles normalmente fornecem um URL (`my-app.herokuapp.com`) que é um subdomínio de seu domínio raiz. Você pode visualizar os endereços IP dos servidores da web em que seu conteúdo está. Eles, no entanto, estão totalmente sob o controle do provedor de PaaS e serão alterados sem aviso prévio.</p><p>A escala e a frequência das alterações de <em>back-end</em> feitas pelo provedor de PaaS podem dificultar a manutenção do registro <code>A</code> do domínio raiz apontando para um único endereço IP. Idealmente, você gostaria de fazer isso:</p><pre><code>example.com      IN   CNAME    my-app.herokuapp.com.www.example.com  IN   CNAME    my-app.herokuapp.com.example.com      IN   CNAME    my-app.herokuapp.com.
www.example.com  IN   CNAME    my-app.herokuapp.com.</code></pre><p>para permitir que o Heroku (ou seu provedor de host escolhido) gerencie a atualização do registro <code>A</code> para a qual o <code>CNAME</code> aponta sem nenhuma alteração feita por você. No entanto, como sabemos agora, isso quebra a especificação do DNS e, portanto, é uma péssima ideia.</p><p>É possível simplesmente implementar um redirecionamento 301/302 de <code>example.com</code> para <code>www.example.com</code>. Porém, essa instrução ocorre no servidor da web (portanto, ainda tem o problema de precisar usar um registro <code>A</code> fixo no DNS para apontar para esse servidor da web) ou um redirecionamento de provedor de DNS personalizado (<a href="https://support.dnsimple.com/articles/url-record/">que sofre complicações com HTTPS</a>).</p><p>Isso também tem o efeito colateral de alterar o domínio que você vê na barra de URL, o que você pode não querer. Este método destina-se a quando seu site foi movido permanentemente ou quando você está tentando preservar as <a href="https://support.google.com/webmasters/answer/93633?hl=en">classificações de SEO</a>, em vez de resolver o problema de apontar para um <em>back-end</em> de mudança complexo de maneira escalável.</p><h3 id="a-solu-o">A solução</h3><p>Vários provedores de DNS já desenvolveram soluções personalizadas para contornar esse problema, incluindo:</p><ul><li><code>ALIAS</code> em DNSimple</li><li><code>ANAME</code> em DNS Made Easy</li><li><code>ANAME</code> em easyDNS</li><li><code>CNAME</code> (virtual) em CloudFlare</li></ul><p>Esses são todos os tipos de registro virtual que fornecem comportamento semelhante ao <code>CNAME</code>, sem nenhuma das desvantagens. A implementação exata pode diferir, mas, em um alto nível, quando o servidor DNS vê um desses tipos de registro virtual, ele age como um resolvedor de DNS. Ele segue a cadeia criada pelo apelido até resolver em um registro (ou registros) <code>A</code> e retornar esses registros <code>A</code> ao servidor DNS. Isso "achata" a cadeia <code>CNAME</code> nos registros <code>A</code> retornados e é indistinguível da consulta enviada. A consulta vê apenas um registro <code>A</code> puro, que não quebra a especificação do DNS e não apresenta nenhuma das desvantagens de um <code>CNAME</code>. </p><p>Esses registros virtuais podem ficar ao lado de outros registros na raiz, sem medo de comportamentos não intencionais. Dependendo do método de resolução de DNS do provedor ao seguir a cadeia <code>CNAME</code>, eles também podem ter benefícios de desempenho ao armazenar em cache pesquisas anteriores.</p><p>Para uma configuração simples de DNS, configuraríamos conforme abaixo. Essa solução tem todas as vantagens da técnica de apelidar ou rotular um nome de domínio e nenhum dos riscos de usá-lo no nível raiz.</p><pre><code>example.com      IN   ALIAS    my-app.herokuapp.com.www.example.com  IN   CNAME    my-app.herokuapp.com.</code></pre><p>Obrigado por ler este artigo!</p><p><em>Como sempre, estou aberto a quaisquer correções ou tópicos adicionais.</em></p><h3 id="refer-ncias-todas-em-ingl-s-">Referências (todas em inglês)</h3><ul><li><a href="http://www.itpro.co.uk/domain-name-system-dns/30232/what-is-a-dns-server" rel="noopener">What is a DNS Server</a></li><li><a href="https://www.wired.com/2010/02/Set_Up_a_DNS_Name_Server/" rel="noopener">Set Up a DNS Name Server</a></li><li><a href="https://support.dnsimple.com/categories/dns/" rel="noopener">Páginas de suporte do DNSimple</a> e <a href="https://blog.dnsimple.com/2014/01/why-alias-record/" rel="noopener">blog do ALIAS</a></li><li><a href="https://support.cloudflare.com/hc/en-us/articles/200169056-CNAME-Flattening-RFC-compliant-support-for-CNAME-at-the-root" rel="noopener">Suporte do Cloudflare</a> e <a href="https://blog.cloudflare.com/introducing-cname-flattening-rfc-compliant-cnames-at-a-domains-root/" rel="noopener">blog do CNAME</a></li><li><code><a href="https://www.madboa.com/geek/dig/" rel="noopener">dig</a></code><a href="https://www.madboa.com/geek/dig/" rel="noopener"> HowTo</a></li><li>Várias <a href="https://stackoverflow.com/questions/656009/how-to-overcome-root-domain-cname-restrictions/22659895#22659895">publicações</a> <a href="https://stackoverflow.com/questions/16022324/how-to-setup-dns-for-an-apex-domain-no-www-pointing-to-a-heroku-app">ótimas</a> no <a href="https://stackoverflow.com/questions/655235/is-root-domain-cname-to-other-domain-allowed-by-dns-rfc" rel="noopener">Stack Overflow</a> ou <a href="https://serverfault.com/questions/613829/why-cant-a-cname-record-be-used-at-the-apex-aka-root-of-a-domain/613830#613830">no</a> <a href="https://serverfault.com/questions/170194/why-cant-a-domains-root-be-a-cname?noredirect=1&amp;lq=1" rel="noopener">StackExchange</a></li><li>Entradas <a href="https://en.wikipedia.org/wiki/CNAME_record">bem-escritas</a> da Wikipédia</li><li><a href="https://www.netlify.com/blog/2017/02/28/to-www-or-not-www/" rel="noopener">Blog da Netlify</a> "To www or not www"</li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Como tornar o seu site da web em uma aplicação para dispositivos móveis com 7 linhas de JSON ]]>
                </title>
                <description>
                    <![CDATA[ por Ethan Uma nova abordagem para transformar o mecanismo da web em aplicações para dispositivos móveis nativas E se fosse dito a você que as 7 linhas de JSON acima, de cor laranja, seriam tudo que é preciso para transformar um site da web em uma aplicação para dispositivos móveis? ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/como-tornar-o-seu-site-da-web-em-uma-aplicacao-para-dispositivos-moveis-com-7-linhas-de-json/</link>
                <guid isPermaLink="false">62c9d0418e388806e97fbca9</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Deusiel da Cunha de Souza ]]>
                </dc:creator>
                <pubDate>Wed, 31 Aug 2022 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/08/1_x-5_XGJJhAgiBmLe54I3xg.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/how-to-turn-your-website-into-a-mobile-app-with-7-lines-of-json-631c9c9895f5/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Turn Your Website into a Mobile App with 7 Lines of JSON</a>
      </p><p>por Ethan</p><h3 id="uma-nova-abordagem-para-transformar-o-mecanismo-da-web-em-aplica-es-para-dispositivos-m-veis-nativas">Uma nova abordagem para transformar o mecanismo da web em aplicações para dispositivos móveis nativas</h3><p>E se fosse dito a você que as 7 linhas de JSON acima, de cor laranja, seriam tudo que é preciso para transformar um site da web em uma aplicação para dispositivos móveis? Não há a necessidade de reescrever seu site da web usando alguma API de framework somente para fazê-lo se comportar como uma aplicação para dispositivos móveis. Usaremos seu site como ele está e o tornaremos uma aplicação para dispositivos móveis, apenas alterando o URL.</p><p>E se, apenas ajustando um pouco o &nbsp;JSON, você pudesse acessar todas as APIs nativas, componentes de interface de usuário nativos e transições de visualização nativas prontas para uso?</p><p>Aqui está um exemplo inicial de como seria:</p><p>Observe como eu incorporei uma <a href="https://github.com/Jasonette">página da web do github.com</a>, mas que, no resto do layout, são todos componentes nativos da interface de usuário, como o <a href="https://docs.jasonette.com/document/#bodyheader">cabeçalho de navegação</a> e a <a href="https://docs.jasonette.com/document/#tabs">barra de guias inferior</a>. A transição é automaticamente nativa sem que você precise reescrever o site usando alguma API.</p><p>Antes de eu explicar como isso funciona, você poderia se perguntar: "Isso é legal, mas você pode fazer algo significativo além de apenas exibir a página web em um quadro (frame) de aplicação nativa?"</p><p>Ótima pergunta, porque esse é o tema principal desta publicação. Tudo o que você precisa fazer é criar um canal de comunicação bidirecional contínuo entre a visualização web e a aplicação, de modo que a aplicação principal possa acionar qualquer função JavaScript dentro da visualização web e a visualização web possa buscar dados no exterior para chamar APIs nativas.</p><p>Aqui está um exemplo do quê estamos falando:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/08/2-1-1.gif" class="kg-image" alt="2-1-1" width="428" height="762" loading="lazy"><figcaption>O gerador de QR code em ação</figcaption></figure><p>Observe que esta visualização contém:</p><ol><li>Cabeçalho de navegação nativo, completo com funcionalidade de transição integrada</li><li>Uma visualização web, que incorpora uma aplicação da web geradora de código QR</li><li>Um componente de entrada de bate-papo nativo na parte inferior</li></ol><p>Tudo isso pode ser descrito apenas ajustando alguns dos atributos do JSON que vimos acima.</p><p>Finalmente, observe que o código QR muda à medida que você insere algo na caixa de texto do bate-papo. Essa caixa de texto aciona uma função do JavaScript dentro da aplicação para a web de código QR que gera novamente a imagem.</p><p>Nenhum framework tentou resolver fundamentalmente esse problema de <strong>"integração perfeita da visualização da Web em aplicações nativas"</strong> porque todos estão focados em escolher o lado 100% nativo ou 100% HTML5.</p><p>Sempre que você ouve alguém falar sobre o futuro das aplicações para dispositivos móveis, provavelmente ouve: <strong>"Será a abordagem do HTML5 que vence? Ou será a nativa?"</strong></p><p>Nenhum deles vê <code>nativo</code> ou <code>html</code> como coisas que poderiam coexistir ou, até, criar sinergia e alcançar coisas que não seriam facilmente possíveis de outro modo.</p><p>Neste artigo será explicado:</p><ul><li>Por que misturar o mecanismo da web com componentes nativos é normalmente uma boa ideia.</li><li>Por que uma integração perfeita de HTML e nativo não é fácil e como eu implementei uma.</li><li>Mais importante ainda, como VOCÊ pode usá-la para criar sua própria aplicação instantaneamente.</li></ul><h3 id="por-que-voc-usaria-html-em-uma-aplica-o-nativa">Por que você usaria HTML em uma aplicação nativa?</h3><p>Antes de prosseguirmos, vamos primeiro discutir se isso é mesmo uma boa ideia e quando você pode querer adotar essa abordagem. Aqui estão alguns casos de uso em potencial:</p><h4 id="1-usar-recursos-nativos-da-web"><strong><strong>1. </strong></strong>Usar recursos nativos da web</h4><p>Algumas partes da sua aplicação podem ser implementadas com mais qualidade usando o mecanismo da web. Por exemplo, o Websocket é um recurso nativo da web projetado para o ambiente da web. Nesse caso, faz sentido usar o mecanismo da web integrado (<strong>WKWebView para iOS</strong> e <strong>WebView para Android</strong>) em vez de instalar uma biblioteca de terceiros que essencialmente "emula" o Websocket.</p><p>Não há a necessidade de código adicional para fazer algo que podemos fazer livremente, o que nos leva ao próximo tópico.</p><h4 id="2-evitar-o-uso-de-arquivos-bin-rios-muito-grandes"><strong><strong>2. </strong>Evitar o uso de arquivos binários muito grandes</strong></h4><p>Você pode querer incorporar rapidamente recursos que, de outro modo, exigiriam uma enorme biblioteca de terceiros.</p><p>Por exemplo, para incorporar um gerador de imagem de código QR nativamente, você precisará instalar alguma biblioteca de terceiros que aumentará o tamanho do arquivo binário. Se você, no entanto, usar o mecanismo de visualização da web e uma biblioteca JavaScript por meio de um simples <code>&lt;script src&gt;</code>, obterá tudo isso de graça e não precisará instalar nenhuma biblioteca de terceiros.</p><h4 id="3-falta-de-uma-biblioteca-m-vel-confi-vel"><strong><strong>3. </strong>Falta de uma biblioteca móvel confiável</strong></h4><p>Para algumas tecnologias de ponta, ainda não existe uma implementação móvel confiável e estável.</p><p>Felizmente, a maioria dessas tecnologias possui implementações na web. Portanto, a maneira mais eficiente de integrá-las é usar uma biblioteca JavaScript.</p><h4 id="4-construa-aplica-es-parcialmente-nativas-e-parcialmente-baseadas-na-web"><strong><strong>4. </strong></strong>Construa aplicações parcialmente nativas e parcialmente baseadas na web</h4><p>Muitos novos desenvolvedores que desejam transformar seu site em uma aplicação para dispositivos móveis ficam desencorajados ou sobrecarregados quando descobrem que alguns dos recursos existentes em seu site são muito complexos de reescrever rapidamente do zero para uma plataforma móvel.</p><p>Por exemplo, você pode ter uma única página e ela, ainda assim, pode ser complexa demais para ser convertida imediatamente em uma aplicação para dispositivos móveis, mas o restante do seu site pode ser convertido facilmente.</p><p>Nesse caso, seria bom se houvesse uma maneira de construir a maior parte da aplicação nativamente, mas, para essa página da web complexa específica, de algum modo, integrá-la perfeitamente à aplicação como HTML.</p><h3 id="como-funciona"><strong>Como Funciona?</strong></h3><h4 id="a-jasonette"><strong><strong>A. Jasonette</strong></strong></h4><p>O Jasonette é uma abordagem de código aberto baseada em marcação para criar aplicações nativas multiplataforma.</p><p>É como um navegador da web que, em vez de interpretar o HTML em páginas da web, interpreta o JSON em aplicações nativas no iOS e Android.</p><p>Assim como todos os navegadores da Web têm exatamente o mesmo código, mas podem fornecer todos os tipos de aplicações da Web diferentes interpretando vários HTMLs sob demanda, todas as aplicações do Jasonette têm exatamente o mesmo binário e interpretam vários JSONs sob demanda para criar a sua. Os desenvolvedores nunca precisam tocar no código. Em vez disso, você cria aplicações escrevendo um código que se traduz em uma aplicação nativa em tempo real.</p><p>Você pode descobrir mais sobre Jasonette <a href="https://medium.freecodecamp.org/how-to-build-cross-platform-mobile-apps-using-nothing-more-than-a-json-markup-f493abec1873">aqui</a>.</p><p>Enquanto o Jasonette, em sua essência, tem a ver com a construção de aplicações nativas, este artigo específico é sobre a integração do HTML no mecanismo nativo, então vamos começar.</p><h4 id="b-cont-iner-da-web-do-jasonette"><strong><strong>B. </strong>Contêiner da web do Jasonette</strong></h4><p>Aplicações nativas são ótimas. Porém, algumas vezes, precisamos fazer uso das capacidades da web.</p><p>Integrar visualizações da web em aplicações móveis nativas, no entanto, é um trabalho complicado. Uma integração perfeita requer que:</p><ol><li><strong>A visualização da web seja integrada como parte do layout nativo<strong>:</strong></strong> a visualização da web deve se integrar à aplicação como parte do layout nativo e ser tratada como qualquer outro componente de interface do usuário. Caso contrário, parecerá desajeitada e exatamente como é - um site.</li><li><strong>A aplicação principal possa controlar o contêiner de visualização da web da aplicação subordinada: </strong>a aplicação principal deve poder controlar livremente a visualização da web da aplicação subordinada.</li><li><strong>O contêiner da web da aplicação subordinada possa acionar eventos nativos na aplicação principal</strong>: a aplicação subordinada deve ser capaz de acionar os eventos da aplicação principal para executar APIs nativas.</li></ol><p>É bastante trabalho, então trabalhei apenas na primeira peça do quebra-cabeça - <strong>simplesmente incorporando um contêiner web no layout nativo</strong> - e o lancei como versão 1:</p><p><strong><a href="http://jasonette.com/webcontainer/">Contêiner web JSON</a> &nbsp;</strong><br><em><a href="http://jasonette.com/webcontainer/">O HTML dentro de JSON se transforma em componentes nativos jasonette.com</a></em></p><p>Isso já era bastante útil, mas ainda tinha a limitação de não ser interativo.</p><p>A aplicação principal não pode controlar o contêiner da web da aplicação subordinada e a aplicação subordinada não pode notificar a principal sobre nenhum evento, <strong>mantendo o contêiner da web completamente isolado do mundo exterior.</strong></p><h4 id="c-jasonette-web-container-2-0-tornando-o-interativo"><strong><strong>C. Jasonette</strong> W<strong>eb </strong>Container<strong> 2.0: </strong>tornando-o interativo</strong></h4><p>Após lançar a versão 1, foram feitas experiências com a segunda peça do quebra-cabeça - <strong>adicionar interatividade ao contêiner da web.</strong></p><p>A próxima seção explica as soluções que foram adicionadas para transformar o contêiner da web anteriormente estático em interativo, tornando-o significativamente<strong> </strong>mais poderoso.</p><h3 id="implementa-o-cont-iner-da-web-interativo"><strong>Implementação: contêiner da web interativo</strong></h3><h4 id="1-carregar-pelo-url"><strong><strong><strong><strong>1. </strong></strong></strong>Carregar pelo URL</strong></h4><h4 id="problema"><strong><strong>Problem</strong>a</strong></h4><p>Anteriormente, na versão 1, foi usado um contêiner da web como visualização em segundo plano do componente. Primeiramente, o <a href="https://jasonette.com/webcontainer/" rel="noopener"><code>$jason.body.background.type</code></a><code> </code>tinha que ser configurado para <code><a href="https://jasonette.com/webcontainer/" rel="noopener">"html"</a></code> e, então, era preciso escrever manualmente o HTML no atributo <code><a href="https://jasonette.com/webcontainer/" rel="noopener">$jason.body.background.text</a></code>, deste modo:</p><pre><code>{  "$jason": {    "head": {      ...    },    "body": {      "background": {        "type": "html",        "text": "&lt;html&gt;&lt;body&gt;&lt;h1&gt;Hello World&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;"      }    }  }}</code></pre><p>‌Naturalmente, as pessoas queriam instanciar o contêiner usando simplesmente um URL em vez de ter que digitar todo código HTML em uma única linha.</p><h4 id="solu-o">Solução</h4><p>O Web Container 2.0 adicionou o atributo <code>url.</code> Agora, um arquivo local <code>file:// </code>pode ser adicionado e será carregado dentro da aplicação.</p><pre><code>{  "$jason": {    "head": {      ...    },    "body": {      "background": {        "type": "html",        "url": "file://index.html"      }    }  }}</code></pre><p>A alternativa seria adicionar um URL <code>http[s]://</code> remoto como este (carrega o HTML remotamente):</p><pre><code>{  "$jason": {    "head": {      ...    },    "body": {      "background": {        "type": "html",        "url": "https://news.ycombinator.com"      }    }  }}</code></pre><h2 id="2-comunica-o-entre-a-aplica-o-principal-cont-iner-da-web">2. Comunicação entre a aplicação principal &lt;=&gt; Contêiner da web</h2><h4 id="problema-1"><strong><strong>Problema</strong></strong></h4><p>Anteriormente, o contêiner da web apenas servia para mostrar conteúdo, mas não para interatividade. Isso significava que <strong>nenhuma das situações abaixo era possível:</strong></p><ol><li><strong><strong>Jasonette =&gt; </strong>Contêiner da Web</strong>: chamar funções do JavaScript do Jasonette dentro do contêiner da web.</li><li><strong><strong>Web Container =&gt; </strong>Jasonette</strong>: chamar APIs nativas dentro do código do contêiner da web.</li></ol><p>Tudo que se podia fazer era mostrar o contêiner da web. Isso era similar a inserir um <em>iframe</em> em uma página da web. Porém, a página web não tinha acesso ao que havia no <em>iframe</em>.</p><h4 id="solu-o-1"><strong><strong>Solução</strong></strong></h4><p>A principal finalidade do Jasonette é criar um padrão de linguagem para descrever uma aplicação para dispositivos móveis multiplataforma. Neste caso, precisa-se de uma linguagem de marcação que descreva extensamente a comunicação entre a aplicação principal e o contêiner da web subordinado.</p><p>Para atingir esse objetivo, a solução foi uma comunicação baseada em <code><a href="http://www.jsonrpc.org/specification" rel="noopener">JSON-RPC</a></code>, um canal entre a aplicação principal e o contêiner da web subordinado. Como tudo em Jasonette é escrito em objetos JSON, fez muito sentido usar o padrão <code>JSON-RPC</code> como protocolo de comunicação.</p><p>‌ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/08/image-2-1.png" class="kg-image" alt="image-2-1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/08/image-2-1.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/08/image-2-1.png 800w" sizes="(min-width: 720px) 720px" width="800" height="613" loading="lazy"><figcaption>Antes e depois do Web Container 2.0</figcaption></figure><p>Para chamar uma função do Javascript dentro do contêiner da web, temos que declarar uma ação chamada <code>$agent.request:</code></p><pre><code>{  "type": "$agent.request",  "options": {    "id": "$webcontainer",    "method": "login",    "params": ["username", "password"]  }}</code></pre><p><a href="https://docs.jasonette.com/agents/#1-agentrequest" rel="noopener"><code>$agent.request</code></a> é uma API nativa que aciona uma requisição JSON-RPC dentro do contêiner da web. Para usá-la é preciso passar um objeto <code>options</code> como parâmetro.</p><p>O objeto <code>options</code> é, na verdade, a requisição <a href="http://www.jsonrpc.org/specification#conventions" rel="noopener">JSON-RPC</a>, que será enviada ao contêiner da web. Vamos dar uma olhada no que cada atributo significa:</p><ul><li><code>id</code>: O contêiner da web é construído sobre uma camada inferior chamada <a href="https://jasonette.com/agent/" rel="noopener">agent</a>. Normalmente, pode-se ter múltiplos <em>agents </em>para uma única visualização e cada <em>agent </em>terá sua identificação única (ID). Porém, um contêiner da web é um tipo especial de <em>agent </em>que só pode ter o id <code><a href="https://docs.jasonette.com/web/#1-background-web-container-is-an-agent" rel="noopener">$webcontaine</a>r</code>. É por isso que essa ID foi usada.</li><li><code>method</code>: &nbsp;O nome da função Javascript que será chamada</li><li><code>params</code>: &nbsp;O array com parâmetros a serem passados para a função Javascript.</li></ul><p>O código inteiro ficaria assim:</p><pre><code>{  "$jason": {    "head": {      "actions": {        "$load": {          "type": "$agent.request",          "options": {            "id": "$webcontainer",            "method": "login",            "params": ["alice", "1234"]          }        }      }    },    "body": {      "header": {        "title": "Web Container 2.0"      },      "background": {        "type": "html",        "url": "file://index.html"      }    }  }}</code></pre><p>Explicação:</p><p>Quando a visualização carregar (<code><a href="https://docs.jasonette.com/actions/#1-load" rel="noopener">$jason.head.actions.$load</a></code>), faça uma requisição JSON-RPC dentro do <em>agent </em>do contêiner da web (<a href="https://docs.jasonette.com/agents/#1-agentrequest" rel="noopener"><code>$agent.request</code></a>), onde a requisição será detalhada em <code>options</code>.</p><p>O contêiner da web é definido em <a href="https://docs.jasonette.com/web/#in-depth-on-background-web-container" rel="noopener"><code>$jason.body.background</code></a>, que, nesse caso, carrega um arquivo local chamado <code>file://index.html</code>.</p><p>A função de <code>login</code> será chamada e os dois argumentos em <code>params </code>( <code>"alice"</code> e <code>"1234"</code>) serão passados.</p><pre><code>login("alice", "1234")</code></pre><p>Foi explicado apenas como a aplicação principal pode acionar funções do Javascript na aplicação subordinada, mas também é possível fazer o oposto e <a href="https://docs.jasonette.com/agents/#3-agenttrigger">permitir que o contêiner da web acione a API nativa da aplicação principal</a>.</p><p>Para saber mais, verifique a <a href="https://docs.jasonette.com/agents/">documentação do agent</a>.</p><h4 id="exemplo"><strong><strong>Ex</strong>e<strong>mplo</strong></strong></h4><p>Voltemos ao exemplo do QR Code disponibilizado anteriormente:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/08/2-1-2.gif" class="kg-image" alt="2-1-2" width="428" height="762" loading="lazy"><figcaption>O gerador de QR code em ação</figcaption></figure><ol><li><a href="https://docs.jasonette.com/document/#input">O rodapé é 100% nativo</a>.</li><li>O QR Code é gerado pelo contêiner como uma <a href="https://github.com/Jasonette/Jasonpedia/blob/gh-pages/webcontainer/agent/fn/agent.html">aplicação da web</a>.</li><li>Quando o usuário digita alguma coisa e pressiona "Generate", ele chama <code>$agent.request</code> &nbsp;dentro do <em>agent</em> do contêiner da web, chamando a <a href="https://github.com/Jasonette/Jasonpedia/blob/gh-pages/webcontainer/agent/fn/agent.html#L22">função "qr" do JavaScript</a>.</li></ol><p>Você pode ver um exemplo <a href="https://github.com/Jasonette/Jasonpedia/blob/gh-pages/webcontainer/agent/fn/index.json">aqui</a>.</p><h2 id="3-inje-o-de-script">3. Injeção de script</h2><h4 id="problema-2"><strong><strong>Problema</strong></strong></h4><p>Às vezes, é necessário injetar dinamicamente o código em Javascript no contêiner da web após o carregamento inicial do HTML.</p><p>Imagine que se queira construir um navegador da web personalizado. Poderia ser necessário injetar seu próprio código em Javascript na visualização para personalizar o comportamento da visualização, algo parecido com o modo como as extensões de navegador funcionam.</p><p>Ainda que não se esteja construindo um navegador da web, poderia ser necessário injetar um script a qualquer momento que um comportamento personalizado fosse desejado em um URL em que não se tivesse o controle do conteúdo. A única forma de comunicação entre a aplicação nativa e o contêiner da web é através da API do <code>$agent.</code> Se o conteúdo do HTML não puder ser modificado, no entanto, a única forma de adicionar o <code>$agent</code> ao contêiner da web é através da injeção dinâmica de script.</p><h4 id="solu-o-2"><strong><strong>Solução</strong></strong></h4><p>Como mencionado anteriormente, o contêiner da web <code>$jason.body.background</code> &nbsp;é apenas outro <code>agent</code>. Isso significa que o método <a href="https://docs.jasonette.com/agents/#7-agentinject" rel="noopener"><code>$agent.inject</code></a> pode ser usado (ele fica disponível para todos os <em>agents</em>).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/08/image-4-1.png" class="kg-image" alt="image-4-1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/08/image-4-1.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/08/image-4-1.png 800w" sizes="(min-width: 720px) 720px" width="800" height="676" loading="lazy"><figcaption>Imagem que mostra a aplicação de código em Javascript personalizado ao contêiner da web</figcaption></figure><h4 id="4-gerenciamento-de-cliques-em-url"><strong><strong><strong><strong>4. Gerenciamento de cliques em URL</strong></strong></strong></strong></h4><p>No passado, existia apenas duas formas pelas quais um contêiner da web poderia gerenciar cliques nos links:</p><ol><li><strong>Somente leitura<strong>:</strong></strong> &nbsp;Tratava o contêiner da web como somente leitura e ignorava todos os eventos, como toques ou rolagem de página. Todos os contêineres da web são somente leitura, a menos que seja indicado para eles se comportarem como navegadores normais, como descrito adiante.</li><li><strong>Comportamento padrão do navegador<strong>: </strong></strong>Permite que os usuários interajam com a página, se comportando como um navegador normal. Isso pode ser feito configurando <code>"type": "$default"</code> como atributo de <code>action.</code></li></ol><h4 id="problema-3"><strong><strong>Problema</strong></strong></h4><p>As duas soluções são do tipo "<strong>ou tudo ou nada</strong>".</p><ul><li>No caso de "somente leitura", todas as interações são completamente ignoradas pelo contêiner da web.</li><li>No caso do comportamento padrão do navegador da web, o contêiner funciona literalmente como um navegador. Quando um link é clicado, o navegador vai até esse link e recarrega a página, assim como uma página web normal faria. Não havia meios de capturar o clique e chamar alguma API nativa.</li></ul><h4 id="solu-o-3"><strong><strong>Solução</strong></strong></h4><p>Com o novo contêiner da web, agora é possível associar as ações ao <code>action</code> no contêiner <code>$jason.body.background</code> para gerenciar eventos de clique.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/08/image-5-2.png" class="kg-image" alt="image-5-2" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/08/image-5-2.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/08/image-5-2.png 800w" sizes="(min-width: 720px) 720px" width="800" height="637" loading="lazy"><figcaption>Imagem que mostra o gerenciamento de eventos de clique</figcaption></figure><p>Vamos ver um exemplo:</p><pre><code>{  "$jason": {    "head": {      "actions": {        "displayBanner": {          "type": "$util.banner",          "options": {            "title": "Clicked",            "description": "Link {{$jason.url}} clicked!"          }        }      }    },    "body": {      "background": {        "type": "html",        "url": "file://index.html",        "action": {          "trigger": "displayBanner"        }      }    }  }}</code></pre><p><br>Aqui o <code>"trigger": "displayBanner"</code> &nbsp;foi adicionado ao contêiner da web. Isso significa que, quando um usuário clicar em qualquer link no contêiner da web, será acionada a ação do <code>displayBanner</code> em vez de a visualização web ter que fazer esse gerenciamento.</p><p>Se a ação do <code>displayBanner</code> for observada mais de perto, será possível notar a variável <code>$jason</code>. Por exemplo, se o URL clicado for <code>"https://google.com"</code>, então a variável <code>$jason</code> terá o seguinte conteúdo:</p><pre><code>{  "url": "https://google.com"}</code></pre><p><br>Isso significa que os acionadores podem ser usados de maneira seletiva dependendo do conteúdo de <code><a href="https://docs.jasonette.com/web/#b-intercept-url-visits" rel="noopener">$jason.url</a></code><a href="https://docs.jasonette.com/web/#b-intercept-url-visits" rel="noopener"> </a>.</p><p>Vejamos outro exemplo de implementação de um navegador da web personalizado:</p><pre><code>{  "$jason": {    "head": {      "actions": {        "handleLink": [{          "{{#if $jason.url.indexOf('signin') !== -1 }}": {            "type": "$href",            "options": {              "url": "file://key.html"            }          }        }, {          "{{#else}}": {            "type": "$default"          }        }]      }    },    "body": {      "background": {        "type": "html",        "url": "file://index.html",        "action": {          "trigger": "handleLink"        }      }    }  }}</code></pre><p>Primeiro, verificamos se o URL contém o texto <code>signin</code>. Depois, executamos duas ações diferentes, dependendo do resultado.</p><ol><li>Se contiver <code>signin</code>, uma nova visualização será aberta para fazer a gerência da autenticação nativamente.</li><li>Se não contiver <code>signin</code>, apenas executará a ação <code>"type": "$default"</code> , fazendo-o se comportar como um navegador da web padrão.</li></ol><h3 id="exemplo-de-uso"><strong>Exemplo de uso</strong></h3><h4 id="construindo-um-navegador-da-web-personalizado"><strong>Construindo um navegador da web personalizado</strong></h4><p>Agora, nós podemos nos aproveitar do fato de os novos contêineres da web poderem:</p><ol><li>Pegar o atributo <code>url</code> e carregá-lo, funcionando como um navegador completo</li><li>Seletivamente, gerenciar cliques em links dependendo do URL</li></ol><p>Podemos ainda construir uma aplicação de navegador da web com apenas algumas linhas de JSON. Agora que podemos capturar cada clique, podemos usar o <code>$jason.url</code> e executar quaisquer ações que desejarmos, dependendo do URL.</p><p>Por exemplo, vamos analisar o exemplo seguinte:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/08/3-1.gif" class="kg-image" alt="3-1" width="428" height="762" loading="lazy"><figcaption>GIF que mostra o funcionamento de um navegador da web personalizado</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/08/4-1.gif" class="kg-image" alt="4-1" width="428" height="762" loading="lazy"><figcaption>GIF que mostra o funcionamento de um navegador da web personalizado</figcaption></figure><p>Na primeira imagem, podemos observar que, clicando no link, o navegador se comporta como um navegador comum (<code>"type": "$default"</code>).</p><p>Na segunda imagem, podemos observar que, clicando no link, o navegador se comporta como uma aplicação nativa e mostra uma visualização baseada em JASON.</p><p>Tudo isso é feito executando diferentes ações baseadas nos valores de <code>$jason.url</code>.</p><p><strong>Passo 1. Insira uma ação <strong><code>visit</code> </strong>no contêiner da web, desta forma:</strong></p><pre><code>{  ...  "body": {    "background": {      "type": "html",      "url": "https://news.ycombinator.com",      "action": {        "trigger": "visit"      }    }  }}</code></pre><p><br><strong>Passo<strong> 2. </strong>Execute ações relevantes dentro de <strong><code>visit,</code> </strong>baseadas no conteúdo de <strong><code>$jason.url</code></strong></strong></p><p>No código a seguir, verifica-se se <code>$jason.url</code> contém <code>newest</code>, <code>show</code>, <code>ask</code> e outras coisas (elas estão no topo do menu de itens). Se elas estiverem presentes, então o contêiner da web se comporta como um navegador comum, configurando o &nbsp;<code>"type": "$default"</code> .</p><p>Por outro lado, se não estiverem presentes, faremos uma transição para uma nova visualização nativa, usando o <code>$href</code> e passando o link clicado como parâmetro.</p><pre><code>..."actions": {  "visit": [    {      "{{#if /\\/(newest|show|ask)$/.test($jason.url) }}": {        "type": "$default"      }    },    {      "{{#else}}": {        "type": "$href",        "options": {          "url": "https://jasonette.github.io/Jasonpedia/webcontainer/agent/hijack.json",          "preload": {            "background": "#ffffff"          },          "options": {            "url": "{{$jason.url}}"          }        }      }    }  ]},</code></pre><p><br>Dê uma olhada no código JSON inteiro <a href="https://github.com/Jasonette/Jasonpedia/blob/gh-pages/webcontainer/agent/hijack.json">aqui</a> (são apenas 48 linhas de código).</p><h4 id="uma-aplica-o-h-brida-instantaneamente"><strong>Uma aplicação "híbrida" instantaneamente</strong></h4><p>Normalmente, quando as pessoas falam sobre aplicações híbridas, elas querem dizer aplicações da web dentro de um frame nativo.</p><p>Isso, porém, não é o que eu quero dizer. Quando digo "híbrida", quero dizer híbrida de verdade, como uma aplicação que pode ter múltiplas visualizações nativas e múltiplas visualizações da web simultaneamente. A aplicação também pode ter múltiplas interfaces de usuário e o contêiner da web pode renderizar isso tudo no mesmo layout nativo.</p><p><strong>A fronteira entre a visualização da aplicação da web e a visualização para dispositivos móveis é tão imperceptível, que fica difícil dizer onde uma começa e a outra termina.</strong>‌</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/08/5-1.gif" class="kg-image" alt="5-1" width="400" height="712" loading="lazy"><figcaption>GIF mostrando as capacidades do Jasonbase</figcaption></figure><p>Nesse exemplo, eu criei uma aplicação que mostra <a href="https://www.jasonbase.com/" rel="noopener">jasonbase.com</a> em um contêiner da web como página principal.</p><p>Jasonbase é um serviço gratuito de hospedagem de JSON que eu construí para hospedar facilmente arquivos JSON para aplicações do Jasonette.</p><p>Naturalmente, é apenas um site, mas eu o incorporei no Jasonette para que, quando você clicar no link, em vez de abrir a página da web, faça uma transição <code>$href</code> nativa para uma visualização do JASON nativa.</p><p><strong>Não precisei mexer em nenhum código do Jasonbase.com para criar essa aplicação.</strong></p><p><strong>Eu simplesmente incorporei o site no Jasonette como um contêiner da web e capturei os cliques do link para tratá-los nativamente, para que ele possa fazer todas as coisas nativas, como acionar APIs nativas e fazer transições nativas.</strong></p><p>Você pode conferir o código <a href="https://github.com/Jasonette/Jasonpedia/blob/gh-pages/webcontainer/agent/hybrid.json">aqui</a>.</p><h3 id="conclus-o"><strong>Conclusão</strong></h3><p>Na minha opinião, o que faz tudo isso funcionar fabulosamente é que <strong>tudo é cuidado no nível do framework</strong>. Todo o trabalho duro é feito nos bastidores.</p><p>Em vez de colocar a carga sobre os desenvolvedores de aplicações de implementar tudo do zero:</p><ul><li>Incorporar uma visualização da web no layout nativo</li><li>Criar uma ponte de JavaScript para que a aplicação possa fazer chamadas de função na visualização da web</li><li>Criar uma arquitetura nativa de manipulação de eventos para que a visualização da web possa acionar eventos nativos na aplicação principal</li></ul><p>A solução foi criar uma abstração composta por:</p><ol><li><strong>Linguagem de marcação declarativa<strong>:</strong></strong> para descrever como incorporar uma visualização da web em uma aplicação nativa</li><li><strong>Protocolo de comunicação <strong>(JSON-RPC):</strong></strong> para permitir interações simples entre a aplicação e suas visualizações da web subordinadas.</li></ol><p>Não afirmo que essa abordagem seja a solução definitiva para resolver tudo, mas fico feliz em dizer que essa foi uma ótima solução para meu próprio caso de uso.</p><p>Eu tentava construir uma aplicação baseada em uma tecnologia de ponta que não possui implementações móveis estáveis ​​e confiáveis ​​(e não está claro se haverá uma implementação móvel devido à natureza do protocolo). Felizmente, havia implementações de JavaScript que podiam ser integradas facilmente à aplicação sem problemas.</p><p>No geral, foi ótimo e estou satisfeito com o resultado. A documentação está atualizada para refletir todos os novos recursos. Portanto, sinta-se à vontade para pesquisar e brincar.</p><blockquote>Aviso: com grandes poderes vêm grandes responsabilidades</blockquote><p>Eu gostaria de terminar com um aviso: por maior que seja esse poder recém-descoberto, acho que você precisa manter um equilíbrio para criar uma aplicação com uma ótima experiência do usuário.</p><p>Alguns podem pegar isso e criar uma aplicação inteira usando apenas visualizações da web, mas você acabará com uma aplicação que é basicamente apenas um site, o que anula o objetivo de criar uma aplicação dedicada.</p><p>Enfatizo que não estou dizendo que você deve sempre construir aplicações com HTML e nativa. Estou dizendo que isso pode ser muito útil para muitas pessoas em diferentes situações. Só não exagere.</p><h3 id="para-saber-mais-">Para saber mais:</h3><p>Existem muitas configurações diferentes nas quais o núcleo nativo do Jasonette e seu contêiner da web subordinado podem se comunicar para fazer as coisas de maneiras criativas e poderosas. Esta publicação é apenas o início.</p><p>No futuro, o autor planeja compartilhar mais desses casos de uso e tutoriais. Se estiver interessado, siga-o no <a href="https://medium.com/@gliechtenstein" rel="noopener">Medium</a> ou no <a href="https://twitter.com/jasonclient" rel="noopener">Twitter</a>.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Como criar a sua própria criptomoeda usando Python ]]>
                </title>
                <description>
                    <![CDATA[  > Com a fama recente das criptomoedas, a blockchain criou uma tendência no mundo da tecnologia. A blockchain tem atraído muita atenção, principalmente por conta de sua capacidade de garantir segurança, reforçar a descentralização e acelerar processos, especialmente no setor financeiro. Essencialmente, a blockchain é um banco de dados ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/como-criar-a-sua-propria-criptomoeda-usando-python/</link>
                <guid isPermaLink="false">62c2395ea3520206e79cfc51</guid>
                
                    <category>
                        <![CDATA[ Criptomoeda ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Deusiel da Cunha de Souza ]]>
                </dc:creator>
                <pubDate>Tue, 05 Jul 2022 18:47:48 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/blockchain-3448502_1920-2.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/create-cryptocurrency-using-python/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Create Your Own Cryptocurrency Using Python</a>
      </p><p></p><blockquote>Com a fama recente das criptomoedas, a blockchain criou uma tendência no mundo da tecnologia. A blockchain tem atraído muita atenção, principalmente por conta de sua capacidade de garantir segurança, reforçar a descentralização e acelerar processos, especialmente no setor financeiro.</blockquote><p>Essencialmente, a blockchain é um banco de dados público que documenta e autentica, de maneira irreversível, a posse e a transmissão de ativos digitais. Moedas digitais, como o Bitcoin e o Ethereum, são baseadas nesse conceito. A blockchain é uma tecnologia maravilhosa, que você pode usar para transformar suas aplicações.</p><p>Atualmente, governos, organizações e indivíduos têm usado a tecnologia da blockchain para criar suas próprias criptomoedas e para evitar ficar pra trás. Notavelmente, quando o Facebook propôs sua própria criptomoeda, chamada de Libra, o anúncio gerou muita repercussão ao redor do mundo.</p><p>E se você pudesse também fazer como eles e criar a sua própria versão de criptomoeda?</p><p>Eu pensei sobre isso e decidi desenvolver um algoritmo que cria uma criptomoeda.</p><p>Decidi chamá-la de: <strong><strong>fccCoin</strong></strong>.</p><p>Neste tutorial, vou ilustrar passo a passo o processo usado para construir essa moeda digital (usando conceitos de programação orientada a objetos da linguagem Python para fazer isso).</p><p>Aqui está o modelo básico de algoritmo para blockchain usado para criar a &nbsp;<strong><strong>fccCoin</strong></strong>:</p><pre><code class="language-python">class Block:

    def __init__():

    #Primeiro bloco da classe

        pass
    
    def calculate_hash():
    
    #Calcula o hash para cada bloco
        
    
class BlockChain:
    
    def __init__(self):
     # Método Construtor
    pass
    
    def construct_genesis(self):
        # Constrói o bloco inicial
        pass

    def construct_block(self, proof_no, prev_hash):
        # Constrói um novo bloco e adiciona-o à cadeia de blocos.
        pass

    @staticmethod
    def check_validity():
        # Verifica se a blockchain é válida.
        pass

    def new_data(self, sender, recipient, quantity):
        # Adiciona uma nova transação ao registro de transações
        pass

    @staticmethod
    def construct_proof_of_work(prev_proof):
        # Protege a blockchain de ataques
        pass
   
    @property
    def last_block(self):
        # Retorna o último bloco da cadeia
        return self.chain[-1]

</code></pre><p>Agora, deixe-me explicar como fazer...</p><h2 id="1-criando-a-primeira-classe-block"><strong><strong>1. </strong>Criando a primeira classe "Block"</strong></h2><p>Uma blockchain é uma cadeia de blocos que são unidos uns aos outros (soa familiar, não soa?).</p><p>Essa cadeia de blocos é construída de tal forma que, se um dos blocos for adulterado, toda a cadeia se torna inválida.</p><p>Aplicando o conceito acima, criamos a primeira classe Block.</p><pre><code class="language-python">import hashlib
import time

class Block:

    def __init__(self, index, proof_no, prev_hash, data, timestamp=None):
        self.index = index
        self.proof_no = proof_no
        self.prev_hash = prev_hash
        self.data = data
        self.timestamp = timestamp or time.time()

    @property
    def calculate_hash(self):
        block_of_string = "{}{}{}{}{}".format(self.index, self.proof_no,
                                              self.prev_hash, self.data,
                                              self.timestamp)

        return hashlib.sha256(block_of_string.encode()).hexdigest()

    def __repr__(self):
        return "{} - {} - {} - {} - {}".format(self.index, self.proof_no,
                                               self.prev_hash, self.data,
                                               self.timestamp)
</code></pre><p>Como podemos ver no código acima, foi definido o método <strong><strong>__init__()</strong>, </strong>que será executado quando o bloco for instanciado, assim como em qualquer outra classe em Python.</p><p>Foram definidos os seguintes parâmetros para a inicialização da função:</p><ul><li><strong><strong>self</strong> </strong>— refere-se à instância do bloco de classe, tornando possível acessar os métodos e atributos associados a ela;</li><li><strong><strong>index</strong> </strong>— mantém o controle da posição do bloco dentro da cadeia de blocos (blockchain);</li><li><strong><strong>proof_no</strong> </strong>— é o número produzido durante a criação de um bloco (chamada de mineração);</li><li><strong><strong>prev_hash</strong> </strong>— refere-se ao hash do bloco anterior da cadeia;</li><li><strong><strong>data</strong> </strong>— mantém o registro de todas as operações feitas, com informações como a quantidade comprada;</li><li><strong><strong>timestamp</strong> </strong>— possui a data e a hora em que as transações aconteceram.</li></ul><p>O segundo método da classe, <strong><strong>calculate_hash</strong></strong>, gerará um <em>hash</em> para os blocos que usarem os parâmetros descritos acima. O módulo SHA-256 foi importado no projeto para auxiliar na obtenção de <em>hashes</em> dos blocos.</p><p>Depois que os parâmetros forem passados para o algoritmo de <em>hash</em>, a função então retornará uma string de 256 bits representando o conteúdo do bloco.</p><p>É assim que a segurança é garantida em blockchains. Cada bloco terá um <em>hash</em> e esse <em>hash </em>fará referência a um hash do bloco anterior.</p><p>Dessa forma, se alguém tentar comprometer qualquer bloco da cadeia, os outros blocos terão <em>hashes </em>inválidos, o que fará com que a cadeia inteira seja inválida.</p><p>Até o momento, um bloco tem essa aparência:</p><pre><code class="language-python">{
    "index": 2,
    "proof": 21,
    "prev_hash": "6e27587e8a27d6fe376d4fd9b4edc96c8890346579e5cbf558252b24a8257823",
    "transactions": [
        {'sender': '0', 'recipient': 'Quincy Larson', 'quantity': 1}
    ],
    "timestamp": 1521646442.4096143
}
</code></pre><h2 id="2-construindo-a-classe-blockchain"><strong><strong>2. </strong>Construindo a classe Blockchain</strong></h2><p>A ideia principal por trás da blockchain, assim como o próprio nome sugere, envolve encadear vários blocos uns aos outros.</p><p>Portanto, será construída uma classe <strong><strong>Blockchain</strong></strong>, que será muito útil no gerenciamento do trabalho de toda a cadeia. Será nela que a maior parte do trabalho será feito.</p><p>A classe <strong><strong>Blockchain</strong></strong> terá vários métodos auxiliares para desempenhar várias funções na cadeia de blocos.</p><p>Será explicado o que cada um desses métodos fará na classe.</p><h3 id="a-m-todo-construtor"><strong><strong>a. </strong>Método construtor</strong><br></h3><p>Esse método é o responsável pela inicialização da classe.</p><pre><code class="language-python">class BlockChain:

    def __init__(self):
        self.chain = []
        self.current_data = []
        self.nodes = set()
        self.construct_genesis()
</code></pre><p>Aqui estão as atribuições do método construtor:</p><ul><li><strong><strong>self.chain</strong> </strong>— variável que mantém todos os blocos;</li><li><strong><strong>self.current_data</strong> </strong>— variável que controla todas as transações feitas nesse bloco;</li><li><strong><strong>self.construct_genesis()</strong> </strong>— método que cuidará da criação do bloco inicial.</li></ul><h3 id="b-construindo-o-bloco-inicial"><strong><strong>b. </strong>Construindo o bloco inicial</strong><br></h3><p>A blockchain precisa do método <em><em><strong><strong>construct_genesis</strong></strong></em><strong> </strong>para criar o bloco inicial da cadeia. Normalmente, nas blockchains, esse bloco é especial, pois ele simboliza o início da cadeia.</em></p><p>Agora, o bloco inicial será construído com valores padrão passados ao método <em><em><strong><strong>construct_block</strong></strong></em><strong>.</strong></em></p><p>Foi definido o valor zero para <em><em><strong><strong>proof_no</strong></strong></em><strong> </strong>e<strong> </strong><em><strong><strong>prev_hash</strong></strong></em><strong>, </strong></em>mas você pode definir o valor que quiser para eles.</p><pre><code class="language-python">def construct_genesis(self):
    self.construct_block(proof_no=0, prev_hash=0)


def construct_block(self, proof_no, prev_hash):
    block = Block(
        index=len(self.chain),
        proof_no=proof_no,
        prev_hash=prev_hash,
        data=self.current_data)
    self.current_data = []

    self.chain.append(block)
    return block
</code></pre><h3 id="c-criando-outros-blocos"><strong><strong>c. </strong>Criando outros blocos</strong></h3><p><br>O método <strong><strong><em><em>construct_block</em></em></strong><em> </em></strong>é usado para criar outros blocos na blockchain.</p><p>Adiante, vemos os valores dos atributos desse método:</p><ul><li><strong><strong>index</strong> </strong>— representa o tamanho da blockchain;</li><li><strong><strong>proof_nor </strong>e<strong> prev_hash</strong> </strong>— o método que chama o construtor passará esses parâmetros;</li><li><strong><strong>data</strong> </strong>— aqui temos o registro das transações que não foram incluídas em nenhum bloco da cadeia; </li><li><strong><strong>self.current_data</strong> </strong>— essa variável é usada para reiniciar a lista de transações nesse bloco. Se um bloco foi construído e transações forem registradas nele, a lista é reiniciada para se ter certeza que transações futuras serão adicionadas a essa variável;</li><li><strong><strong>self.chain.append()</strong> <strong>—</strong> </strong>esse método adiciona os blocos recém-construídos à cadeia;</li><li><strong><strong>return</strong> </strong>— por último, temos um objeto do tipo bloco que é retornado.</li></ul><h3 id="d-verificando-a-validade"><strong><strong>d. </strong>Verificando a validade</strong></h3><p><br>Para o método <em><em><strong><strong>check_validity</strong></strong></em><strong>, </strong></em>é importante verificar a integridade da blockchain e ter certeza de que não há quaisquer anomalias nela.</p><p>Como mencionado anteriormente, os <em>hashes</em> são essenciais para a segurança da blockchain. Por isso, qualquer mudança no bloco gerará um <em>hash</em> completamente novo.</p><p>Portanto, esse método <strong><strong><em><em>check_validity</em></em></strong><em> </em></strong><em>usa um <em><strong><strong>if</strong></strong></em></em> &nbsp;para verificar se o <em>hash</em> de cada bloco está correto.</p><p>Ele também verifica se cada bloco aponta corretamente para o bloco anterior, comparando o valor dos <em>hashes</em>. Se tudo estiver correto, ele retorna verdadeiro. Caso contrário, retorna falso.</p><pre><code class="language-python">@staticmethod
def check_validity(block, prev_block):
    if prev_block.index + 1 != block.index:
        return False

    elif prev_block.calculate_hash != block.prev_hash:
        return False

    elif not BlockChain.verifying_proof(block.proof_no, prev_block.proof_no):
        return False

    elif block.timestamp &lt;= prev_block.timestamp:
        return False

    return True
</code></pre><h3 id="e-adicionando-dados-de-transa-es"><strong><strong>e. </strong>Adicionando dados de transações</strong></h3><p><br>O método <em><em><strong><strong>new_data</strong></strong></em><strong> </strong></em>é usado para adicionar dados de transações a um bloco. É um método muito simples e aceita três parâmetros: (dados do remetente, do recebedor e quantidade) e adiciona isso aos dados da transação na lista <em><em><strong><strong>self.current_data</strong></strong></em><strong>.</strong></em></p><p>No momento em que um bloco é criado, essa lista é alocada a esse bloco e reiniciada, como foi explicado no método <em><em><strong><strong>construct_block</strong></strong></em><strong>.</strong></em></p><p>Uma vez que os dados da transação são colocados na lista, a posição do próximo bloco é retornada.</p><p>Essa posição é calculada adicionando-se 1 à posição do bloco atual (que é sempre o último da blockchain). Esses dados auxiliarão o usuário no envio de transações futuras.</p><pre><code class="language-python">def new_data(self, sender, recipient, quantity):
    self.current_data.append({
        'sender': sender,
        'recipient': recipient,
        'quantity': quantity
    })
    return True

</code></pre><h3 id="f-adicionando-a-prova-de-trabalho"><br><strong><strong>f. </strong>Adicionando a prova de trabalho</strong></h3><p><a href="https://en.bitcoin.it/wiki/Proof_of_work">Prova de trabalho</a> (texto em inglês) é um conceito que ajuda a blockchain a evitar abusos. De maneira simplificada, o objetivo da prova de trabalho é identificar um número que resolva um problema após algum processamento computacional.</p><p>Se a dificuldade desse processamento for alta, o uso de spams e tentativas de fraude serão desencorajadas na blockchain.</p><p>Na blockchain que estamos criando, foi usado um algoritmo simples para desencorajar pessoas a minerar ou a criar blocos de maneira muito fácil.</p><pre><code class="language-python">@staticmethod
def proof_of_work(last_proof):
    '''este algoritmo simples identifica um número f' tal que hash(ff') contenha 4 zeros à esquerda
         f é o f' anterior
         f' é a nova prova
        '''
    proof_no = 0
    while BlockChain.verifying_proof(proof_no, last_proof) is False:
        proof_no += 1

    return proof_no


@staticmethod
def verifying_proof(last_proof, proof):
    #verificando a prova: hash(last_proof, proof) contém 4 zeros à esquerda?

    guess = f'{last_proof}{proof}'.encode()
    guess_hash = hashlib.sha256(guess).hexdigest()
    return guess_hash[:4] == "0000"

</code></pre><h3 id="g-obtendo-o-ltimo-bloco"><strong><strong>g. </strong>Obtendo o último bloco</strong></h3><p>Finalmente, o método <strong><strong><em><em>latest_block</em></em></strong><em> </em></strong>é um método auxiliar que ajuda na obtenção do último bloco da blockchain. Lembre-se de que o último bloco, na verdade, é o bloco atual da blockchain.</p><pre><code class="language-python">@property
    def latest_block(self):
        return self.chain[-1]
</code></pre><h2 id="vamos-resumir-tudo-at-aqui"><strong>Vamos resumir tudo até aqui</strong></h2><p>Aqui está o código completo para criação da criptomoeda <strong><strong>fccCoin</strong>.</strong></p><p>Você também pode dar uma olhada no código através <a href="https://github.com/Alfrick/Create-Cryptocurrency-in-Python">desse repositório no Github.</a></p><pre><code class="language-python">import hashlib
import time


class Block:

    def __init__(self, index, proof_no, prev_hash, data, timestamp=None):
        self.index = index
        self.proof_no = proof_no
        self.prev_hash = prev_hash
        self.data = data
        self.timestamp = timestamp or time.time()

    @property
    def calculate_hash(self):
        block_of_string = "{}{}{}{}{}".format(self.index, self.proof_no,
                                              self.prev_hash, self.data,
                                              self.timestamp)

        return hashlib.sha256(block_of_string.encode()).hexdigest()

    def __repr__(self):
        return "{} - {} - {} - {} - {}".format(self.index, self.proof_no,
                                               self.prev_hash, self.data,
                                               self.timestamp)


class BlockChain:

    def __init__(self):
        self.chain = []
        self.current_data = []
        self.nodes = set()
        self.construct_genesis()

    def construct_genesis(self):
        self.construct_block(proof_no=0, prev_hash=0)

    def construct_block(self, proof_no, prev_hash):
        block = Block(
            index=len(self.chain),
            proof_no=proof_no,
            prev_hash=prev_hash,
            data=self.current_data)
        self.current_data = []

        self.chain.append(block)
        return block

    @staticmethod
    def check_validity(block, prev_block):
        if prev_block.index + 1 != block.index:
            return False

        elif prev_block.calculate_hash != block.prev_hash:
            return False

        elif not BlockChain.verifying_proof(block.proof_no,
                                            prev_block.proof_no):
            return False

        elif block.timestamp &lt;= prev_block.timestamp:
            return False

        return True

    def new_data(self, sender, recipient, quantity):
        self.current_data.append({
            'sender': sender,
            'recipient': recipient,
            'quantity': quantity
        })
        return True

    @staticmethod
    def proof_of_work(last_proof):
        '''este algoritmo simples identifica um número f', de modo que hash(ff') contenha 4 zeros à esquerda
         f é o f' anterior
         f' é a nova prova
        '''
        proof_no = 0
        while BlockChain.verifying_proof(proof_no, last_proof) is False:
            proof_no += 1

        return proof_no

    @staticmethod
    def verifying_proof(last_proof, proof):
        #verificando a prova: hash(last_proof, proof) contém 4 zeros à esquerda?

        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        return guess_hash[:4] == "0000"

    @property
    def latest_block(self):
        return self.chain[-1]

    def block_mining(self, details_miner):

        self.new_data(
            sender="0",  #implica que esse nó criou um outro bloco
            receiver=details_miner,
            quantity=
            1,  #criar um bloco (ou identificar o número da prova) recebe um valor 1
        )

        last_block = self.latest_block

        last_proof_no = last_block.proof_no
        proof_no = self.proof_of_work(last_proof_no)

        last_hash = last_block.calculate_hash
        block = self.construct_block(proof_no, last_hash)

        return vars(block)

    def create_node(self, address):
        self.nodes.add(address)
        return True

    @staticmethod
    def obtain_block_object(block_data):
        #obtains block object from the block data

        return Block(
            block_data['index'],
            block_data['proof_no'],
            block_data['prev_hash'],
            block_data['data'],
            timestamp=block_data['timestamp'])

</code></pre><p><br>Agora, vamos testar o código e ver se ele funciona.</p><pre><code class="language-python">blockchain = BlockChain()

print("***Mineração de fccCoin prestes a começar***")
print(blockchain.chain)

last_block = blockchain.latest_block
last_proof_no = last_block.proof_no
proof_no = blockchain.proof_of_work(last_proof_no)

blockchain.new_data(
    sender="0",  #implica que esse nó criou um outro bloco
    recipient="Quincy Larson",  #let's send Quincy some coins!
    quantity=
    1,  #criar um bloco (ou identificar o número da prova) recebe um valor 1
)

last_hash = last_block.calculate_hash
block = blockchain.construct_block(proof_no, last_hash)

print("***Mineração de fccCoin feita com sucesso***")
print(blockchain.chain)
</code></pre><p><br>Funciona!</p><p>Aqui está o resultado da mineração que fizemos:</p><pre><code class="language-python">***Mining fccCoin about to start***
[0 - 0 - 0 - [] - 1566930640.2707076]
***Mining fccCoin has been successful***
[0 - 0 - 0 - [] - 1566930640.2707076, 1 - 88914 - a8d45cb77cddeac750a9439d629f394da442672e56edfe05827b5e41f4ba0138 - [{'sender': '0', 'recipient': 'Quincy Larson', 'quantity': 1}] - 1566930640.5363243]
</code></pre><h2 id="conclus-es"><br>Conclusões</h2><p>Agora, temos uma criptomoeda!</p><p>Esse foi o tutorial de como criar sua própria criptomoeda usando Python.</p><p>Porém, é preciso notar que esse tutorial apenas demonstra os conceitos básicos e que serve apenas pra você molhar os pés nessa onda de inovação trazida pela tecnologia da blockchain.</p><p>Se <strong>essa moeda</strong> fosse lançada como está, ela poderia não atender às demandas atuais de mercado por estabilidade, segurança e facilidade de uso.</p><p>Portanto, ela pode ser melhorada, adicionando-se novos recursos para aumentar as capacidades de mineração e o envio de <a href="https://www.forextradingbig.com/">transações financeiras</a> (texto em inglês).</p><p>Entretanto, é um bom ponto de partida se você decidir ficar famoso no maravilhoso mundo das criptomoedas.</p><p>Se tiver dúvidas ou comentários, <a href="https://twitter.com/a_opidi">fale com o autor do texto</a>.</p><p>Boa (cripto)programação!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Como criar um Aplicativo Web Progressivo (PWA) do zero com HTML, CSS e JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Aplicativos Web Progressivos (do inglês, PWA, ou Progressive Web App) são uma forma de trazer a usabilidade de um aplicativo móvel para um aplicativo web tradicional. Com eles, podemos melhorar nosso website com funcionalidades de aplicativos móveis que aumentarão a usabilidade e oferecerão uma ótima experiência de usuário.  Nesse ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/como-criar-um-aplicativo-web-progressivo-pwa-do-zero-com-html-css-e-javascript/</link>
                <guid isPermaLink="false">62be2c62a3520206e79cf594</guid>
                
                    <category>
                        <![CDATA[ PWA ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Deusiel da Cunha de Souza ]]>
                </dc:creator>
                <pubDate>Fri, 01 Jul 2022 17:05:54 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/Group-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/build-a-pwa-from-scratch-with-html-css-and-javascript/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to build a PWA from scratch with HTML, CSS, and JavaScript</a>
      </p><p>Aplicativos Web Progressivos (do inglês, PWA, ou <em>Progressive Web App</em>) são uma forma de trazer a usabilidade de um aplicativo móvel para um aplicativo web tradicional. Com eles, podemos melhorar nosso website com funcionalidades de aplicativos móveis que aumentarão a usabilidade e oferecerão uma ótima experiência de usuário. </p><p>Nesse artigo, criaremos um PWA do zero, com HTML, CSS e JavaScript. Aqui estão os tópicos que nós cobriremos:</p><ul><li><a href="https://www.freecodecamp.org/portuguese/news/como-criar-um-aplicativo-web-progressivo-pwa-do-zero-com-html-css-e-javascript/#o-que-um-aplicativo-web-progressivo">O que é um Aplicativo Web Progressivo?</a> </li><li><a href="https://www.freecodecamp.org/portuguese/news/como-criar-um-aplicativo-web-progressivo-pwa-do-zero-com-html-css-e-javascript/#html">HTML</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/como-criar-um-aplicativo-web-progressivo-pwa-do-zero-com-html-css-e-javascript/#estiliza-o">Estilização</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/como-criar-um-aplicativo-web-progressivo-pwa-do-zero-com-html-css-e-javascript/#exibi-o-dos-dados-com-javascript">Exibição dos dados com JavaScript</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/como-criar-um-aplicativo-web-progressivo-pwa-do-zero-com-html-css-e-javascript/#manifesto-do-aplicativo-web">Manifesto do Aplicativo Web</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/como-criar-um-aplicativo-web-progressivo-pwa-do-zero-com-html-css-e-javascript/#o-que-um-service-worker">O que é um Service Worker?</a> </li><li><a href="https://www.freecodecamp.org/portuguese/news/como-criar-um-aplicativo-web-progressivo-pwa-do-zero-com-html-css-e-javascript/#armazenamento-cache-dos-arquivos">Armazenamento (cache) dos arquivos</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/como-criar-um-aplicativo-web-progressivo-pwa-do-zero-com-html-css-e-javascript/#acesso-aos-arquivos">Acesso aos arquivos</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/como-criar-um-aplicativo-web-progressivo-pwa-do-zero-com-html-css-e-javascript/#registro-do-service-worker">Registro do Service Worker</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/como-criar-um-aplicativo-web-progressivo-pwa-do-zero-com-html-css-e-javascript/#conclus-es">Conclusões</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/como-criar-um-aplicativo-web-progressivo-pwa-do-zero-com-html-css-e-javascript/#pr-ximos-passos-textos-em-ingl-s-">Próximos passos</a></li></ul><p>Então, vamos começar com uma pergunta importante: o que é, afinal, um Aplicativo Web Progressivo (PWA)?</p><h2 id="o-que-um-aplicativo-web-progressivo"><strong>O que é um Aplicativo Web Progressivo?</strong></h2><p>Um Aplicativo Web Progressivo é um aplicativo web que entrega uma experiência semelhante a de um aplicativo móvel para os usuários usando as capacidades da web moderna. No final, é só o seu site da web normal, rodando em um navegador, com alguns melhoramentos. Aplicativos Web Progressivos têm a capacidade de:</p><ul><li>Instalar em uma página home do seu celular </li><li>Acessar o aplicativo quando você estiver desconectado da internet</li><li>Acessar a sua câmera </li><li>Receber notificações push</li><li>Manter uma sincronização em segundo plano</li></ul><p>E muito mais.</p><p>Contudo, para podermos transformar nosso aplicativo web normal em um PWA, teremos que fazer alguns pequenos ajustes, adicionando um arquivo de manifesto de aplicativo web e um service worker.</p><p>Não se preocupe com esses termos novos. Falaremos sobre eles adiante.</p><p>Primeiro, temos que construir nosso aplicativo web tradicional. Então, vamos começar com o HTML.</p><h2 id="html"><strong>HTML</strong></h2><p>O arquivo HTML é relativamente simples. Envolveremos tudo dentro de uma tag <code>main</code>.</p><ul><li>N0 arquivo <code>index.html</code></li></ul><pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8" /&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="ie=edge" /&gt;
    &lt;link rel="stylesheet" href="css/style.css" /&gt;
    &lt;title&gt;Dev'Coffee PWA&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;main&gt;
      &lt;nav&gt;
        &lt;h1&gt;Dev'Coffee&lt;/h1&gt;
        &lt;ul&gt;
          &lt;li&gt;Home&lt;/li&gt;
          &lt;li&gt;About&lt;/li&gt;
          &lt;li&gt;Blog&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/nav&gt;
      &lt;div class="container"&gt;&lt;/div&gt;
    &lt;/main&gt;
    &lt;script src="js/app.js"&gt;&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><p>Vamos criar um barra de navegação com a tag <code>nav</code>. Então, uma <code>div</code> com a classe <code>.container</code>, onde ficarão todos os cartões que vamos adicionar depois com JavaScript.</p><p>Agora que terminamos essa parte, vamos estilizar nosso aplicativo com CSS.</p><h2 id="estiliza-o">Estilização</h2><p>Aqui, como de costume, começamos importando as fontes de que vamos precisar. Então, vamos redefinir alguns comportamentos (e valores) que são padrão em nosso navegador.</p><ul><li>No arquivo <code>css/style.css</code></li></ul><pre><code class="language-css">@import url("https://fonts.googleapis.com/css?family=Nunito:400,700&amp;display=swap");
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  background: #fdfdfd;
  font-family: "Nunito", sans-serif;
  font-size: 1rem;
}
main {
  max-width: 900px;
  margin: auto;
  padding: 0.5rem;
  text-align: center;
}
nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
ul {
  list-style: none;
  display: flex;
}

li {
  margin-right: 1rem;
}
h1 {
  color: #e74c3c;
  margin-bottom: 0.5rem;
}
</code></pre><p>Então, vamos limitar o tamanho do elemento <code>main</code> para largura máxima de <code>900px</code> (900 pixels) para fazer com que ele apareça bem em telas mais largas.</p><p>Para a barra de navegação, queremos que o logotipo fique à esquerda e os links à direita. Então, para a tag <code>nav</code>, depois de torná-la um "contêiner flex", usamos <code>justify-content: space-between;</code> para alinhá-las.</p><ul><li>No arquivo <code>css/style.css</code></li></ul><pre><code class="language-css">.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  grid-gap: 1rem;
  justify-content: center;
  align-items: center;
  margin: auto;
  padding: 1rem 0;
}
.card {
  display: flex;
  align-items: center;
  flex-direction: column;
  width: 15rem auto;
  height: 15rem;
  background: #fff;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
  border-radius: 10px;
  margin: auto;
  overflow: hidden;
}
.card--avatar {
  width: 100%;
  height: 10rem;
  object-fit: cover;
}
.card--title {
  color: #222;
  font-weight: 700;
  text-transform: capitalize;
  font-size: 1.1rem;
  margin-top: 0.5rem;
}
.card--link {
  text-decoration: none;
  background: #db4938;
  color: #fff;
  padding: 0.3rem 1rem;
  border-radius: 20px;
}
</code></pre><p>Teremos alguns cartões. Por isso, o elemento com a classe <code>.container</code> será mostrado como grid. E, com a propriedade <code>grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr))</code>, podemos, agora, fazer nossos cartões ficarem responsivos, então eles podem usar pelo menos <code>15rem</code> de largura se houver espaço suficiente para isso (ou <code>1fr</code> se não houver espaço para <code>15rem</code>).</p><p>E para fazer nossos cartões ficarem ainda mais bonitos, nós duplicamos o efeito de sombra no elemento com a classe <code>.card</code> e usamos <code>object-fit: cover</code> no <code>.card--avatar</code> para evitar que a imagem estique demais.</p><p>Agora, está muito melhor. Porém, ainda não temos dados para mostrar.</p><p>Vamos consertar isso na próxima seção.</p><h2 id="exibi-o-dos-dados-com-javascript">Exibição dos dados com JavaScript</h2><p>Note que usei imagens grandes, que demoram algum tempo para carregar. Isso mostrará a você, da melhor forma, o poder dos service workers.</p><p>Como disse antes, a classe <code>.container</code> conterá nossos cartões. Portanto, precisamos selecioná-la com nosso código JavaScript.</p><ul><li>No arquivo <code>js/app.js</code></li></ul><pre><code class="language-javascript">const container = document.querySelector(".container")
const coffees = [
  { name: "Perspiciatis", image: "images/coffee1.jpg" },
  { name: "Voluptatem", image: "images/coffee2.jpg" },
  { name: "Explicabo", image: "images/coffee3.jpg" },
  { name: "Rchitecto", image: "images/coffee4.jpg" },
  { name: " Beatae", image: "images/coffee5.jpg" },
  { name: " Vitae", image: "images/coffee6.jpg" },
  { name: "Inventore", image: "images/coffee7.jpg" },
  { name: "Veritatis", image: "images/coffee8.jpg" },
  { name: "Accusantium", image: "images/coffee9.jpg" },
]
</code></pre><p>Então, criamos um array de cartões com os nomes e imagens.</p><ul><li>No arquivo <code>js/app.js</code></li></ul><pre><code class="language-javascript">const showCoffees = () =&gt; {
  let output = ""
  coffees.forEach(
    ({ name, image }) =&gt;
      (output += `
              &lt;div class="card"&gt;
                &lt;img class="card--avatar" src=${image} /&gt;
                &lt;h1 class="card--title"&gt;${name}&lt;/h1&gt;
                &lt;a class="card--link" href="#"&gt;Taste&lt;/a&gt;
              &lt;/div&gt;
              `)
  )
  container.innerHTML = output
}

document.addEventListener("DOMContentLoaded", showCoffees)
</code></pre><p>Com o código acima, podemos fazer um loop no array e renderizar os elementos no nosso arquivo HTML. E, para fazer tudo funcionar, esperamos até que o conteúdo do DOM (Modelo de Objeto de Documentos) termine de carregar para que possamos executar o método <code>showCoffees</code>.</p><p>Já fizemos muito, mas, por agora, temos somente um aplicativo web tradicional. Então, vamos mudar isso na próxima seção, introduzindo algumas funcionalidades de Aplicativo Web Progressivo.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/source.gif" class="kg-image" alt="source" width="500" height="281" loading="lazy"></figure><h2 id="manifesto-do-aplicativo-web"><strong>Manifesto do Aplicativo Web </strong></h2><p>O manifesto do aplicativo web é um simples arquivo em formato JSON, que informa ao navegador sobre o seu aplicativo web. Esse manifesto informa como o aplicativo deve se comportar quando for instalado no dispositivo móvel do usuário ou no seu desktop. E, para mostrar a opção de adicionar à tela inicial, é obrigatório ter o manifesto.</p><p>Agora que já sabemos o que é um manifesto, vamos criar um novo arquivo chamado <code>manifest.json</code> (o nome precisa ser esse) na pasta raiz. Então, vamos adicionar o bloco de código abaixo.</p><ul><li>No arquivo <code>manifest.json</code></li></ul><pre><code class="language-javascript">{
  "name": "Dev'Coffee",
  "short_name": "DevCoffee",
  "start_url": "index.html",
  "display": "standalone",
  "background_color": "#fdfdfd",
  "theme_color": "#db4938",
  "orientation": "portrait-primary",
  "icons": [
    {
      "src": "/images/icons/icon-72x72.png",
      "type": "image/png", "sizes": "72x72"
    },
    {
      "src": "/images/icons/icon-96x96.png",
      "type": "image/png", "sizes": "96x96"
    },
    {
      "src": "/images/icons/icon-128x128.png",
      "type": "image/png","sizes": "128x128"
    },
    {
      "src": "/images/icons/icon-144x144.png",
      "type": "image/png", "sizes": "144x144"
    },
    {
      "src": "/images/icons/icon-152x152.png",
      "type": "image/png", "sizes": "152x152"
    },
    {
      "src": "/images/icons/icon-192x192.png",
      "type": "image/png", "sizes": "192x192"
    },
    {
      "src": "/images/icons/icon-384x384.png",
      "type": "image/png", "sizes": "384x384"
    },
    {
      "src": "/images/icons/icon-512x512.png",
      "type": "image/png", "sizes": "512x512"
    }
  ]
}
</code></pre><p>No final, é só um arquivo JSON com algumas propriedades obrigatórias e outras opcionais.</p><p>name: Quando o navegador iniciar a tela de boas-vindas, esse será o nome mostrado na tela de boas-vindas</p><p>short_name: Será o nome que aparecerá abaixo do atalho para seu aplicativo na tela inicial</p><p>start_url: Será a página mostrada ao usuário quando o seu aplicativo for aberto.</p><p>display: Informará ao seu navegador como mostrar o aplicativo. Existem diversas formas de mostrar, como <code>minimal-ui</code>, <code>fullscreen</code>, <code>browser</code>, etc. Aqui, nós usaremos o modo <code>standalone</code> para esconder qualquer coisa relacionada com o navegador.</p><p>background_color: Quando o navegador iniciar a tela de boas-vindas, essa será a cor de fundo da tela.</p><p>theme_color: Essa será a cor da barra de status quando o app for aberto.</p><p>orientation: Informa ao navegador em qual orientação (horizontal ou vertical) mostrar o app.</p><p>icons: Quando o navegador carregar a página de boas-vindas, esse será o ícone mostrado na tela. Aqui, eu usei todos os tamanhos para que o app se ajuste a qualquer dispositivo, mas você pode usar um ou dois. Depende da sua escolha.</p><p>Agora que nós temos um manifesto de aplicativo web, vamos adicioná-lo ao nosso arquivo HTML.</p><ul><li>No arquivo <code>index.html</code> (na tag <em>head</em>)</li></ul><pre><code class="language-html">&lt;link rel="manifest" href="manifest.json" /&gt;
&lt;!-- ios support --&gt;
&lt;link rel="apple-touch-icon" href="images/icons/icon-72x72.png" /&gt;
&lt;link rel="apple-touch-icon" href="images/icons/icon-96x96.png" /&gt;
&lt;link rel="apple-touch-icon" href="images/icons/icon-128x128.png" /&gt;
&lt;link rel="apple-touch-icon" href="images/icons/icon-144x144.png" /&gt;
&lt;link rel="apple-touch-icon" href="images/icons/icon-152x152.png" /&gt;
&lt;link rel="apple-touch-icon" href="images/icons/icon-192x192.png" /&gt;
&lt;link rel="apple-touch-icon" href="images/icons/icon-384x384.png" /&gt;
&lt;link rel="apple-touch-icon" href="images/icons/icon-512x512.png" /&gt;
&lt;meta name="apple-mobile-web-app-status-bar" content="#db4938" /&gt;
&lt;meta name="theme-color" content="#db4938" /&gt;
</code></pre><p>Como você pode ver, nosso arquivo <code>manifest.json</code> foi importado na tag <em>head</em>. Também importamos alguns outros links para podermos oferecer suporte para sistemas iOS na hora de mostrar ícones e colorir nossa barra de status com nosso tema de cores.</p><p>Com isso, agora podemos mergulhar mais fundo na parte final e introduzir o conceito de service worker.</p><h2 id="o-que-um-service-worker"><strong>O que é um<strong> Service Worker?</strong></strong></h2><p>Note que Aplicativos Web Progressivos (PWAs) funcionarão apenas em https, porque o service worker pode acessar as requisições e lidar com elas.</p><p>Um service worker é um script que seu navegador executa em segundo plano em uma thread separada. Isso significa que ele é executado em um lugar completamente diferente da sua página web. Essa é a razão pela qual ele não pode manipular os elementos do DOM.</p><p>Contudo, ele é muito poderoso. O service worker pode interceptar e lidar com requisições de rede, gerenciar o cache para possibilitar o uso off-line ou enviar notificações push para seus usuários.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/source--1-.gif" class="kg-image" alt="source--1-" width="470" height="376" loading="lazy"></figure><p>Então, vamos criar nosso primeiro service worker na pasta raiz e dar a ele o nome de <code>serviceWorker.js</code> (você pode usar o nome que quiser). Porém, você deve colocá-lo na pasta raiz para não limitar o escopo a apenas uma pasta.</p><h3 id="armazenamento-cache-dos-arquivos"><strong>Armazenamento (cache) dos arquivos </strong></h3><ul><li>No arquivo <code>serviceWorker.js</code></li></ul><pre><code class="language-javascript">const staticDevCoffee = "dev-coffee-site-v1"
const assets = [
  "/",
  "/index.html",
  "/css/style.css",
  "/js/app.js",
  "/images/coffee1.jpg",
  "/images/coffee2.jpg",
  "/images/coffee3.jpg",
  "/images/coffee4.jpg",
  "/images/coffee5.jpg",
  "/images/coffee6.jpg",
  "/images/coffee7.jpg",
  "/images/coffee8.jpg",
  "/images/coffee9.jpg",
]

self.addEventListener("install", installEvent =&gt; {
  installEvent.waitUntil(
    caches.open(staticDevCoffee).then(cache =&gt; {
      cache.addAll(assets)
    })
  )
})
</code></pre><p>Esse código parece intimidador no começo, mas é só JavaScript (então, não se preocupe).</p><p>Declaramos o nome do nosso cache <code>staticDevCoffee</code> e os arquivos para salvar no cache. Após fazermos isso, precisamos adicionar um listener ao <code>self</code>.</p><p><code>self</code> é o service worker. Ele nos permite "ouvir" o ciclo de vida dos eventos e fazer alguma coisa quando esses eventos de fato acontecem.</p><p>O service worker tem alguns ciclos de vida e um deles é o evento <code>install</code>. Ele é executado quando um service worker é instalado. Ele executa tão logo um worker execute e somente é executado uma vez por service worker.</p><p>Quando o evento <em>install</em> é acionado, executamos uma função de retorno (callback), que nos dará acesso ao objeto <code>event</code>.</p><p>Armazenar alguma coisa no navegador pode levar algum tempo para terminar, porque é assíncrono.</p><p>Então, para lidar com isso, precisamos usar <code>waitUntil()</code> que, como você pode imaginar, vai esperar o carregamento para, então, finalizar.</p><p>Uma vez que a API do cache está pronta, podemos executar o método <code>open()</code> e criar nosso cache passando o nome do nosso cache como um argumento, assim: <code>caches.open(staticDevCoffee)</code></p><p>Então, essa função nos retornará uma <em>Promise</em>, que nos ajuda a armazenar nossos arquivos no cache com <code>cache.addAll(assets)</code>.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/cached-images.png" class="kg-image" alt="cached-images" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/cached-images.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/07/cached-images.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/cached-images.png 1278w" sizes="(min-width: 720px) 720px" width="1278" height="655" loading="lazy"></figure><p>Espero que você ainda esteja comigo.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/source--2-.gif" class="kg-image" alt="source--2-" width="150" height="123" loading="lazy"></figure><p>Agora, armazenamos nossos arquivos no navegador com sucesso. E, na próxima vez em que carregarmos a página, o service worker lidará com as requisições e acessará o cache se estivermos off-line.</p><p>Então, vamos acessar nosso cache.</p><h3 id="acesso-aos-arquivos"><strong>Acesso aos arquivos</strong></h3><ul><li>No arquivo <code>serviceWorker.js</code></li></ul><pre><code class="language-javascript">self.addEventListener("fetch", fetchEvent =&gt; {
  fetchEvent.respondWith(
    caches.match(fetchEvent.request).then(res =&gt; {
      return res || fetch(fetchEvent.request)
    })
  )
})
</code></pre><p>Aqui, usamos o evento <code>fetch</code> para obter nossos dados. A função de retorno (callback) nos dá acesso ao <code>fetchEvent</code>. Então, acoplamos <code>respondWith()</code> para modificar a resposta padrão do navegador. Em vez disso (da resposta padrão), a função retorna uma <em>promise</em>, já que a ação de acessar os arquivos pode demorar algum tempo para terminar.</p><p>Quando o cache estiver pronto, podemos aplicar <code>caches.match(fetchEvent.request)</code>. Isso verificará se alguma coisa no cache corresponde a <code>fetchEvent.request</code>. Para que você saiba, <code>fetchEvent.request</code> é apenas um array de arquivos.</p><p>Então, a função retorna uma <em>promise</em>. Finalmente, podemos retornar os resultados, se eles existirem, ou fazer o fetch inicial caso não existam resultados a exibir.</p><p>Agora, nossos arquivos podem ser armazenados e acessados pelo service worker, o que melhora o tempo de carregamento das nossas imagens um pouco.</p><p>E o mais importante: isso faz com que o nosso app fique disponível off-line.</p><p>O service worker, porém, não consegue fazer todo o trabalho sozinho. Precisamos registrá-lo em nosso projeto.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/source--3-.gif" class="kg-image" alt="source--3-" width="320" height="180" loading="lazy"></figure><h2 id="registro-do-service-worker"><strong>Registro do<strong> Service Worker</strong></strong></h2><ul><li>No arquivo <code>js/app.js</code></li></ul><pre><code class="language-javascript">if ("serviceWorker" in navigator) {
  window.addEventListener("load", function() {
    navigator.serviceWorker
      .register("/serviceWorker.js")
      .then(res =&gt; console.log("service worker registered"))
      .catch(err =&gt; console.log("service worker not registered", err))
  })
}
</code></pre><p>Aqui, verificamos se o service worker é suportado pelo nosso navegador (ele não é suportado por todos os navegadores).</p><p>Então, "ouvimos" o evento de carregamento da página para registrar nosso service worker, passando para ele o nome do arquivo <code>serviceWorker.js</code> para <code>navigator.serviceWorker.register()</code> como parâmetro para registrá-lo.</p><p>Com esse ajuste, agora transformamos nosso aplicativo web normal em um PWA.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/source--4-.gif" class="kg-image" alt="source--4-" width="480" height="266" loading="lazy"></figure><h2 id="conclus-es"><strong>Conclusões</strong></h2><p>Através deste artigo, vimos que os PWAs podem ser maravilhosos. Adicionando um manifesto de aplicativo web e um service worker, realmente melhoramos a experiência de usuário do nosso aplicativo web tradicional. Isso ocorre porque os PWAs são rápidos, seguros, confiáveis e, o mais importante de tudo, eles permitem o uso off-line. </p><p>Muitos frameworks atuais já vêm com um service worker configurado para nós. Saber como implementá-los com JavaScript puro, no entanto, pode ajudá-lo a entender os PWAs.</p><p>Você pode ir mais além com os service workers, como, por exemplo: armazenar arquivos dinamicamente, limitar o tamanho do seu armazenamento e assim por diante.</p><p>Obrigado por ler esse artigo.</p><p>Você pode ver como o aplicativo ficou <a href="https://devcoffee-pwa.netlify.com/">aqui </a>e o código fonte <a href="https://github.com/ibrahima92/pwa-with-vanilla-js">aqui</a>.</p><p>Leia mais artigos do autor no <a href="https://www.ibrahima-ndaw.com/blog/how-to-build-pwa-with-javascript/">blog do autor</a>.</p><h2 id="pr-ximos-passos-textos-em-ingl-s-"><strong>Próximos passos (textos em inglês)</strong></h2><p><a href="https://developers.google.com/web/fundamentals/web-app-manifest">Web Manifest Documentation</a> (documentação do manifesto da web)</p><p><a href="https://developers.google.com/web/fundamentals/primers/service-workers">Service Worker Documentation</a> (documentação do service worker)</p><p><a href="https://app-manifest.firebaseapp.com/">Web Manifest Generator</a> (gerador do manifesto da web)</p><p><a href="https://caniuse.com/#search=service%20worker">Browser Support</a> (suporte aos navegadores)</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
