Vue e Vuetify por CDN sem build: versão fixa, SRI e a hora de migrar para o Vite
Use Vue por tag script com versão exata, hash de integridade e cópia própria do arquivo, conheça os limites do template no HTML e saiba os sinais de que a página virou aplicativo e precisa de build.
Segunda de manhã, a página de agendamento da clínica amanheceu com os botões desalinhados e o seletor de data sem abrir. Ninguém fez deploy desde quinta. O HTML puxava Vue e Vuetify de um CDN com a versão "mais recente", e o CDN passou a entregar outra versão no fim de semana. A resposta curta: Vue por CDN é legítimo para página pequena, desde que a URL aponte para uma versão exata, com hash de integridade e, de preferência, uma cópia do arquivo servida pelo seu próprio servidor. Quando a página passa a ter várias telas, componentes próprios em arquivos separados e mais de uma pessoa mexendo, é hora de um build com Vite e lockfile.
Se a dúvida é se você precisa de webpack para começar, o guia Vue sem webpack responde. Se o Vue vai morar dentro de páginas Razor, veja Vue em Razor Pages. Aqui o foco é fazer a versão sem build com segurança e reconhecer quando ela deixou de servir.
Por que a página quebrou sem commit
Uma URL como https://unpkg.com/vue@3/dist/vue.global.js não aponta para um arquivo fixo. O @3 é uma faixa: o CDN resolve para a versão 3 mais recente publicada no npm. A documentação usa essa forma porque é a mais curta para um exemplo, não porque seja adequada para produção. Sem número exato, cada nova versão publicada muda o código que roda na sua página, sem passar pelo seu teste.
Com uma biblioteca de componentes como o Vuetify, o risco cresce. O CSS e o JavaScript precisam ser da mesma versão, e ambos precisam ser compatíveis com a versão do Vue. Três faixas soltas significam três atualizações independentes que ninguém testou juntas.
Há ainda a dependência de terceiros em tempo real: se o CDN estiver lento, bloqueado na rede da empresa do cliente ou fora do ar, a página não monta. E se o arquivo servido for adulterado, ele roda com acesso total à página, incluindo formulários com dados de pacientes.
Como usar o CDN do jeito certo
- Fixe a versão exata de cada arquivo, a mesma que você testou. Nada de
@3ou@latest. - Use o build de produção (
vue.global.prod.js), que é minificado e não carrega avisos de desenvolvimento. - Adicione o atributo
integritycom o hash do arquivo, ecrossorigin="anonymous". Se o conteúdo mudar, o navegador recusa executar. - Prefira servir a cópia você mesmo. Baixe o arquivo daquela versão para
/js/vendor/e publique junto com o site. Some a dependência do CDN em tempo real. - Registre as versões num lugar só, como um comentário no topo do HTML ou um arquivo de notas, para quem vier depois saber o que está rodando.
- Atualize de propósito: troque a versão, rode o teste da página, publique.
A página de agendamento corrigida
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<title>Agendamento</title>
<!-- vue 3.4.21, vuetify 3.5.0: atualizar juntos e testar -->
<link rel="stylesheet" href="/js/vendor/vuetify-3.5.0.min.css">
</head>
<body>
<div id="app">
<v-app>
<v-main>
<v-container>
<v-text-field label="Nome completo" v-model="nome"></v-text-field>
<v-btn color="primary" @click="agendar">Agendar</v-btn>
</v-container>
</v-main>
</v-app>
</div>
<script src="/js/vendor/vue-3.4.21.global.prod.js"></script>
<script src="/js/vendor/vuetify-3.5.0.min.js"></script>
<script>
const app = Vue.createApp({
data() { return { nome: '' }; },
methods: {
agendar() { /* chama a API de agendamento */ }
}
});
app.use(Vuetify.createVuetify());
app.mount('#app');
</script>
</body>
</html>
Os arquivos em /js/vendor/ foram baixados uma vez das URLs com versão exata, como https://unpkg.com/vue@3.4.21/dist/vue.global.prod.js. Os números de versão do exemplo são ilustrativos: use os que você testou. Se preferir manter o CDN, use a URL com versão exata e o hash de integridade:
curl -s https://unpkg.com/vue@3.4.21/dist/vue.global.prod.js \
| openssl dgst -sha384 -binary | openssl base64 -A
<script src="https://unpkg.com/vue@3.4.21/dist/vue.global.prod.js"
integrity="sha384-COLE_AQUI_O_HASH_GERADO"
crossorigin="anonymous"></script>
Gere o hash do arquivo exato que você vai referenciar. Hash de outra versão ou de outro build faz o navegador bloquear o script, e a página não monta; é o comportamento correto, mas teste antes de publicar.
Os limites do template escrito no HTML
Sem build, o template é o próprio HTML da página, lido pelo navegador antes de o Vue compilar. A documentação do Vue chama isso de template no DOM, e ele tem regras que não existem nos arquivos .vue:
- Nomes de componente e de prop em kebab-case. O HTML não diferencia maiúsculas, então
<MeuCalendario>vira<meucalendario>. Escreva<meu-calendario>. - Sem tag autofechada em componente.
<v-text-field />não fecha nada no HTML; o navegador entende como tag aberta e engole o que vem depois. Feche explicitamente. - Restrições de aninhamento. Dentro de
<table>ou<ul>, o navegador move elementos desconhecidos para fora. A documentação indica o atributois="vue:nome-do-componente"para esses casos. - Compilação no navegador. O build global inclui o compilador de templates, que pesa no download e trabalha a cada carregamento.
Nada disso impede uma página simples. Tudo isso fica caro quando são vinte componentes.
Sinais de que a página virou aplicativo
A página de agendamento cresceu: agora tem login, lista de consultas, remarcação e painel do médico. Os sinais de que o CDN deixou de ser a escolha certa costumam ser estes: mais de uma tela com roteamento; componentes próprios espalhados em vários arquivos .js globais carregados em ordem frágil; dependências além de Vue e kit visual; mais de uma pessoa alterando o front; necessidade de testes automatizados. Nesse ponto, um projeto criado com npm create vue@latest usa Vite, gera arquivos com hash no nome a cada build, compila os templates antes do deploy e registra as versões no package-lock.json. O processo de deploy passa a publicar o que o build gerou, e o ambiente fica reproduzível em qualquer máquina.
A migração não precisa ser de uma vez. Dá para começar pela parte nova em Vite e manter a página antiga até ela ser substituída.
Um lembrete que vale para os dois caminhos: componente pronto não garante acessibilidade. Rótulo associado ao campo, foco visível e navegação por teclado continuam sendo responsabilidade de quem monta a tela.
Erros comuns
- URL com
@3ou@latestem produção. A página muda sozinha quando sai versão nova e quebra sem commit. - Vue e Vuetify em versões desencontradas. CSS de uma versão com JavaScript de outra gera layout quebrado e erro difícil de rastrear.
- Depender do CDN em tempo real sem plano. Bloqueio na rede do cliente ou queda do CDN deixa a página em branco.
- Script de terceiro sem
integrity. Um arquivo adulterado roda com acesso a todos os dados da página. - Tag autofechada no template do HTML. O navegador aninha o resto da página dentro do componente e o layout some.
- Continuar sem build depois que a página virou aplicativo. A ordem dos scripts globais vira o processo de deploy, e ninguém consegue reproduzir o ambiente.
Checklist
- Toda tag script e link de biblioteca aponta para uma versão exata.
- Vue, kit visual e CSS estão em versões testadas juntas e registradas.
- Arquivos vêm do seu servidor ou têm
integrityecrossorigin. - A produção usa o build
.prod.js. - Componentes no template do HTML usam kebab-case e tags fechadas.
- Existe um critério combinado para migrar para Vite quando a página crescer.
- Campos têm rótulo e a tela funciona só com teclado.
Abra o código-fonte da sua página que usa Vue por CDN: a URL do script tem um número de versão completo, ou um @3 que muda sem avisar?
Referência: https://vuetifyjs.com/en/getting-started/installation/#usage-with-cdn