segunda-feira, 25 de julho de 2016

Aquele tutorial longo sobre modularização em javascript

"Dividir para conquistar", é a regra de ouro de todos os tempos.

Jamais seria diferente com o Javascript, esse novo rico, de origem humilde e hoje a mola mestra do desenvolvimento de software.

Para abordar o tema, precisaremos dos seguintes componentes:

  • node.js funcionando na sua linha de comando ou interface que mais gostar.
  • um bom editor de códigos: atom, code, eclipse, todos valendo.
Nossa situação problema será a seguinte: uma pequena SPA simulando dificuldades com a quantidade de scripts e o relacionamento entre eles, bem como o controle das bibliotecas javascript envolvidas.

Pra você entender essa parte, siga os passos abaixo e crie nessa primeira etapa a aplicação.

Dentro da pasta que você criou com o nome samplespa, digite "npm init". Isso faz o npm criar um projeto node novinho pra você:

Em seguida, ainda dentro da pasta do projeto, instale neste projeto algumas dependências para o lado do servidor:
"npm install express knex pg --save"

Pra quem não é acostumado, vale lembrar: essa saída horrenda é a saída de sucesso do npm instalando pacotes. E o --save é para ele salvar as dependências no seu package.json, criado no passo anterior. O projeto deve estar mais ou menos assim.

Agora é hora de criar o ponto de entrada do node, o arquivo index.js (visto lá no lance do npm init). Ainda na pasta do projeto, crie e edite o arquivo:
"touch index.js ; code ."

 Vamos colocar a rota mínima do express para servir arquivos estáticos:

Com isso aí estamos dizendo ao express para servir de modo estático todos os arquivos dentro da pasta www (que nem criamos ainda, mas vai ficar assim). Vamos criar um conteúdo mínimo para nosso servidor: 
"mkdir www ; touch www/index.html"

Esta aplicação serve para organizar festas e seus convidados. Vamos adicionar suporte a banco e adicionar uma API REST nela.
O banco que utilizaremos será o postgresql. Pra não deixar este artigo muito grande, veja este artigo aqui sobre como configurar ele e em seguida volte e crie um esquema chamado festaspa:


Nota: pra garantir que sua conexão seja via "rede", lembre-se de especificar o host no ato da conexão (opção -h). Sem isso ele não vai querer saber da sua senha.

Agora que temos o banco de dados, hora de configurarmos nosso query builder

Você já o tem no projeto, mas não o tem na linha de comando. resolva isso com um simples "sudo npm -g install knex". Em máquinas windows não precisa do sudo.

Agora ainda pelo terminal, na pasta do projeto, insira o comando "knex init": 

Agora no editor de códigos abra este arquivo. Vamos informar pra ele o nome do esquema, o tipo de banco de dados, bem como usuário e senha pra conectar:

Como para o nosso exemplo só precisaremos de um perfil de banco, pode apagar os demais e deixar só um. Perfis adicionais são excelentes pra rodar testes e coisas do tipo sem comprometer dados valiosos de produção.

Retorne agora ao index.js para adicionarmos o suporte de banco de dados:

Note que o knex não é só um query builder, mas também cuida das versões do esquema e estado do banco. a linha "knex.migrate.latest()" cuida de rodar todas as migrações pendentes.

Nosso banco, falando nisso, não tem tabela alguma ainda. Vamos criar uma migração pra fazer o esquema de dados nascer. Insira no terminal na pasta do projeto o seguinte comando: "knex migrate:make esquema_inicial"

Isso criou um novo arquivo em uma nova pasta, como pode ser conferido no print.

Para criar ou consultar o banco de dados, todo o material de consulta encontra-se aqui.

O esquema inicial deverá criar uma tabela de convidados, uma de festas e uma de relacionamento pra indicar quais convidados participarão de quais festas. Vamos criar ainda uma visão de banco pra saber o número de festas que o convidado já participou e uma visão pra dizer quantos convidados cada festa teve:

Caso seja seu primeiro contato com um arquivo de migração, nada tema. Leia a documentação. O benefício de gerenciar com mão de ferro o banco de dados é muito bom. Caso deseje ver esse migrate melhor, aperte aqui.

Pra fechar o lado do servidor, só falta subir os serviços REST. No index.js adicione as seguintes rotas e consultas do express:


Como os dados do knex são disponibilizados no formato JSON, podemos repassar o resultado das consultas diretamente para o express. Veja o index.js em mais detalhes aqui.

Você já pode testar algumas chamadas desses serviços. Na linha de comando, execute o comando "node index.js" e abra um navegador de internet no seguinte endereço: 

Note que temos tabelas mas não temos dados para testar. Isso pode ser resolvido com uma migração inserindo dados de testes. Digite o comando "knex migrate:make dados_de_teste" e vamos fazer algumas festas:

Execute o servidor novamente e visite o link outra vez. Veja a diferença:

Confira o script de migração aqui.

Temos servidor, banco de dados, tudo em pé. Agora podemos começar nossa situação problema!

No cenário zero resolvemos tudo no braço. Quem nem se fazia antes de 2010. Isso me lembra um pouco minha época de Slackware, mas mesmo o mais velho dos linux em atividade tem um gerenciador de pacotes com download automático agora. Idos tempos. Agora foco.

Faça o download dos seguintes frameworks para dentro da pasta www:
Agora que você fez o download pra dentro da www, temos que descompactar os arquivos zip:

Muita paz até agora, certo? O passo seguinte é referenciar no index.html cada script e cada estilo necessário para que o uso das bibliotecas escolhidas tenha algum efeito. O index.html deverá ficar mais ou menos assim:

Verifique o endereço no navegador. você verá algo assim caso ative as ferramentas do desenvolvedor

Como? faltou baixar o jquery? Sem problemas, colocamos ele na lista também:
Adicione uma tag de script para ele também, conforme feito para os outros scripts. Apenas cuide de colocá-lo antes do bootstrap, que foi quem reclamou da falta dele. Deve ficar assim:

Como?! Outro erro? Sem problemas, removemos o jquery 3 e colocamos um mais antigo:
Enfim! Terminamos! 

Como? só começamos, tem mais!

Nossa SPA usará a injeção de dependências do angular e o componente de roteamento dele. Caso você já tenha mexido com angular antes, nada de novo vem por aqui, mas vou explicando pro pessoal de casa conseguir entender.

O  primeiro passo é o bootstrap do contexto angular. Ah, então foi por isso que baixamos o bootstrap? Não, aquele bootstrap é outra coisa.

Pra fazer isso, marcamos no documento html onde o módulo angular vai se pendurar, adicionando um atributo chamado ng-app. No caso do nosso exemplo, vou pendurar isso na raíz do documento html:

Agora nosso aplicativo responde assim:

Nada tema, isso significa que o angular não encontrou a definição do módulo. Vamos definir um. Crie um script chamado spa.js e coloque a tag de script dele no header do index.html:

Dependências resolvidas, index no ar, módulo angular instanciado. Vamos codificar o aplicativo.

Nosso gestor de festas é bem simples, teremos uma interface pra listar festas, uma para criar, alterar ou excluir festas, listar convidados, criar, alterar ou excluir convidados e por fim uma para adicionar ou remover convidados de uma festa. 5 telas.

Nosso primeiro passo será adicionar um menu de navegação e um ponto de montagem das rotas. O html ficará assim:

O passo seguinte é configurarmos no javascript as rotas a serem mostradas. Isso é feito numa seção de configuração junto ao provedor de rotas:


Nesta etapa o aplicativo já tenta navegar, mas fracassa, porque estamos prometendo a ele controles e templates que nem temos ainda. Mas somos desenvolvedores honrados, vamos criar tudo o que foi prometido:
Note ainda que tem 5 controles (ou controladores) também prometidos e que não estão aí. Para piorar o cenário zero, vamos colocar estes cinco controles no mesmo arquivo que temos no momento, o spa.js:

Uma vez criados os fragmentos, podemos colocar conteúdo neles e nos controladores podemos colocar nossas ações. Veja por exemplo a listagem de convidados:

Isso é capaz de produzir a seguinte tela:

A tela de detalhe segue:

Que deve produzir uma tela assim:

Note que é isso, um aplicativo simples, com um sistema de roteamento, uns fragmentos de tela, controladores, serviços e uma interface REST sendo consumida.

Este primeiro cenário apresentou uma variedade bacana de problemas. O primeiro é o gerenciamento destas bibliotecas. Se você se responsabiliza pelo download dos seus scripts, também se responsabiliza pela gestão das versões e dependências deles. Acredite, é um problema bem maior do que fiz parecer, versão do jquery você ainda consegue resolver.

O segundo problema é o monolito de script de aplicação em spa.js. Imagine o problema que deve ser uma equipe de 5 ou mais pessoas editando concorrentemente um script.

Claro, podemos dividir o script em vários pedaços, mas aí o gerenciamento de cada script segue como responsabilidade do programador.

Vamos para um segundo cenário agora. Vamos voltar no tempo em nosso projeto e vamos fazer diferente desta vez.

Usaremos o bower para gerenciar os downloads de nossas bibliotecas. Para tanto, primeiro instale-o em seu sistema, pois ele é um gerenciador de pacotes tipo o npm, só que voltado pra web. O divertido é que para instalar o gerenciador de pacotes você deve usar o gerenciador de pacotes, :-)
"sudo npm -g install bower"

Agora, em analogia ao que fizemos com o npm, toca criar o projeto dentro do projeto:
"bower init"

Após o pequeno questionário, o arquivo bower.json será criado.

Agora uma dica importante:

Enquanto que o package.json diz respeito ao projeto node, o bower.json diz respeito apenas ao projeto web, que fica contido dentro da pasta que indicamos ao express como sendo a pasta de "conteúdo estático", doravante chamada por conteúdo web ou conteúdo client side.

Entenda que agora é como se você tivesse dois projetos dentro de um só, cada um com seu sistema de dependências. Enquanto o npm usa a pasta node_modules, o bower usa a bower_components, e esta última deverá residir dentro da nossa pasta de conteúdo web.

Posto isso, é muito importante que as dependências do bower sejam visíveis "client side".

Você pode resolver isso de duas maneiras: a primeira é mover o bower.json para dentro, em nosso caso, da pasta www. A segunda solução é apelar para o arquivo .bowerrc e indicar o diretório onde o bower depositará as dependências web.

A vantagem da segunda solução é que você não precisará mudar de pasta no terminal quando estiver instalando uma dependência node ou uma dependência bower. Aqui neste tutorial vamos adotar a segunda abordagem:

Agora virou passeio, vamos ao terminal instalar de volta nossas três (na verdade duas. Não, quatro, mas corrijo depois) dependências:

"bower install bootstrap angularjs --save"

E sabe todo aquele trabalho que tivemos resolvendo dependências? Tudo resolvido automático!
Nosso index.html ficará assim:

E digo mais: sabe o tempo extra que ganhamos? podemos investi-lo em uma ou outra melhoria na organização do aplicativo:
 

Ao testar, que surpresa: falta uma dependência:

O que não chega a ser problema, posto que podemos resolver assim:
"bower install angular-route --save"

E depois adicionar ao index.html a dependência (e corrigir o caminho dos arquivos js do projeto!):

Agora sim, temos o aplicativo de volta em operação!
Nota: instale o font-awesome também, que eu esqueci, mas vou deixar você adivinhar como faz.

Com esta abordagem, resolvemos a busca e resolução de dependências de módulos javascript. Seja muito bem-vindo a 2012!

Mas nós queremos mais.

Temos avanço, é claro, pois nossas dependências já vem resolvidas da rua. Isso é bacana, é sim.

Mas o desafio de "modularização" e reuso é dentro de casa. Em nosso exemplo, criamos 5 ou 6 fragmentos de script, e nossa SPA é bem pequena. O crescimento é quase linearítmico em proporção ao número de features do projeto.

E é por isso que vamos continuar e ver mais algumas alternativas.

Marcando mais uma reestruturação do código de exemplo, vamos falar de browserify.

De um modo resumido, o node.js + npm é um negócio tão legal que um possível fã de harry potter colocou essa dobradinha disponível no browser.

Isso é bacana porque em teoria tudo o que tem pra node tem pro navegador.

Além disso, seus scripts são otimizados para download, posto que todos os scripts serão "empacotados" em um arquivo final.

Existe, é claro, uma contrapartida.

A forma com que tradicionalmente se trabalha no browser vai para o saco. O javascript na web é comumente construído em cima de um tecido global e, salvo um ou outro esquema de modularização, sujeira no namespace é uma procupação.

Com os módulos é diferente. Precisamos ativamente exportar e requisitar objetos a partir dos vários scripts de aplicação. Tem um caminho de busca de módulos.

Mas chega de conversa, hora de colocar browserify no projeto!

Comece tornando o browserify disponível na linha de comando, que nem fizemos antes com o bower e com o knex:

"sudo npm -g install browserify"


Lembrando que o sudo é dispensável nos windows.

Na abordagem a seguir, nossos scripts client-side vão residir dentro de uma pasta chamada src-client.
"mkdir src-client"

Dentro dela teremos um "entry point" e cada script que temos hoje espalhados. A estrutura será esta:

Sim... vamos mexer no javascript do lado do cliente mais uma vez. Vamos mover tudo de lá e levar para esta estrutura server-side:

O passo seguinte é remover as dependências do html:

O comando para gerar o "bundle" ou "script compilado" (que é um nome infeliz, mas é o que tem por enquanto) é o seguinte:

"browserify src-client/main.js -o www/spa.js"

Se você reiniciar o processo node (conforme visto semana passada, lá no começo deste artigo) e atualizar o browser, verá que não deu erro no console, mas funcionar não funcionou ainda:

E, além disso, o script spa.js está diferente.

Isso ocorreu porque apenas movemos os scripts, mas não reestruturamos os códigos para exportar e requisitar módulos.

Agora que corrigimos isso, nosso main.js deverá ficar mais apresentável:

Não esquecer de "compilar" o script usando o browserify toda vez que uma mudança ocorrer.

Observe que ainda temos algumas dependências sendo resolvidas com o bower. O próprio angular é um exemplo.

Se eu quiser resolver o angular "do lado do servidor" ou seja, fazer com que ele participe do "script compilado", devemos usar o npm em vez do bower para resolvê-lo.

Como?! se é mais vantajoso resolver com npm ou com bower?

A princípio é mais vantajoso resolver com o que tiver disponível, pois algumas bibliotecas javascript não suportam módulos, enquanto outras suportam demais.

Estes tempos são um pouco como o velho-oeste e a corrida do ouro, temos algumas ferrovias importantes montadas, mas o deserto e vasto e ainda esconde uns poucos tesouros.

O fato é que escolher o sistema principal de resolução de dependências é importante e pode impactar no resto da aplicação.

O font-awesome, por exemplo, é 100% css, logo o fato dele ser gerido via bower ou npm torna-se secundário. Caso você decida usar algum preprocessador de css, bom, aí o npm é o da vez, caso contrário o bower se traduz em resolver e esquecer.

Aqui nós vamos gerir o angular do lado do browserify mesmo. Comece removendo ele do bower:
"bower uninstall angular angular-route --save"


Agora remova eles do html:

Em seguida, entregue os dois ao novo patrão:
"npm install angular angular-route --save"

Por fim, fazemos o require dos dois lá no entry point dos scripts client-side:

Não esquecer de compilar com "browserify src-client/main.js -o www/spa.js".

Como? Vivo esquecendo de recompilar? Não tem problema, você pode usar o watchify e ter paz de espírito. Não vou cobrir ele porque... Porque não. tem ele e tem várias outras estratégias.

Nosso projeto está assim. Modularizado. Organizado. Nos trinques.

O mais doidera é que, no tocante a javascript, tanto o lado cliente quanto o lado servidor tem suas dependências elegantemente resolvidas pelo npm. E sim, isso permite até usar/criar uns meio de campo comuns ao dois lados, embora cheguem a rodar em runtimes separadas. Reuso benigno de código, aleluia aleluia!

Ferramentas de teste também ficam mais acessíveis às duas porções de código nesta abordagem.

O browserify é o sólido, testado e melhor integrado sistema de modularização client-side de todo o velho oeste, bem-vindo a, digamos, 2014.

Poderíamos parar por aqui, mas nós queremos mais.

Tem um garoto novo na cidade, e que tem recebido recentemente alguma atenção, em especial por conta de andar com os descolados da escola. Bem "prafrentex" mesmo.

Eita! Vamos tacar fogo no velho javascript e apagar com pauladas? Calma, nem tanto. As polêmicas ficam de lado hoje.

O Systemjs começou humilde ali em 2013 e os manos da comunidade trataram de dar moral pra ele.

Hoje ele é versátil, flexível e tem um parceiro capaz de resolver dependências client-side com bastante desenvoltura.

Vamos uma vez mais limpar nosso projeto. Mover os scripts de volta para o lado do cliente:


Nosso "server side" está limpo once more again. E está assim porque, à época da concepção deste longo tutorial o humilde locutor que vos fala não achou meios eruditos e laicos o bastante para seguir dividindo com o server-side porção alguma de javascript que fosse.

Hora de abrir trabalhos, à despeito de tudo, com a nossa derradeira estratégia.

Num novo terminal, abra a pasta www do projeto e comece com o seguinte:
"sudo npm -g install jspm"


E agora damos start no jspm dentro da nossa www:
"cd www ; npm install jspm --save-dev ; jspm init"

Agora calma que o susto é grande. A começar pelo package.json:

Note que ele descobriu que estava dentro de um projeto node e já posicionou as dependências de desenvolvimento do jeito certo. E botou de quebra um transpilador ali na estória. Pois é, amigos, assim como o Brasil, jspm não é para principiantes.

E agora tirando do bolso esse passo, vá ao seu index.html e promova as seguintes alterações:



Ainda no estilo anterior, faça isso:
"jspm install angular angular-route"

Não parando por aí, vamos mudar o estilo de importação de dependências para o estilo do es-6:

E pra fechar com a solução de bundle, vamos gerar um script com tudo dentro com o seguinte comando:

"jspm bundle-sfx fragments/main.js spa.js"

(lembrando que estamos na pasta www!)

Pronto! teremos, mais uma vez nosso app rodando.:


Bacana, né não? Seja muito bem vindo a 2016!

Em retrospecto, talvez pudéssemos manter a src-client no lado "servidor", uma vez que optamos pela opção de gerar um "bundle" (que a turminha do jspm batizou de "sfx", mas todos sabemos o que realmente é e do que se trata!) pro cliente.

A possibilidade de usar as novidades do es6 ali ao vivo é um plus. É, todavia, algo conquistável usando o plugin aproriado de transformação do browserify.

Como? Qual abordagem escolher? Jovem, estamos em 2016 e isso ainda é muito mais opinião do que certeza.

E falando em opiniões, aí vão um monte!

Ao passo que o browserify é a tal plataforma sólida que todos sonham, o systemjs apenas chegou na freguesia, embora tenha chegado de mãos dadas com o povo famosos do Google/angular2. Um outro modo de dizer isso é que o erro é mais claro de resolver se você estiver de browserify, mas no futuro a mensagem de erro do systemjs será tão boa quanto.

Angular2, inclusive, que pode ou não vingar. Vide meu artigo opinioso de um dia desses.

Só reforçando, basta que o angular 2 faça o roteador do jeito certo, porque de todo o resto eu acho que o esquema de componentes dele bota no chinelo o esquema de componentes do react, mas achar é de graça e falar é fácil. Embora menos bonito do que esse aqui, que infelizmente tá verde demais também. De todo modo, como falei, ainda estamos em 2016 e tem muita água pra rolar. Só esse parágrafo renderia uns 3 ou 4 outros artigos, que fico, por hora, devendo.

Por fim, explicar como modularizar seus scripts e conhecer as opções mais famosinhas que temos hoje era o meu objetivo aqui e espero ter atingido ele.

Agora você está por conta própria!

Sem mais.

***Rodada Bônus!!!***

Não posso esquecer do webpack, cara!

O tutorial oficial dos caras é muito bom, basta ler o que tem lá.

Vê lá também. Ele é bonito também.


Nenhum comentário :

Postar um comentário