Como criar componentes JS usando módulos - AMD, CommonJS e UMD - Parte 3/3

Já vimos como modularizar nossos códigos usando AMD e CommonJS. Hoje veremos o Capitão Planeta dos módulos: o UMD!

UMD

Universal Module Definition (UMD), é o cara que vai unir os poderes do AMD e do CommonJS em um único componente! Na verdade, ele é responsável por verificar qual dos formatos está sendo usado, para que você não precise duplicar o seu código :)

Seu código usando UMD vai ficar mais ou menos assim:

1
2
3
4
5
6
7
8
9
10
11
12
13
;(function( root, factory ) {
if( typeof define === 'function' && define.amd ) {
define([ 'jquery' ], factory );
}
else if( typeof exports === 'object' ) {
module.exports = factory( require( 'jquery' ) );
}
else {
root.myModule = factory( root.jQuery );
}
})(this, function( $ ) {
return 'Hello World'!
});

Eu sei, o código é bem feio, e à primeira vista é meio ruim de entender. Mas vou quebrá-lo em partes para explicar melhor o que ele faz, ok? Vamos lá!

Basicamente, você vai iniciar com uma IIFE, ou função imediata, que é uma função auto-executável:

1
2
3
;(function() {

})();

Essa função vai receber dois parâmetros: o primeiro é o this, definido como root. Todo mundo sabe como o this em Javascript é polêmico, pois ele varia o seu valor conforme o escopo em que ele se encontra. No caso, estamos chamando o this no escopo global, logo, se eu estiver no client (browser), ele vai ser o objeto window. E se eu estiver no server (usando Node, por exemplo), ele será o objeto global.

O segundo parâmetro, definido como factory, é a função que vai executar seu módulo. Sabemos que, em Javascript, funções são objetos de primeira classe. Elas são tratadas como qualquer outro tipo de valor, logo, elas também podem ser passadas por parâmetro para outra função. O UMD se aproveita disso para deixar o negócio um pouco mais ilegível e difícil de entender :P

Mas olhando pelo lado bom, assim que você entende isso, fica fácil identificar cada parte :D

Então, passando os parâmetros, vai ficar assim:

1
2
3
;(function( root, factory ) {

})( this, function() {} );

Dentro da função passada como parâmetro vai o código do seu módulo:

1
2
3
4
5
;(function( root, factory ) {

})( this, function() {
// Código do seu módulo
});

Agora vamos ver o que acontece por dentro da função principal. Como o UMD identifica se você está usando AMD ou CommonJS.

O formato AMD tem por padrão a função define e uma propriedade amd nessa função. É isso que é verificado no primeiro if:

1
2
3
if( typeof define === 'function' && define.amd ) {
// ...
}

Ou seja, se existir um define e este for uma função, e a propriedade amd estiver definida para essa função, então o desenvolvedor está usando alguma lib no formato AMD. Sabendo disso, é só eu usar essa função define para “definir” meu módulo, passar as dependências no array e chamar a função que executa o módulo:

1
2
3
if( typeof define === 'function' && define.amd ) {
define([ 'jquery' ], factory );
}

Lembra quando falamos sobre AMD? Cada parâmetro da função do módulo representa uma depência do array, mantendo a ordem. Então a função que é passada como parâmetro (factory), precisa receber o parâmetro para chamar o jQuery dentro do nosso módulo, já que ele é uma dependência:

1
2
3
4
5
6
7
;(function( root, factory ) {
if( typeof define === 'function' && define.amd ) {
define([ 'jquery' ], factory );
}
})( this, function( $ ) {
// Código do seu módulo
});

Legal não? xD

Já resolvemos o problema do AMD :D

Agora, se o usuário não estiver usando AMD, vamos ver se ele está usando CommonJS, na próxima verificação:

1
2
3
else if( typeof exports === 'object' ) {
// ...
}

Vimos que uma das coisas que define o formato CommonJS é que ele tem um objeto exports. Então é isso que iremos verificar. Se exports existir, e for um objeto, exportamos nosso módulo:

1
2
3
4
5
// ...
else if( typeof exports === 'object' ) {
module.exports = factory( require( 'jquery' ) );
}
// ...

Como precisamos passar o jQuery como parâmetro ao nosso módulo, usamos o require, pois já vimos que é assim que uma dependência é incluída usando esse formato.

Resolvido também o CommonJS! o/

Mas e se o desenvolvedor quiser usar nosso módulo, mas não estiver usando nem AMD, e nem CommonJS?

Nesse caso, podemos dar um nome ao nosso módulo, exportando-o no escopo global, usando o root, que vai ser o objeto window, se ele estiver no browser, ou global, se ele estiver usando Node. Passamos também o objeto jQuery, que já deve estar também no escopo global:

1
2
3
else {
root.myModule = factory( root.jQuery );
}

Da hora né? :D

Existem algumas outras variações do UMD, que você pode ver aqui.

Então a solução é usar UMD pra tudo o que eu fizer?

Não jovem.

Para tudo o que for referente à sua aplicação em específico, você vai usar um único padrão: ou AMD, ou CommonJS, ou então algum pattern próprio.

Agora, quando você tiver um módulo genérico, que você quiser reutilizar em qualquer ambiente, e em qualquer projeto, é a hora de usar UMD.

Ficou claro? Se tiver alguma dúvida, comenta ae :D

Se você ficou um pouco perdido com esses formatos por não conhecer as ferramentas que trabalham com eles (RequireJS, Browserify, etc), não se preocupe! Em breve farei alguns posts falando melhor sobre essas ferramentas e vai ficar muito mais claro de entender :)

Até a próxima!

Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/

Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1