À medida que o JavaScript se afasta mais e mais
do playground de linguagem de programação que costumava ser, e é cada
vez mais utilizado como núcleo para aplicações em larga escala,
gerenciar o código começa a ficar extremamente difícil. Dividir o código
em vários arquivos ajuda, embora também adicione mais complexidade de
uma maneira diferente. Uma forma de combater essa nova complexidade é
com as bibliotecas de gerenciamento de dependência, mas qual é a certa
para você?
Script loading vs dependency management
As duas bibliotecas mencionadas no título - $script.js e RequireJS -
não são tecnicamente classificadas da mesma forma, porque, embora façam
coisas semelhantes, elas possuem ênfases diferentes. $script.js é um
carregador de script com gerenciamento de dependência, enquanto que o
gerenciamento de dependência de RequireJS é muito mais poderoso e mais
parecido com o que você usaria para importar classes em linguagens
compiladas como Java. Você verá o que quero dizer em breve.
$script.js
Esta prática biblioteca foi criada por Dustin Diaz e Thornton Jacob e está hospedada no Github.
É onde você vai encontrar a documentação sobre como usá-la, mas ainda
vou mostrá-la um pouco aqui para dar uma ideia de como ela funciona.
Primeiro, vamos falar sobre o uso mais básico: o carregamento de um script.
$script('jquery.js');
Isso carrega jquery.js de forma assíncrona para a página. Porém não é mais útil do que apenas usar uma tag script normal. É ligeiramente menor, mas, uma vez que é carregado de forma assíncrona, o código logo após essa linha será executado antes que jquery.js seja carregada. Então também daremos a ela uma função callback que é executada após jquery.js ser carregada.
$script('jquery.js', function() { // do stuff with jQuery. });
Agora, uma vez que jquery.js for carregado e executado, teremos a
certeza de que podemos acessar os objetos e as funções que ela define.
Há muito mais que você pode fazer com $script.js - incluindo
dependências nomeadas -, mas isso lhe dá a essência de como usá-la. Com
isso, definimos com sucesso uma dependência e garantimos que ela mesma
seria carregada e executada antes que tentássemos usá-la. Utilizar algo
como isso nos permite precisar usar somente duas tags script em
nosso HTML (uma para carregar $script.js e outra para carregar o
aplicativo principal). O resto dos scripts dos quais dependemos pode ser
gerenciado com $script.js.
RequireJS
RequireJS é um projeto muito maior, com um projeto no Github e um site próprio.
Você encontrará a documentação para RequireJS no segundo link, mas se
quiser ler um pouco de história e uma introdução mais completa à
RequireJS, você pode ler este artigo no Adobe Developer Connection.
RequireJS pode ser usado quase exatamente como $script.js para
carregar arquivos javascript simples, mas é muito mais poderoso do que
isso. Você pode definir os módulos, e então carregar as dependências
deles sem expô-los globalmente, de modo que cada bit do seu código possa
estar seguro em relação a scripts de terceiros. Dê uma olhada.
Primeiro, vamos definir um módulo que pode ser carregado como uma dependência.
// This is just an object literal define({ name: "Joe Zim", gender: "male" }); // Here's a constructor so you can create // new objects, rather than just the one // literal object define(function() { var Person = function( name, gender ) { this.name = name; this.gender = gender; }; return Person; } );
Você pode ver dois tipos diferentes de módulos lá. O primeiro é
simplesmente definido como um objeto literal, que será o que é retornado
para o script dependente, como você verá mais tarde. O segundo exemplo
tem uma função, que será executada imediatamente quando o módulo estiver
carregado como uma dependência e o valor que for retornado daquela
função será o valor que é dado para o script dependente.
Agora, vamos criar um módulo que é dependente do mesmo que acabamos de definir. Vamos supor que o módulo acima é salvo como person.js. Aqui está como nós definimos outro módulo que é dependente do módulo que criamos nos bastidores.
define( [ 'person', 'list' ], function( Person, List ) { var personList = new List( new Person('Joe Zim', 'male'); ); return personList; } );
Definimos o módulo exatamente como fizemos antes, mas desta vez
enviamos um array como o primeiro parâmetro. O array apresenta strings
de nomes de arquivos (sem o ".js") dos módulos para buscar. Então,
quando esses módulos estão totalmente carregados, eles são enviados como
parâmetros para a função para o novo módulo que você está definindo.
Como indicado acima, isso localiza os módulos de modo que não estejam
acessíveis globalmente.
Agora, vamos escrever um pouco de código que é dependente do último módulo e do módulo person, mas não é a criação de um novo. Vamos assumir que o último módulo criado foi salvo como default-person-list.js.
require( [ 'default-person-list', 'person' ], function( list, Person ) { var chuck = new Person( 'Chuck Norris', 'male' ); list.addItem( chuck ); list.forEach( function( item ) { alert(item.name); } ); } );
Isso é quase exatamente o mesmo que criar um módulo que é dependente de outro módulo, exceto por algumas coisas importantes:
- Nós não utilizamos mais a função define; em vez disso, usamos require (finalmente sabemos de onde vem o nome da biblioteca!).
- Não há necessidade de retornar nada da função. Uma vez que isso
não está sendo definido como um módulo, basta executar do mesmo modo e,
portanto, não há necessidade de devolver nada.
Essa é a essência de RequireJS, mas há mais uma coisa que é muito
importante sobre ela. Ela vem com uma build tool que irá procurar
através de todos os módulos e outros arquivos JavaScript e concatenar e
diminuí-los, de modo que mesmo que você passe todo o tempo criando
arquivos separados, isso não vai se tornar um problema de desempenho.
O que é certo para você?
Se você é um programador muito modular e gosta da ideia de manter os
módulos localizados, então tomar a rota RequireJS é provavelmente uma
ideia realmente boa para você. Se o seu aplicativo é relativamente
simples ou você simplesmente não gosta de converter tudo em arquivos de
módulos individuais, então, algo como $ script.js provavelmente seria um
grande ajuste. Em última análise, cabe a você! Ambas são excelentes
ferramentas. Enfim, isso é tudo por hoje. Happy coding e Deus te
abençoe!
⁂
Texto original de Joe Zim, disponível em http://www.joezimjs.com/javascript/script-js-vs-requirejs-dependency-management-comparisons/