Modularização em Javascript 101

Diego Nascimento
Liferay Engineering Brazil
4 min readNov 16, 2017

--

Nesse texto irei explicar a motivação de usar código modularizado, os formatos de modularização mais conhecidos.

Necessidade de modularizar o código

Todas as linguagens de programação modernas possuem uma alternativa para se trabalhar de forma modularizada, ajudando a criar pedaços de software que serão usados com um objetivo somente e de forma satisfatória. Modularizar o código funciona de forma análoga a dividir livros em capítulos e seções. Bons módulos, no entanto, são altamente auto-suficientes com funcionalidades bem definidas, o que lhes permitem ser adicionados ou removidos conforme a necessidade sem impactar no sistema como um todo.

Na teoria é meio complicado né? Vamos exemplificar essa definição:

Um módulo é um arquivo Python contendo definições e instruções. O nome do arquivo é o módulo com o sufixo .py adicionado.

e pode ser usado da seguinte forma:

Ainda no python podemos usar somente alguns pedaços do nosso módulo, melhorando assim a performance! Visto que não precisaremos importar todas as funções de greetings:

No javascript temos um problema grande com isso visto que desde a última implementação da linguagem(ECMA-262 5.1, que atualmente roda em todos os browsers modernos), não é possível trabalhar com módulos de forma nativa. Tendo em vista essa necessidade de trabalhar de forma modularizada, a comunidade desenvolveu formas de fazer isso utilizando module patterns como AMD,CommonJS e UMD.

CommonJS(CJS)

CommonJS é um grupo de comunidade que projeta e implementa especificações de javascript para declarar módulos, essa abordagem funciona muito bem em ambientes server-side visto que foi criado pela comunidade de Node.js para gerenciar seus módulos.

Um módulo de CJS é um pedaço reutilizável de javascript que exporta objetos específicos tornando-os disponível para que outros módulos possam chamá-lo em seus programas.

Com CommonJS, cada arquivo javascript armazena módulos em seu próprio contexto de módulo único (exatamente como envolvê-lo em um closure). Neste escopo, podemos usar o objeto module.exports para expor os módulos e require para importá-los.

Exemplo:

No arquivo ‘greetings.js’ estamos tornando possível exportar a função sayHello para ser usadas em outros arquivos da seguinte forma:

Utilizando sayHello:

Existem vários benefícios de usar esse pattern como: criar dependências explícitas e ter uma sintaxe bem compacta.

Como desvantagem temos que funciona perfeitamente no server-side, mas infelizmente, torna-se difícil de usá-lo quando escrito em javascript para o browser. Enquanto um script para carregar um módulo estiver sendo executado, isso bloqueará o browser do processamento de qualquer coisa até que essa task termine de carregar. Esse comportamento acontece por que a thread do javascript para, até que o código esteja completamente carregado.

AMD(Asynchronous Module Definition)

Carregar módulos utilizando AMD pode ser feito dessa forma:

O que acontece aqui é que a função ‘define’ toma como seu primeiro argumento um array das dependências dos módulos, as dependências são carregadas em background e uma vez carregadas, o ‘define’ chama a função de callback que foi dada. Depois, a função de callback leva, como argumento, as dependências que foram carregadas, como ‘greetings’, permitindo a função a usar essas dependências.

Ao contrário do CommonJS, o AMD tem uma abordagem browser-first com seu trabalho assíncrono. Além disso o formato dá suporte à módulos em JSON, funções, strings e muitos outros tipos, enquanto CJS suporta apenas objetos como módulos. Entretanto o AMD não é compatível com io, fs e outras características de server-side então não se usa esse formato para server-side.

UMD(Universal Module Definition)

É usado para projetos que precisam de suporte dos dois patterns(AMD e CommonJS). UMD cria um modo de usar os dois patterns usando o conceito de variável global viabilizando assim a reutilização de código tanto no browser quanto no server.

Exemplo:

Note o tratamento e a verificação se é do tipo ‘exports’ por exemplo no formato CJS.

ES6 ou NativeJS

É a maneira de como módulos estão sendo tratados no ES6, como já falei antes a linguagem na sua versão nativa não dava suporte a módulos e precisávamos utilizar caminhos alternativos para usar-los como AMD ou CJS.

Felizmente os membros do TC39(um grupo de usuários que decide a especificação da linguagem) decidiram incorporar eles à implementação mais recente da linguagem, deixando-a mais completa ❤

Como está sendo usado em ES6:

Primeiro implementaremos nossas funções que serão importadas, que pode ser de duas formas:

#1 — export functions!

Usando o identificador export na função que identifica que aquela função será exportada, possibilitando ser importada em outros scripts.

#2 — objeto exports no final do script

Podemos utilizar ao invés da definição anterior, criar um objeto de module.exports com as funções que serão exportadas daquele script.

Para utilizarmos os módulos em ES6 basta:

Essa notação é bastante limpa e bem mais intuitiva do que as outras e não precisaremos nos preocupar se aquela função X ou Y irá rodar no browser ou no server como nas outras definições que mostrei anteriormente.

Irei criar um post sobre module bundles e os porquês de você utilizar eles em breve, keep on line!

--

--

Diego Nascimento
Liferay Engineering Brazil

20, like things that flies, photo nonsense things, PhD in Procrastination. Computer Engineering student and Liferay's intern.