Grid com "is not defined" no console: o script da biblioteca carregou depois
O código que cria o grid roda antes do script que define a biblioteca, ou uma segunda cópia apaga a primeira. Veja como async, defer e script inline decidem a ordem e como corrigir de vez.
A tela de pedidos abre em branco. No console, Uncaught ReferenceError: GridKit is not defined. O HTML do grid está lá, a biblioteca está no servidor, e no computador do desenvolvedor funciona. A resposta curta: o código que cria o grid rodou antes do script que define a biblioteca. Quase sempre é um script inline executando antes de um script com defer ou async, um bundle que mudou de ordem, ou duas cópias da mesma biblioteca em que a segunda apaga a primeira. Carregue a base antes do componente, o componente antes do código que o usa, e uma versão só de cada um.
O exemplo deste guia usa uma biblioteca fictícia, GridKit, mas o problema é o mesmo do fórum original, com o objeto da Syncfusion, e o de qualquer biblioteca que se registra numa variável global: jQuery e seus plugins, componentes de UI de fornecedores, gráficos.
Por que a ordem dos scripts muda sem você mexer
O navegador executa scripts seguindo regras que dependem de como cada um foi incluído:
- Script clássico sem atributo bloqueia a leitura do HTML, baixa, executa e só então o navegador continua. A ordem é a do documento.
- Script com
deferbaixa em paralelo e executa depois que o HTML foi todo lido, na ordem em que aparece no documento, antes do eventoDOMContentLoaded. - Script com
asyncexecuta assim que termina de baixar, em qualquer ordem. Duas bibliotecas comasyncpodem rodar em ordens diferentes a cada carregamento. - Script inline (código dentro da própria tag) executa na hora em que o navegador chega nele. O atributo
defernão tem efeito em script inline. - Script
type="module"se comporta comodeferpor padrão.
Agora junte as peças. Alguém colocou defer nos scripts da biblioteca para "deixar o site mais rápido", e o código de inicialização ficou inline logo abaixo. O inline roda imediatamente; a biblioteca, só no fim. No computador do desenvolvedor, com cache cheio e um bundle antigo, a ordem era outra e funcionava.
Como diagnosticar em dois minutos
- Abra as ferramentas de desenvolvedor, aba Rede, marque "Disable cache" e recarregue. É o primeiro acesso de um usuário novo que importa.
- Leia a mensagem exata.
X is not defineddiz que o objeto global não existia naquele momento.Cannot read properties of undefineddiz que o objeto existe, mas uma parte dele não (um módulo da biblioteca que não foi carregado).$(...).algumPlugin is not a functioncostuma indicar jQuery carregado duas vezes. - Liste os scripts na ordem do documento, com os atributos, colando isto no console:
document.querySelectorAll('script').forEach((s, i) => {
console.log(i, s.src || '(inline)', 'async=' + s.async, 'defer=' + s.defer, 'type=' + (s.type || 'clássico'));
});
console.log('jQuery:', window.jQuery ? window.jQuery.fn.jquery : 'ausente');
4. Procure na aba Rede a biblioteca com status diferente de 200, ou carregada duas vezes de lugares diferentes (CDN e servidor próprio).
5. Clique no link da linha do erro no console. Ele mostra qual script tentou usar o objeto e em que ponto da página ele está.
Como corrigir de vez
A página que quebrava
<script src="/js/gridkit-base.min.js" defer></script>
<script src="/js/gridkit-grid.min.js" defer></script>
<script>
new GridKit.Grid('#pedidos', { url: '/api/pedidos' });
</script>
Os dois primeiros esperam o fim do HTML. O terceiro roda na hora, e GridKit ainda não existe.
A correção: inicialização num arquivo, também com defer
<div id="pedidos">Carregando pedidos...</div>
<script src="/js/gridkit-base.min.js" defer></script>
<script src="/js/gridkit-grid.min.js" defer></script>
<script src="/js/pedidos.js" defer></script>
Com defer nos três, a ordem de execução é a do documento: base, componente, inicialização. E o pedidos.js trata a falha em vez de deixar a tela em branco:
const alvo = document.getElementById('pedidos');
if (typeof window.GridKit === 'undefined' || typeof window.GridKit.Grid !== 'function') {
alvo.textContent = 'Não foi possível carregar a tabela de pedidos. Recarregue a página ou fale com o suporte.';
console.error('GridKit não carregou antes de pedidos.js');
} else {
new window.GridKit.Grid(alvo, { url: '/api/pedidos' });
}
Se você não pode tirar a inicialização do HTML (por exemplo, porque o servidor injeta dados ali), envolva o código inline num ouvinte de DOMContentLoaded. Scripts com defer executam antes desse evento, então a biblioteca já existe quando o ouvinte roda.
Uma cópia só de cada biblioteca
Se o jQuery vem do CDN no layout e de novo dentro do bundle da página, a segunda cópia substitui window.jQuery. Os plugins registrados na primeira somem, e o grid, que se pendurou nela, dá is not a function. Escolha uma origem, remova a outra e confira no console que só uma versão aparece. Se você usa um empacotador com módulos ES, prefira importar a biblioteca no arquivo que a usa: a ordem passa a ser garantida pelo import, não pela posição das tags. O guia de juntar scripts sem pegar tudo mostra como o bundle cresce e quebra quando inclui o kit inteiro.
Erros comuns
- Pôr
asyncem bibliotecas que dependem umas das outras. A ordem passa a depender da rede, e o erro aparece num acesso sim, noutro não. - Acrescentar
defernos scripts e deixar a inicialização inline. O inline roda antes, sempre. - Carregar a mesma biblioteca do CDN e do servidor. A segunda cópia apaga a primeira e leva os plugins junto.
- Confiar na ordem acidental de um bundle gerado por coringa de pasta. Um arquivo novo com nome que vem antes no alfabeto muda a ordem.
- Testar só com cache cheio. A versão antiga em cache esconde o erro que o usuário novo vê.
- Deixar a área do grid vazia quando o script falha. A pessoa vê uma tela em branco e não sabe se os pedidos sumiram.
Checklist
- A base da biblioteca aparece antes do componente, e o componente antes do código que o usa.
- Bibliotecas dependentes usam
defer(ou nenhum atributo), nuncaasync. - Não há inicialização inline dependendo de script com
defer, exceto dentro deDOMContentLoaded. - Cada biblioteca é carregada uma vez, de uma única origem.
- O código de inicialização confere se a biblioteca existe e mostra mensagem se não existir.
- A página foi testada com cache desativado, no primeiro carregamento.
- A ordem dos arquivos no bundle é explícita, não alfabética por acidente.
Para ganhos de velocidade que não quebram a ordem dos scripts, veja por onde começar a deixar o site mais rápido.
Abra a página do seu grid com o cache desativado e rode no console o trecho que lista os scripts: a biblioteca aparece antes do código que a usa, e com os atributos que garantem essa ordem?
Referência: https://www.syncfusion.com/forums/118939/grid-javascript-error-syncfusion-is-undefined