Qual é a diferença entre Require.js e simplesmente criar um elemento <script> no DOM? [fechadas]

138

Qual é a diferença entre usar o Require.JS amd simplesmente criando um <script>elemento no DOM?

Meu entendimento do Require.JS é que ele oferece a capacidade de carregar dependências, mas isso não pode ser feito simplesmente criando um <script>elemento que carrega o arquivo JS externo necessário?

Por exemplo, vamos assumir que eu tenho a função doStuff(), que requer a função needMe(). doStuff()está no arquivo externo do_stuff.js, enquanto needMe()está no arquivo externo need_me.js.

Fazendo isso da maneira Require.JS:

define(['need_me'],function(){
    function doStuff(){
        //do some stuff
        needMe();
        //do some more stuff
    }
});

Para fazer isso, basta criar um elemento de script:

function doStuff(){
    var scriptElement  = document.createElement('script');
    scriptElement.src = 'need_me.js';
    scriptElement.type = 'text/javascript';
    document.getElementsByTagName('head')[0].appendChild(scriptElement);

    //do some stuff
    needMe();
    //do some more stuff
}

Ambos funcionam. No entanto, a segunda versão não exige que eu carregue toda a biblioteca Require.js. Eu realmente não vejo nenhuma diferença funcional ...

maxedison
fonte
1
e quanto ao cache do navegador, o requirejs interfere nele?
Muhammad Umer
Estou reabrindo isso porque está pedindo a diferença entre duas coisas muito semelhantes. Ele pode ser respondida objetivamente, e tbh não vejo onde os laços de opinião para ele.
RamenChef

Respostas:

43

Aqui está o belo artigo no ajaxian.com sobre por que usá-lo:

RequireJS: carregamento assíncrono de JavaScript

  • algum tipo de # inclusão / importação / necessidade
  • capacidade de carregar dependências aninhadas
  • facilidade de uso para o desenvolvedor, mas depois apoiada por uma ferramenta de otimização que ajuda na implantação
Sarfraz
fonte
2
Eu os tinha lido, mas agora que penso mais nisso, percebo que a idéia de dependências aninhadas não pode ser alcançada simplesmente escrevendo tags <script>. Obrigado.
Maxedison
37
a "facilidade de uso do desenvolvedor" não poderia estar mais longe da verdade. Definitivamente, tem uma curva de aprendizado acentuada para você e qualquer pessoa que venha trabalhar nesse projeto.
Sahat Yalkabov
3
@TwilightPony Eu não me considero tão inteligente e exigente, não era realmente uma coisa difícil para mim. Isso elimina a necessidade de se preocupar com dependências e acelera a página. Seu código se torna mais alinhado com a programação do lado do servidor em como você declara suas dependências, que eu pessoalmente acho atualizadoras e simples. A sintaxe era mínima e restrita pelo design; em seguida, define o roteiro da produção para combinar facilmente seus scripts. Além disso, a depuração é como declarações estáticas. Não tenho certeza do que é mais fácil do que isso. Muito mais difícil para o outro lado, como eu fiz para o outro lado.
Jason Sebring #
Eu estou lutando. Especialmente com módulos que tentam se conectar a objetos globais. (React modules) ...
geilt 31/03/2015
1
Os comentários nessa página realmente me deixaram sentindo que alguém deveria fugir e não exigir. Especialmente a um perto da parte inferior que links para stevesouders.com/tests/require.php
Dave Kanter
52

Quais vantagens o Require.JS oferece em comparação à simples criação de um elemento no DOM?

No seu exemplo, você está criando a tag de script de forma assíncrona, o que significa que sua needMe()função seria invocada antes que o arquivo need_me.js conclua o carregamento. Isso resulta em exceções não capturadas onde sua função não está definida.

Em vez disso, para fazer o que você está sugerindo realmente funcionar, você precisa fazer algo assim:

function doStuff(){
    var scriptElement  = document.createElement('script');
    scriptElement.src = 'need_me.js';
    scriptElement.type = 'text/javascript';

    scriptElement.addEventListener("load", 
        function() { 
            console.log("script loaded - now it's safe to use it!");

            // do some stuff
            needMe();
            //do some more stuff

        }, false);

    document.getElementsByTagName('head')[0].appendChild(scriptElement);

}

Pode-se argumentar que pode ou não ser melhor usar um gerenciador de pacotes como o RequireJS ou utilizar uma estratégia de JavaScript puro, como demonstrado acima. Embora o aplicativo da Web possa carregar mais rapidamente, a invocação de funcionalidades e recursos no site seria mais lenta, pois envolveria a espera do carregamento dos recursos antes que a ação pudesse ser executada.

Se um aplicativo da Web for criado como um aplicativo de página única, considere que as pessoas não recarregarão a página com muita frequência. Nesses casos, pré-carregar tudo ajudaria a tornar a experiência mais rápida ao usar o aplicativo. Nesses casos, você está certo, basta carregar todos os recursos, incluindo as tags de script no cabeçalho ou no corpo da página.

No entanto, se a construção de um site ou aplicativo da Web que segue o modelo mais tradicional em que uma pessoa faz a transição de uma página para outra, causando a recarga de recursos, uma abordagem de carregamento lento pode ajudar a acelerar essas transições.

jmort253
fonte
10

Algumas outras razões bem apontadas para o uso do RequireJS fazem sentido:

  1. Gerenciar suas próprias dependências rapidamente se desfaz em projetos consideráveis.
  2. Você pode ter quantos arquivos pequenos quiser e não precisa se preocupar em controlar as dependências ou carregar a ordem.
  3. O RequireJS permite escrever um aplicativo modular inteiro sem tocar no objeto da janela.

Retirado dos comentários de rmurphey aqui nesta Gist .

Camadas de abstração podem ser um pesadelo para aprender e ajustar, mas quando serve a um propósito e o faz bem, faz sentido.

girls_can_code_too
fonte
9
Você ainda precisa gerenciar todos os requisitos e definir instruções, arquivos de configuração, colisões com outros sistemas e bibliotecas que não implementaram a especificação AMD etc. Tentei usar o Require.JS em um projeto node-webkit e o Require.JS briguei comigo a cada passo do caminho ... Compare isso com a simples solicitação de scripts de uma certa maneira ... É claro que você ganha um carregamento lento com o Require.JS, e foi por isso que tentei fazê-lo funcionar. :)
jmort253
Eu concordo totalmente com @ jmort253, foi uma luta no começo, mas agora eu gosto muito. Todos os três pontos estão corretos! E AMDificar uma biblioteca não deve ser tão difícil ... ou usar o calço.
Legends
0

Aqui está um exemplo mais concreto.

Estou trabalhando em um projeto com 60 arquivos. Temos dois modos diferentes de executá-lo.

  1. Carregue uma versão concatenada, 1 arquivo grande. (Produção)

  2. Carregue todos os 60 arquivos (desenvolvimento)

Estamos usando um carregador, então temos apenas um script na página da web

<script src="loader.js"></script>

O padrão é o modo 1 (carregando um arquivo concatenado grande). Para executar o modo 2 (arquivos separados), definimos alguma sinalização. Poderia ser qualquer coisa. Uma chave na string de consulta. Neste exemplo, apenas fazemos isso

<script>useDebugVersion = true;</script>
<script src="loader.js"></script>

loader.js tem algo parecido com isto

if (useDebugVersion) {
   injectScript("app.js");
   injectScript("somelib.js");
   injectScript("someotherlib.js");
   injectScript("anotherlib.js");
   ... repeat for 60 files ...
} else {
   injectScript("large-concatinated.js");
}

O script de construção é apenas um arquivo .sh que se parece com isso

cat > large-concantinated.js app.js somelib.js someotherlib.js anotherlib.js

etc ...

Se um novo arquivo for adicionado, provavelmente usaremos o modo 2, pois estamos desenvolvendo, temos que adicionar uma injectScript("somenewfile.js")linha ao loader.js

Mais tarde, para produção, também precisamos adicionar somenewfile.js ao nosso script de construção. Um passo que muitas vezes esquecemos e depois recebemos mensagens de erro.

Ao mudar para a AMD, não precisamos editar 2 arquivos. O problema de manter o loader.js e o script de compilação sincronizados desaparece. Usando r.jsou webpackele pode simplesmente ler o código para criarlarge-concantinated.js

Ele também pode lidar com dependências, por exemplo, tivemos 2 arquivos lib1.js e lib2.js carregados assim.

injectScript("lib1.js");
injectScript("lib2.js");

lib2 precisa da lib1. Tem um código dentro que faz algo como

lib1Api.installPlugin(...);

Mas, como os scripts injetados são carregados de forma assíncrona, não há garantia de que eles serão carregados na ordem correta. Esses 2 scripts não são scripts da AMD, mas usando o require.js, podemos dizer suas dependências

require.config({
    paths: {
        lib1: './path/to/lib1',
        lib2: './path/to/lib2',
    },
    shim: {
        lib1: {
            "exports": 'lib1Api',
        },
        lib2: {
            "deps": ["lib1"],
        },
    }
});

I nosso módulo que usa lib1 fazemos isso

define(['lib1'], function(lib1Api) {
   lib1Api.doSomething(...);
});

Agora, o require.js injetará os scripts para nós e não injetará a lib2 até que a lib1 seja carregada, pois informamos que a lib2 depende da lib1. Ele também não iniciará nosso módulo que usa lib1 até que ambos sejam carregados.

Isso torna o desenvolvimento agradável (sem etapa de compilação, sem se preocupar com a ordem de carregamento) e com a produção (sem a necessidade de atualizar um script de compilação para cada script adicionado).

Como um bônus adicional, podemos usar o plugin babel do webpack para executar o babel sobre o código em navegadores mais antigos e, novamente, não precisamos manter esse script de construção também.

Observe que, se o Chrome (nosso navegador preferido) começasse a oferecer suporte importreal, provavelmente mudaríamos para isso para desenvolvimento, mas isso realmente não mudaria nada. Ainda poderíamos usar o webpack para criar um arquivo concatenado e executar o babel sobre o código para todos os navegadores.

Tudo isso é obtido ao não usar tags de script e usar AMD

gman
fonte