Vize

Regras entre arquivos

Cada regra desta categoria reúne nesta página sua finalidade, configuração e exemplos incorreto e correto completos. Os exemplos de projeto também incluem os arquivos compartilhados, que devem ser usados nos dois casos. As linhas destacadas mostram a alteração; o código copiado preserva o conteúdo completo. As notas de suporte distinguem os diagnósticos disponíveis das convenções ilustrativas e dos contratos sem produtor atual; ativar um ID não implementa uma verificação ausente.

As verificações de projeto precisam do grafo completo de componentes analisados. Esta página reúne a finalidade, a configuração, os arquivos compartilhados e os exemplos incorreto e correto completos de cada entrada; use os arquivos compartilhados nos dois exemplos. Os 60 códigos publicados têm limites de suporte distintos: 19 pertencem à etapa da CLI, com 18 pares de código-fonte qualificados e um projeto Vue ilustrativo acompanhado de seu grafo de fluxo reativo; 16 têm produtores experimentais no analisador Rust, mas não são emitidos individualmente por essa etapa; 25 são contratos publicados sem produtor atual de diagnósticos. Os seis IDs de regras específicas de projeto são apresentados separadamente. Ativar um ID não ativa um produtor indisponível. As notas de cada exemplo preservam as limitações de fatos de código-fonte, a qualificação por grafo e as dependências específicas de versão.

A CLI pública expõe a mesma etapa por meio de vize lint --cross-file. Os códigos exibidos como vize:croquis/cf/* usam croquis/cf/* em vue.linter.rules (omita vize:). Diagnósticos de informação ou sugestão tornam-se avisos da CLI. As localizações relacionadas explicam a relação entre a origem e o consumidor.

Regra Exemplos Finalidade Suporte atual
ecosystem/vue-router-extra-param Incorreto · Correto A rota não declara tab; o Vue Router o descarta. ID de lint específico do projeto
ecosystem/vue-router-missing-param Incorreto · Correto O parâmetro obrigatório postId está ausente; depender da rota atual é frágil. ID de lint específico do projeto
ecosystem/vue-router-param-type Incorreto · Correto postId não é repetível, portanto um array é inválido. ID de lint específico do projeto
ecosystem/vue-router-unknown-route Incorreto · Correto O nome está ausente do roteador completo instalado. ID de lint específico do projeto
html/cross-component-nesting Incorreto · Correto Verificar o aninhamento HTML real após compor os componentes importados. ID de lint específico do projeto
vize:croquis/cf/array-mutation Incorreto · Correto Um array é alterado por índice, o que um array reativo não rastreia. Contrato; sem produtor atual
vize:croquis/cf/async-boundary Incorreto · Correto O estado reativo atravessa um limite assíncrono e pode ser observado desatualizado. CLI
vize:croquis/cf/async-no-suspense Incorreto · Correto Um componente assíncrono é renderizado sem um limite Suspense. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/browser-api-ssr Incorreto · Correto Uma API exclusiva do navegador é usada onde o componente pode ser renderizado no servidor. CLI
vize:croquis/cf/circular-dep Incorreto · Correto Os componentes importam uns aos outros em um ciclo. Contrato; sem produtor atual
vize:croquis/cf/circular-reactive-dependency Incorreto · Correto Os cálculos reativos dependem uns dos outros em um ciclo. CLI
vize:croquis/cf/closure-captures-reactive Incorreto · Correto Um fechamento captura um valor reativo e não verá atualizações posteriores. Contrato; sem produtor atual
vize:croquis/cf/composable-outside-setup Incorreto · Correto Uma função de composição é chamada fora de setup. Contrato; sem produtor atual
vize:croquis/cf/computed-side-effects Incorreto · Correto Uma função de leitura computada escreve no estado ou produz outro efeito colateral. Contrato; sem produtor atual
vize:croquis/cf/deep-import Incorreto · Correto Uma cadeia de importações é mais profunda do que o projeto permite. Contrato; sem produtor atual
vize:croquis/cf/destructuring-breaks-reactivity Incorreto · Correto Desestruturar um objeto reativo copia os campos e perde o rastreamento. CLI
vize:croquis/cf/di-outside-setup Incorreto · Correto provide ou inject é chamado fora de setup. Contrato; sem produtor atual
vize:croquis/cf/dom-access-without-next-tick Incorreto · Correto O DOM é lido antes de o Vue aplicar a atualização. Contrato; sem produtor atual
vize:croquis/cf/duplicate-id Incorreto · Correto O mesmo id de elemento é usado em mais de um componente. CLI
vize:croquis/cf/event-listener-leak Incorreto · Correto Um ouvinte de eventos é registrado e nunca removido. Contrato; sem produtor atual
vize:croquis/cf/event-modifier Incorreto · Correto Um ouvinte de eventos usa um modificador que o evento emitido não suporta. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/hydration-risk Incorreto · Correto Este código de diagnóstico agrupa várias ocorrências de reatividade, incluindo uma prop copiada para uma ref. Ele não implica que toda expressão Date.now() seja detectada pela passagem de análise entre arquivos. CLI
vize:croquis/cf/inherit-attrs-unused Incorreto · Correto inheritAttrs: false está definido e o componente nunca lê os atributos. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/inject-without-symbol Incorreto · Correto inject usa uma chave comum em vez de um símbolo InjectionKey. CLI
vize:croquis/cf/injected-async-mutation-race Incorreto · Correto Um valor injetado é alterado por uma tarefa assíncrona sujeita a uma condição de corrida. CLI
vize:croquis/cf/lifecycle-outside-setup Incorreto · Correto Um gancho de ciclo de vida é registrado fora de setup. Contrato; sem produtor atual
vize:croquis/cf/lifecycle-without-cleanup Incorreto · Correto Um gancho de ciclo de vida inicia um trabalho e nunca faz sua limpeza. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/missing-required-prop Incorreto · Correto Uma prop obrigatória não é passada. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/missing-suspense Incorreto · Correto Uma dependência assíncrona é usada fora de um limite Suspense. Contrato; sem produtor atual
vize:croquis/cf/module-scope-reactive Incorreto · Correto O estado reativo é criado no escopo do módulo e compartilhado por todos os chamadores. Contrato; sem produtor atual
vize:croquis/cf/multi-root-attrs Incorreto · Correto Um componente com múltiplas raízes recebe atributos e não tem onde colocá-los. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/mutated-after-escape Incorreto · Correto Um objeto reativo é alterado depois de escapar de seu proprietário. Contrato; sem produtor atual
vize:croquis/cf/non-reactive-provide Incorreto · Correto Um valor fornecido não é reativo, portanto os descendentes não verão atualizações. CLI
vize:croquis/cf/non-unique-id Incorreto · Correto Um id de elemento dentro de um laço não é único por item. CLI
vize:croquis/cf/object-identity-comparison Incorreto · Correto Um objeto reativo é comparado por identidade, que muda ao remover os invólucros. Contrato; sem produtor atual
vize:croquis/cf/pinia-getter Incorreto · Correto Uma função de leitura do Pinia é lida sem storeToRefs, portanto não permanecerá reativa. Contrato; sem produtor atual
vize:croquis/cf/prop-type-mismatch Incorreto · Correto O valor de uma prop passada não corresponde ao tipo declarado. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/provide-inject-type Incorreto · Correto Um valor fornecido e sua injeção não têm o mesmo tipo. CLI
vize:croquis/cf/provide-without-symbol Incorreto · Correto provide usa uma chave comum em vez de um símbolo InjectionKey. CLI
vize:croquis/cf/reactive-export Incorreto · Correto O estado reativo é exportado pelo módulo. Contrato; sem produtor atual
vize:croquis/cf/reactivity-outside-setup Incorreto · Correto Uma API reativa é chamada fora de setup. Contrato; sem produtor atual
vize:croquis/cf/reassignment-breaks-reactivity Incorreto · Correto Reatribuir uma variável reativa a substitui por um valor simples. CLI
vize:croquis/cf/reference-escapes-scope Incorreto · Correto Uma referência reativa escapa do escopo responsável por seu ciclo de vida. Contrato; sem produtor atual
vize:croquis/cf/setup-context-violation Incorreto · Correto O contexto de setup é usado de uma forma que o Vue não permite. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/shallow-deep-access Incorreto · Correto Uma propriedade profunda de um valor shallowReactive ou shallowRef é lida como se fosse rastreada. Contrato; sem produtor atual
vize:croquis/cf/spread-breaks-reactivity Incorreto · Correto Espalhar um objeto reativo copia seus valores e perde o rastreamento. CLI
vize:croquis/cf/suspense-no-fallback Incorreto · Correto <Suspense> não tem conteúdo alternativo. Contrato; sem produtor atual
vize:croquis/cf/template-ref-timing Incorreto · Correto Uma ref de template é lida antes da montagem do componente. Contrato; sem produtor atual
vize:croquis/cf/toraw-mutation Incorreto · Correto toRaw é usado e o objeto bruto é alterado em seguida. Contrato; sem produtor atual
vize:croquis/cf/uncaught-error Incorreto · Correto Um componente pode lançar um erro e nenhum limite de erro o captura. CLI
vize:croquis/cf/undeclared-emit Incorreto · Correto O componente emite um evento que não foi declarado. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/undeclared-prop Incorreto · Correto Um pai passa uma prop que o filho não declara. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/undefined-slot Incorreto · Correto Um pai preenche um slot que o filho não expõe. Contrato; sem produtor atual
vize:croquis/cf/unhandled-event Incorreto · Correto Um filho emite um evento que nenhum pai trata. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/unmatched-inject Incorreto · Correto inject nomeia uma chave que nenhum ancestral fornece. CLI
vize:croquis/cf/unmatched-listener Incorreto · Correto Um pai escuta um evento que o filho não emite. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/unregistered-component Incorreto · Correto Um template usa um componente que não foi registrado nem importado. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/unresolved-import Incorreto · Correto Uma importação não resolve para um módulo. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/unused-attrs Incorreto · Correto Atributos de herança automática são passados para um componente com múltiplas raízes que não os usa. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/unused-emit Incorreto · Correto Um evento declarado nunca é emitido. Analisador Rust experimental; não é um código individual da CLI
vize:croquis/cf/unused-provide Incorreto · Correto Uma chave fornecida nunca é injetada. CLI
vize:croquis/cf/value-extraction-breaks-reactivity Incorreto · Correto Ler um valor reativo para uma variável local perde as atualizações posteriores. CLI
vize:croquis/cf/watch-can-be-computed Incorreto · Correto Um observador apenas copia um valor para o estado e pode ser um valor computado. Contrato; sem produtor atual
vize:croquis/cf/watcheffect-async Incorreto · Correto watchEffect inicia uma tarefa assíncrona e não consegue limpar a execução anterior. CLI
vize:croquis/cf/watcher-outside-setup Incorreto · Correto watch ou watchEffect é chamado fora de setup. Contrato; sem produtor atual
vue/cross-file-attrs-fallthrough Incorreto · Correto Um pai passa atributos para um filho resolvido cuja raiz não pode herdá-los e que não usa $attrs explicitamente. ID de lint específico do projeto

Todas as regras · Opções das regras · Mapa de migração do ESLint · Verificações do projeto · Atributos entre componentes

ecosystem/vue-router-extra-param

A rota não declara tab; o Vue Router o descarta.

Severidade padrão: error
Aplicável a: Declarações alcançáveis do projeto e componentes importados
Opções: crossFile; severidade da regra (off/warn/error)
Correção automática: Nenhuma

O roteador completo instalado deve ser alcançável a partir de createApp(...).use(router) da aplicação. Tabelas de rotas desconhecidas ou dinâmicas não comprovam diagnósticos de nomes desconhecidos. Parâmetros ausentes geram avisos porque a navegação pode herdar um valor da rota atual.

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "ecosystem/vue-router-extra-param": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

index.html

<div id="app"></div>
<script type="module" src="/src/main.ts"></script>

src/main.ts

import { createApp } from "vue";
import App from "./App.vue";
import { router } from "./router";
createApp(App).use(router).mount("#app");

src/App.vue

<script setup lang="ts">
import { RouterView } from "vue-router";
</script>
<template><RouterView /></template>

src/router.ts

import { createRouter, createWebHistory } from "vue-router";
import UserPost from "./UserPost.vue";
export const router = createRouter({
  history: createWebHistory(),
  routes: [{ path: "/users/:userId/posts/:postId", name: "user-post", component: UserPost }],
});

Incorreto

O caminho user-post declara userId e postId, mas a navegação também fornece tab, que não foi declarado, como parâmetro do caminho.

src/UserPost.vue

<script setup lang="ts">
import { useRouter } from "vue-router";
const router = useRouter();
router.push({ name: "user-post", params: { userId: "1", postId: "2", tab: "a" } });
</script>
<template><p>Post</p></template>

Correto

Remova tab de params e mantenha apenas as chaves presentes no caminho da rota. Use query separadamente se a aplicação precisar selecionar uma aba.

src/UserPost.vue

<script setup lang="ts">
import { useRouter } from "vue-router";
const router = useRouter();
router.push({ name: "user-post", params: { userId: "1", postId: "2" } });
</script>
<template><p>Post</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Índice de verificações entre arquivos

ecosystem/vue-router-missing-param

O parâmetro obrigatório postId está ausente; depender da rota atual é frágil.

Severidade padrão: warning
Aplicável a: Declarações alcançáveis do projeto e componentes importados
Opções: crossFile; severidade da regra (off/warn/error)
Correção automática: Nenhuma

O roteador completo instalado deve ser alcançável a partir de createApp(...).use(router) da aplicação. Tabelas de rotas desconhecidas ou dinâmicas não comprovam diagnósticos de nomes desconhecidos. Parâmetros ausentes geram avisos porque a navegação pode herdar um valor da rota atual.

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "ecosystem/vue-router-missing-param": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

index.html

<div id="app"></div>
<script type="module" src="/src/main.ts"></script>

src/main.ts

import { createApp } from "vue";
import App from "./App.vue";
import { router } from "./router";
createApp(App).use(router).mount("#app");

src/App.vue

<script setup lang="ts">
import { RouterView } from "vue-router";
</script>
<template><RouterView /></template>

src/router.ts

import { createRouter, createWebHistory } from "vue-router";
import UserPost from "./UserPost.vue";
export const router = createRouter({
  history: createWebHistory(),
  routes: [{ path: "/users/:userId/posts/:postId", name: "user-post", component: UserPost }],
});

Incorreto

A navegação omite o parâmetro obrigatório postId do caminho user-post. Isso é um aviso porque o Vue Router pode herdar um valor da rota atual.

src/UserPost.vue

<script setup lang="ts">
import { useRouter } from "vue-router";
const router = useRouter();
router.push({ name: "user-post", params: { userId: "1" } });
</script>
<template><p>Post</p></template>

Correto

Passe userId e postId explicitamente para que a navegação não dependa do estado dos parâmetros da rota atual.

src/UserPost.vue

<script setup lang="ts">
import { useRouter } from "vue-router";
const router = useRouter();
router.push({ name: "user-post", params: { userId: "1", postId: "2" } });
</script>
<template><p>Post</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Índice de verificações entre arquivos

ecosystem/vue-router-param-type

postId não é repetível, portanto um array é inválido.

Severidade padrão: error
Aplicável a: Declarações alcançáveis do projeto e componentes importados
Opções: crossFile; severidade da regra (off/warn/error)
Correção automática: Nenhuma

O roteador completo instalado deve ser alcançável a partir de createApp(...).use(router) da aplicação. Tabelas de rotas desconhecidas ou dinâmicas não comprovam diagnósticos de nomes desconhecidos. Parâmetros ausentes geram avisos porque a navegação pode herdar um valor da rota atual.

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "ecosystem/vue-router-param-type": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

index.html

<div id="app"></div>
<script type="module" src="/src/main.ts"></script>

src/main.ts

import { createApp } from "vue";
import App from "./App.vue";
import { router } from "./router";
createApp(App).use(router).mount("#app");

src/App.vue

<script setup lang="ts">
import { RouterView } from "vue-router";
</script>
<template><RouterView /></template>

src/router.ts

import { createRouter, createWebHistory } from "vue-router";
import UserPost from "./UserPost.vue";
export const router = createRouter({
  history: createWebHistory(),
  routes: [{ path: "/users/:userId/posts/:postId", name: "user-post", component: UserPost }],
});

Incorreto

postId é um parâmetro escalar do caminho, mas a navegação fornece a ele o array ["2"].

src/UserPost.vue

<script setup lang="ts">
import { useRouter } from "vue-router";
const router = useRouter();
router.push({ name: "user-post", params: { userId: "1", postId: ["2"] } });
</script>
<template><p>Post</p></template>

Correto

Passe o escalar "2" para o segmento postId, que não é repetível.

src/UserPost.vue

<script setup lang="ts">
import { useRouter } from "vue-router";
const router = useRouter();
router.push({ name: "user-post", params: { userId: "1", postId: "2" } });
</script>
<template><p>Post</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Índice de verificações entre arquivos

ecosystem/vue-router-unknown-route

O nome está ausente do roteador completo instalado.

Severidade padrão: error
Aplicável a: Declarações alcançáveis do projeto e componentes importados
Opções: crossFile; severidade da regra (off/warn/error)
Correção automática: Nenhuma

O roteador completo instalado deve ser alcançável a partir de createApp(...).use(router) da aplicação. Tabelas de rotas desconhecidas ou dinâmicas não comprovam diagnósticos de nomes desconhecidos. Parâmetros ausentes geram avisos porque a navegação pode herdar um valor da rota atual.

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "ecosystem/vue-router-unknown-route": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

index.html

<div id="app"></div>
<script type="module" src="/src/main.ts"></script>

src/main.ts

import { createApp } from "vue";
import App from "./App.vue";
import { router } from "./router";
createApp(App).use(router).mount("#app");

src/App.vue

<script setup lang="ts">
import { RouterView } from "vue-router";
</script>
<template><RouterView /></template>

src/router.ts

import { createRouter, createWebHistory } from "vue-router";
import UserPost from "./UserPost.vue";
export const router = createRouter({
  history: createWebHistory(),
  routes: [{ path: "/users/:userId/posts/:postId", name: "user-post", component: UserPost }],
});

Incorreto

O roteador instalado alcançável declara user-post, mas a navegação usa o nome incorreto user-posts.

src/UserPost.vue

<script setup lang="ts">
import { useRouter } from "vue-router";
const router = useRouter();
router.push({ name: "user-posts", params: { userId: "1", postId: "2" } });
</script>
<template><p>Post</p></template>

Correto

Use o nome registrado user-post, mantendo os dois parâmetros de caminho declarados.

src/UserPost.vue

<script setup lang="ts">
import { useRouter } from "vue-router";
const router = useRouter();
router.push({ name: "user-post", params: { userId: "1", postId: "2" } });
</script>
<template><p>Post</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Índice de verificações entre arquivos

html/cross-component-nesting

Verificar o aninhamento HTML real após compor os componentes importados.

Severidade padrão: warning
Aplicável a: Declarações alcançáveis do projeto e componentes importados
Opções: crossFile; severidade da regra (off/warn/error)
Correção automática: Nenhuma

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "html/cross-component-nesting": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O <p> do pai contém um filho resolvido cuja raiz é <div>, produzindo um aninhamento inválido de bloco em parágrafo após a composição.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><p><Child /></p></template>

Child.vue

<template><div>Block content</div></template>

Correto

Use um contêiner <section> que possa conter o elemento de bloco do filho; o filho permanece inalterado.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><section><Child /></section></template>

Child.vue

<template><div>Block content</div></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Índice de verificações entre arquivos

vize:croquis/cf/array-mutation

Um array é alterado por índice, o que um array reativo não rastreia.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Apenas para o Vue 2.7 histórico: use dependências compatíveis do Vue 2.7 e do compilador de SFCs neste cenário. Os proxies do Vue 3 rastreiam atribuições a índices de arrays, então items[0] = next é reativo no Vue 3 e não é um defeito do Vue 3. Este código publicado não tem produtor atual.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import Vue from 'vue';
import App from './App.vue';
new Vue({ render: h => h(App) }).$mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

App.vue

<script lang="ts">
import Vue from 'vue';
import { replaceFirst } from './replace-first';
export default Vue.extend({
  data() { return { items: ['Before'] }; },
  methods: { replace() { replaceFirst(this.items, 'After'); } },
});
</script>
<template><section><p>{{ items[0] }}</p><button @click="replace">Replace</button></section></template>

Incorreto

Neste projeto histórico com Vue 2.7, items[0] = next altera o array sem notificar o observador de arrays do Vue 2, de modo que o primeiro item exibido pode não ser atualizado.

replace-first.ts

export function replaceFirst(items: string[], next: string): void {
  items[0] = next;
}

Correto

splice(0, 1, next) usa o método de mutação de arrays observado pelo Vue 2, permitindo que a mesma substituição atualize a visualização.

replace-first.ts

export function replaceFirst(items: string[], next: string): void {
  items.splice(0, 1, next);
}

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/async-boundary

O estado reativo atravessa um limite assíncrono e pode ser observado desatualizado.

Severidade padrão: error
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/async-boundary": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./SearchPage.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

api.ts

export interface Result { items: string[]; }
export async function load(query: string, options?: { signal?: AbortSignal }): Promise<Result> {
  const response = await fetch(`/search?q=${encodeURIComponent(query)}`, options);
  return response.json();
}

Incorreto

Uma consulta antiga mais lenta pode terminar após uma consulta mais recente e sobrescrever result, porque o observador não tem limpeza na invalidação.

SearchPage.vue

<script setup lang="ts">
import { ref } from "vue";
import SearchResults from "./SearchResults.vue";

const query = ref("");
</script>

<template>
  <SearchResults :query="query" />
</template>

SearchResults.vue

<script setup lang="ts">
import { load, type Result } from "./api";
import { ref, watch } from "vue";

const props = defineProps<{ query: string }>();
const result = ref<Result | null>(null);

watch(
  () => props.query,
  async (value) => {
    result.value = await load(value);
  },
);
</script>

Correto

Registre a limpeza antes de aguardar: aborte a requisição antiga e invalide seu sinalizador active; depois, atribua apenas uma resposta que ainda esteja ativa.

SearchPage.vue

<script setup lang="ts">
import { ref } from "vue";
import SearchResults from "./SearchResults.vue";

const query = ref("");
</script>

<template>
  <SearchResults :query="query" />
</template>

SearchResults.vue

<script setup lang="ts">
import { load, type Result } from "./api";
import { ref, watch } from "vue";

const props = defineProps<{ query: string }>();
const result = ref<Result | null>(null);

watch(
  () => props.query,
  async (value, _oldValue, onCleanup) => {
    const controller = new AbortController();
    let active = true;

    onCleanup(() => {
      active = false;
      controller.abort();
    });

    const next = await load(value, { signal: controller.signal });
    if (active) result.value = next;
  },
);
</script>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/async-no-suspense

Um componente assíncrono é renderizado sem um limite Suspense.

Severidade padrão: warning
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Suporte atual: no-source-async-fact

O produtor de diagnósticos de limites lê macros.is_async(), mas a análise do código-fonte atualmente registra o await de nível superior no escopo de script-setup. Portanto, o par completo de código-fonte incorreto e correto abaixo não produz um diagnóstico async-no-suspense pela CLI atual. Ele explica a convenção de Suspense; fornecer o fato ausente da macro é um trabalho posterior de implementação.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O filho tem await no nível superior, mas o pai não fornece um limite <Suspense>. A análise atual do código-fonte não fornece o fato de macro necessário para emitir este código de diagnóstico.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child /></template>

Child.vue

<script setup lang="ts">
const greeting = await Promise.resolve("Hello");
</script>
<template><p>{{ greeting }}</p></template>

Correto

O pai envolve o mesmo filho assíncrono em <Suspense> com conteúdo alternativo de carregamento. Isso demonstra a convenção; a passagem atual continua sem emitir o diagnóstico para nenhuma das duas alternativas de código-fonte.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Suspense><Child /><template #fallback><p>Loading</p></template></Suspense></template>

Child.vue

<script setup lang="ts">
const greeting = await Promise.resolve("Hello");
</script>
<template><p>{{ greeting }}</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/browser-api-ssr

Uma API exclusiva do navegador é usada onde o componente pode ser renderizado no servidor.

Severidade padrão: warning
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/browser-api-ssr": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

window.innerWidth é executado durante setup, quando um ambiente SSR não tem o window do navegador.

App.vue

<script setup lang="ts">
const width = window.innerWidth;
</script>
<template><p>Content</p></template>

Correto

Inicialize uma ref com um valor seguro para o servidor e leia window dentro de onMounted, que é executado após a montagem no cliente.

App.vue

<script setup lang="ts">
import { onMounted, ref } from "vue";
const width = ref(0);
onMounted(() => { width.value = window.innerWidth; });
</script>
<template><p>Content</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/circular-dep

Os componentes importam uns aos outros em um ciclo.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Isto ilustra um ciclo concreto de inicialização imediata. Um componente Vue recursivo ou qualquer importação circular não é automaticamente incorreto. Nenhum produtor atual emite este código de contrato.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

App.vue

<script setup lang="ts">
import { aLabel } from './a';
</script>

<template>
<p>{{ aLabel }}</p>
</template>

labels.ts

export const aPrefix = 'A';
export const bPrefix = 'B';

Incorreto

a.ts importa b.ts, que importa a.ts de volta. Ambos inicializam imediatamente uma constante a partir da constante ainda não inicializada do outro módulo, causando uma falha de zona morta temporal.

a.ts

import { bLabel } from './b';
export const aLabel = 'A' + bLabel;

b.ts

import { aLabel } from './a';
export const bLabel = 'B' + aLabel;

Correto

Os dois módulos leem prefixos inicializados do módulo independente labels.ts, removendo o ciclo e a leitura cruzada durante a inicialização imediata.

a.ts

import { bPrefix } from './labels';
export const aLabel = 'A' + bPrefix;

b.ts

import { aPrefix } from './labels';
export const bLabel = 'B' + aPrefix;

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/circular-reactive-dependency

Os cálculos reativos dependem uns dos outros em um ciclo.

Severidade padrão: dependente do contexto
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/circular-reactive-dependency": "warn" },
    },
  },
});
vp run lint

Qualificação do exemplo: illustrative-source-pair

O projeto Vue completo abaixo ilustra a realimentação de atualizações e sua correção. Ele não é uma comprovação qualificada de diagnóstico da CLI: o produtor de diagnósticos exige identidades de referências e arestas de fluxo reativo retidas, como mostra o grafo que acompanha o exemplo. Esses arquivos-fonte não demonstram que o processamento atual do código-fonte emitirá este código exato. Os controles específicos de diagnóstico de grafos com IDs rastreados permanecem separados das verificações de gramática do código-fonte.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

count-key.ts

import type { InjectionKey, Ref } from 'vue';
export const countKey: InjectionKey<Ref<number>> = Symbol('count');

App.vue

<script setup lang="ts">
import { provide, ref } from 'vue';
import { countKey } from './count-key';
import CycleView from './CycleView.vue';
const count = ref(1); // A: the provider-owned source.
provide(countKey, count);
</script>
<template>
  <button @click="count++">Increment</button>
  <CycleView />
</template>

Incorreto

App possui e fornece count (A). CycleView deriva nextCount (B) e imediatamente escreve cada valor derivado de volta no mesmo count injetado. Cada escrita altera novamente a entrada do cálculo, criando um ciclo de realimentação de atualizações A → B → A. As identidades no grafo preservado abaixo representam essas duas referências, não variáveis sem relação que tenham nomes iguais.

CycleView.vue

<script setup lang="ts">
import { computed, inject, watch } from 'vue';
import { countKey } from './count-key';
const count = inject(countKey)!; // App provides this same A reference.
const nextCount = computed(() => count.value + 1); // B: the derived consumer.
watch(nextCount, value => { count.value = value; }, { immediate: true });
</script>
<template><p>{{ nextCount }}</p></template>
Tracked references: A = provider source; B = consumer reference
Tracked flows: A -> B; B -> A

Correto

Remova o observador que escreve B de volta em A. App mantém a propriedade de count e o altera apenas por meio de sua ação explícita Increment; CycleView lê o nextCount derivado sem realimentar o resultado. As mesmas referências preservam apenas a dependência A → B.

CycleView.vue

<script setup lang="ts">
import { computed, inject } from 'vue';
import { countKey } from './count-key';
const count = inject(countKey)!; // App provides this same A reference.
const nextCount = computed(() => count.value + 1); // B: the derived consumer.
</script>
<template><p>{{ nextCount }}</p></template>
Tracked references: A = provider source; B = consumer reference
Tracked flows: A -> B

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/closure-captures-reactive

Um fechamento captura um valor reativo e não verá atualizações posteriores.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

App.vue

<script setup lang="ts">
import { computed, ref } from 'vue';
import { makeReader } from './reader';
const count = ref(0);
const read = makeReader(count);
const shown = computed(read);
</script>

<template>
<button @click="count++">Increment {{ count }}</button><p>{{ shown }}</p>
</template>

Incorreto

makeReader copia count.value antes de criar o fechamento. O leitor computado retorna então esse número inicial sem ler uma dependência reativa.

reader.ts

import type { Ref } from 'vue';
export function makeReader(count: Ref<number>): () => number {
  const captured = count.value;
  return () => captured;
}

Correto

O fechamento lê count.value quando é chamado, de modo que a função de leitura computada pode rastrear a ref e atualizar shown após os incrementos.

reader.ts

import type { Ref } from 'vue';
export function makeReader(count: Ref<number>): () => number {
  return () => count.value;
}

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/composable-outside-setup

Uma função de composição é chamada fora de setup.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

A preocupação é este composable dependente do ciclo de vida, e não uma proibição geral de funções utilitárias comuns ou de todas as chamadas da Composition API fora de setup. Este contrato não tem produtor atual.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

App.vue

<script setup lang="ts">
import { useTitle } from './use-title';
const title = useTitle();
</script>

<template>
<h1>{{ title }}</h1>
</template>

Incorreto

Importar use-title.ts registra onMounted antes que o setup de um componente esteja ativo. Chamar sua função exportada depois apenas retorna essa ref no escopo do módulo; isso não corrige a falta de vínculo com o ciclo de vida.

use-title.ts

import { onMounted, ref } from 'vue';
const title = ref('Before mount');
onMounted(() => { title.value = 'Mounted'; });
export function useTitle() { return title; }

Correto

Tanto a criação do estado quanto o registro do gancho passam para useTitle, que App chama de forma síncrona dentro de setup. O gancho de montagem agora pertence a essa instância de App.

use-title.ts

import { onMounted, ref } from 'vue';
export function useTitle() {
  const title = ref('Before mount');
  onMounted(() => { title.value = 'Mounted'; });
  return title;
}

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/computed-side-effects

Uma função de leitura computada escreve no estado ou produz outro efeito colateral.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

App.vue

<script setup lang="ts">
import { useDouble } from './use-double';
const { count, doubled, lastCalculated } = useDouble();
</script>

<template>
<button @click="count++">Increment {{ count }}</button><p>{{ doubled }} / {{ lastCalculated }}</p>
</template>

Incorreto

Avaliar doubled escreve em lastCalculated, de modo que ler um valor computado também altera um estado separado. Isso vincula o efeito colateral ao momento em que a função de leitura de avaliação adiada é lida.

use-double.ts

import { computed, ref } from 'vue';
export function useDouble() {
  const count = ref(0);
  const lastCalculated = ref(0);
  const doubled = computed(() => {
    const next = count.value * 2;
    lastCalculated.value = next;
    return next;
  });
  return { count, doubled, lastCalculated };
}

Correto

A função de leitura apenas retorna o número derivado. Um observador separado fica responsável pela escrita em lastCalculated quando count muda, incluindo seu valor inicial.

use-double.ts

import { computed, ref, watch } from 'vue';
export function useDouble() {
  const count = ref(0);
  const lastCalculated = ref(0);
  const doubled = computed(() => count.value * 2);
  watch(count, next => { lastCalculated.value = next * 2; }, { immediate: true });
  return { count, doubled, lastCalculated };
}

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/deep-import

Uma cadeia de importações é mais profunda do que o projeto permite.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Esta é uma política de organização de projeto escolhida explicitamente; não cria um limite de profundidade ou uma opção suportados. Não há um produtor atual de diagnósticos para este contrato.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

App.vue

<script setup lang="ts">
import { label } from './entry';
</script>

<template>
<p>{{ label }}</p>
</template>

value.ts

export const label = 'Notice';

level-one.ts

export { label } from './level-two';

level-two.ts

export { label } from './level-three';

level-three.ts

export { label } from './value';

public-api.ts

export { label } from './value';

Incorreto

O ponto de entrada encaminha um valor simples por level-one, level-two e level-three, criando uma cadeia de importações desnecessariamente profunda para um projeto que deseja um limite público raso.

entry.ts

export { label } from './level-one';

Correto

O ponto de entrada usa public-api.ts, que reexporta o valor diretamente. O consumidor mantém o mesmo nome importado, enquanto a cadeia fica mais curta.

entry.ts

export { label } from './public-api';

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/destructuring-breaks-reactivity

Desestruturar um objeto reativo copia os campos e perde o rastreamento.

Severidade padrão: error
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/destructuring-breaks-reactivity": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./UserPage.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

A desestruturação comum do objeto props copia seu valor atual de item; isso é diferente da desestruturação direta de defineProps() no Vue 3.5.

UserPage.vue

<script setup lang="ts">
import { reactive } from "vue";
import UserSummary from "./UserSummary.vue";

const user = reactive({ name: "Ada" });
</script>

<template>
  <UserSummary :item="user" />
</template>

UserSummary.vue

<script setup lang="ts">
const props = defineProps<{ item: { name: string } }>();
const { item } = props;
</script>

Correto

toRef(props, "item") preserva a conexão com a propriedade em props.

UserPage.vue

<script setup lang="ts">
import { reactive } from "vue";
import UserSummary from "./UserSummary.vue";

const user = reactive({ name: "Ada" });
</script>

<template>
  <UserSummary :item="user" />
</template>

UserSummary.vue

<script setup lang="ts">
import { toRef } from "vue";

const props = defineProps<{ item: { name: string } }>();
const item = toRef(props, "item");
</script>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Produtor

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/di-outside-setup

provide ou inject é chamado fora de setup.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Este exemplo usa provide/inject de componente. A injeção por app.provide e pelo app.runWithContext suportado são outras formas válidas de associação a um responsável, não proibidas por este cenário. Nenhum produtor atual emite este código de contrato.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

theme.ts

import { inject, provide } from 'vue';
import type { InjectionKey } from 'vue';
export const ThemeKey: InjectionKey<string> = Symbol('theme');
export function provideTheme() { provide(ThemeKey, 'dark'); }
export function useTheme() { return inject(ThemeKey, 'light'); }

ThemedText.vue

<script setup lang="ts">
import { useTheme } from './theme';
const theme = useTheme();
</script>

<template>
<p>{{ theme }}</p>
</template>

Incorreto

main.ts chama o provide de componente sem uma instância de componente ativa. Por isso, o inject do filho não pode receber o valor pretendido do ancestral e usa light.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
import { provideTheme } from './theme';
provideTheme();
createApp(App).mount('#app');

App.vue

<script setup lang="ts">
import ThemedText from './ThemedText.vue';
</script>

<template>
<ThemedText />
</template>

Correto

App chama o provedor em seu setup antes de renderizar o filho. O filho agora herda o valor dark de seu componente ancestral.

App.vue

<script setup lang="ts">
import ThemedText from './ThemedText.vue';
import { provideTheme } from './theme';
provideTheme();
</script>

<template>
<ThemedText />
</template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/dom-access-without-next-tick

O DOM é lido antes de o Vue aplicar a atualização.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

read-label.ts

export function readLabel(node: HTMLElement | null): string {
  return node?.textContent ?? '';
}

Incorreto

O manipulador de clique incrementa count e imediatamente lê o parágrafo renderizado, antes de o Vue aplicar a atualização agendada do DOM. sampled pode conter a contagem anterior.

App.vue

<script setup lang="ts">
import { ref } from 'vue';
import { readLabel } from './read-label';
const count = ref(0);
const label = ref<HTMLElement | null>(null);
const sampled = ref('');
function increment() {
  count.value++;
  sampled.value = readLabel(label.value);
}
</script>

<template>
<button @click="increment">Increment</button><p ref="label">{{ count }}</p><p>DOM sample: {{ sampled }}</p>
</template>

Correto

Aguardar nextTick() após a escrita no estado permite que o Vue atualize o parágrafo antes de readLabel capturar seu texto.

App.vue

<script setup lang="ts">
import { nextTick, ref } from 'vue';
import { readLabel } from './read-label';
const count = ref(0);
const label = ref<HTMLElement | null>(null);
const sampled = ref('');
async function increment() {
  count.value++;
  await nextTick();
  sampled.value = readLabel(label.value);
}
</script>

<template>
<button @click="increment">Increment</button><p ref="label">{{ count }}</p><p>DOM sample: {{ sampled }}</p>
</template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/duplicate-id

O mesmo id de elemento é usado em mais de um componente.

Severidade padrão: warning
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/duplicate-id": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./CheckoutForm.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

Os componentes alcançáveis de entrega e cobrança renderizam id="postal-code", de modo que seus rótulos compartilham um destino ambíguo no documento.

CheckoutForm.vue

<script setup lang="ts">
import BillingAddress from "./BillingAddress.vue";
import ShippingAddress from "./ShippingAddress.vue";
</script>

<template>
  <ShippingAddress />
  <BillingAddress />
</template>

ShippingAddress.vue

<template>
  <label for="postal-code">Shipping postal code</label>
  <input id="postal-code" />
</template>

BillingAddress.vue

<template>
  <label for="postal-code">Billing postal code</label>
  <input id="postal-code" />
</template>

Correto

Cada componente chama useId() e vincula seu próprio valor ao rótulo e ao campo de entrada, preservando a associação sem repetir um ID literal.

CheckoutForm.vue

<script setup lang="ts">
import BillingAddress from "./BillingAddress.vue";
import ShippingAddress from "./ShippingAddress.vue";
</script>

<template>
  <ShippingAddress />
  <BillingAddress />
</template>

ShippingAddress.vue

<script setup lang="ts">
import { useId } from "vue";

const postalCodeId = useId();
</script>

<template>
  <label :for="postalCodeId">Shipping postal code</label>
  <input :id="postalCodeId" />
</template>

BillingAddress.vue

<script setup lang="ts">
import { useId } from "vue";

const postalCodeId = useId();
</script>

<template>
  <label :for="postalCodeId">Billing postal code</label>
  <input :id="postalCodeId" />
</template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/event-listener-leak

Um ouvinte de eventos é registrado e nunca removido.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

App.vue

<script setup lang="ts">
import { useWidth } from './use-width';
const width = useWidth();
</script>

<template>
<p>{{ width }}</p>
</template>

Incorreto

A montagem adiciona um ouvinte de redimensionamento de window que captura a ref de largura do componente, mas a desmontagem nunca o remove. Montagens repetidas podem reter ouvintes e estado sem uso.

use-width.ts

import { onMounted, ref } from 'vue';
export function useWidth() {
  const width = ref(0);
  const resize = () => { width.value = window.innerWidth; };
  onMounted(() => { resize(); window.addEventListener('resize', resize); });
  return width;
}

Correto

onUnmounted remove exatamente a mesma função resize registrada na montagem, encerrando o ciclo de vida do ouvinte externo dessa instância.

use-width.ts

import { onMounted, onUnmounted, ref } from 'vue';
export function useWidth() {
  const width = ref(0);
  const resize = () => { width.value = window.innerWidth; };
  onMounted(() => { resize(); window.addEventListener('resize', resize); });
  onUnmounted(() => { window.removeEventListener('resize', resize); });
  return width;
}

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/event-modifier

Um ouvinte de eventos usa um modificador que o evento emitido não suporta.

Severidade padrão: info
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

.stop pressupõe o método de propagação de um evento nativo no evento personalizado save do filho, cujo conteúdo não precisa ser um Event do DOM.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child @save.stop="() => {}" /></template>

Child.vue

<script setup lang="ts">
const emit = defineEmits<{ save: [] }>();
emit("save");
</script>
<template><p>Content</p></template>

Correto

Remova .stop do ouvinte do evento personalizado; trate a propagação nativa no ouvinte real do DOM quando necessário.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child @save="() => {}" /></template>

Child.vue

<script setup lang="ts">
const emit = defineEmits<{ save: [] }>();
emit("save");
</script>
<template><p>Content</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/hydration-risk

Este código de diagnóstico agrupa várias ocorrências de reatividade, incluindo uma prop copiada para uma ref. Ele não implica que toda expressão Date.now() seja detectada pela passagem de análise entre arquivos.

Severidade padrão: error
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/hydration-risk": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O filho inicializa ref(props.count) uma vez, de modo que sua contagem local deixa de acompanhar mudanças posteriores na prop do pai. Este é o produtor atual prop-to-ref, não um exemplo geral de SSR não determinístico.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child :count="0" /></template>

Child.vue

<script setup lang="ts">
import { ref } from "vue";
const props = defineProps<{ count: number }>();
const count = ref(props.count);
</script>
<template><p>{{ count }}</p></template>

Correto

toRef(props, "count") aponta para a prop em vez de copiar seu valor inicial para um estado independente.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child :count="0" /></template>

Child.vue

<script setup lang="ts">
import { toRef } from "vue";
const props = defineProps<{ count: number }>();
const count = toRef(props, "count");
</script>
<template><p>{{ count }}</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/inherit-attrs-unused

inheritAttrs: false está definido e o componente nunca lê os atributos.

Severidade padrão: warning
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O filho define inheritAttrs: false, mas nunca encaminha o atributo class="notice" do pai.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child class="notice" /></template>

Child.vue

<script setup lang="ts">
defineOptions({ inheritAttrs: false });
</script>
<template><main>Content</main></template>

Correto

Mantenha o controle explícito da herança e vincule $attrs ao destino <main> pretendido.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child class="notice" /></template>

Child.vue

<script setup lang="ts">
defineOptions({ inheritAttrs: false });
</script>
<template><main v-bind="$attrs">Content</main></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/inject-without-symbol

inject usa uma chave comum em vez de um símbolo InjectionKey.

Severidade padrão: warning
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/inject-without-symbol": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./ThemeProvider.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

keys/theme.ts

import type { InjectionKey, Ref } from "vue";
export interface Theme { color: string; }
export const ThemeKey: InjectionKey<Ref<Theme>> = Symbol("theme");

Incorreto

O consumidor injeta a chave de string sem tipo "theme", que não oferece uma identidade de símbolo compartilhada com o provedor.

ThemeProvider.vue

<script setup lang="ts">
import { provide, ref } from "vue";
import ThemeLabel from "./ThemeLabel.vue";

const theme = ref({ color: "blue" });
provide("theme", theme);
</script>

<template>
  <ThemeLabel />
</template>

ThemeLabel.vue

<script setup lang="ts">
import { inject } from "vue";

const theme = inject("theme");
</script>

Correto

O consumidor e o provedor importam o mesmo ThemeKey em vez de duplicar nomes de string.

ThemeProvider.vue

<script setup lang="ts">
import { provide, ref } from "vue";
import ThemeLabel from "./ThemeLabel.vue";
import { ThemeKey } from "./keys/theme";

const theme = ref({ color: "blue" });
provide(ThemeKey, theme);
</script>

<template>
  <ThemeLabel />
</template>

ThemeLabel.vue

<script setup lang="ts">
import { inject } from "vue";
import { ThemeKey } from "./keys/theme";

const theme = inject(ThemeKey);
</script>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/injected-async-mutation-race

Um valor injetado é alterado por uma tarefa assíncrona sujeita a uma condição de corrida.

Severidade padrão: error
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/injected-async-mutation-race": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./StoreProvider.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

api.ts

export async function loadCount(query: string, options?: { signal?: AbortSignal }): Promise<number> {
  const response = await fetch(`/count?q=${encodeURIComponent(query)}`, options);
  return Number(await response.text());
}

CountSummary.vue

<script setup lang="ts">
import { inject } from "vue";
import { StoreKey } from "./keys/store";
const store = inject(StoreKey)!;
</script>
<template><p>{{ store.count }}</p></template>

Incorreto

CountLoader.vue escreve um resultado aguardado diretamente no armazenamento injetado compartilhado com CountSummary.vue, permitindo que um trabalho desatualizado afete os dois consumidores.

keys/store.ts

import type { InjectionKey } from "vue";

export interface Store {
  count: number;
}

export const StoreKey: InjectionKey<Store> = Symbol("store");

StoreProvider.vue

<script setup lang="ts">
import { provide, reactive } from "vue";
import CountLoader from "./CountLoader.vue";
import CountSummary from "./CountSummary.vue";
import { StoreKey, type Store } from "./keys/store";

const store = reactive<Store>({ count: 0 });
provide(StoreKey, store);
</script>

<template>
  <CountLoader />
  <CountSummary />
</template>

CountLoader.vue

<script setup lang="ts">
import { loadCount } from "./api";
import { inject, ref, watch } from "vue";
import { StoreKey } from "./keys/store";

const store = inject(StoreKey)!;
const query = ref("");

watch(query, async (value) => {
  store.count = await loadCount(value);
});
</script>

Correto

O carregador cancela trabalhos invalidados e emite apenas um resultado ativo. O provedor fica responsável pela alteração do armazenamento por meio de applyLoadedCount.

keys/store.ts

import type { InjectionKey } from "vue";

export interface Store {
  count: number;
}

export const StoreKey: InjectionKey<Store> = Symbol("store");

StoreProvider.vue

<script setup lang="ts">
import { provide, reactive } from "vue";
import CountLoader from "./CountLoader.vue";
import CountSummary from "./CountSummary.vue";
import { StoreKey, type Store } from "./keys/store";

const store = reactive<Store>({ count: 0 });
provide(StoreKey, store);

function applyLoadedCount(count: number) {
  store.count = count;
}
</script>

<template>
  <CountLoader @loaded="applyLoadedCount" />
  <CountSummary />
</template>

CountLoader.vue

<script setup lang="ts">
import { loadCount } from "./api";
import { ref, watch } from "vue";

const emit = defineEmits<{ loaded: [count: number] }>();
const query = ref("");

watch(query, async (value, _oldValue, onCleanup) => {
  const controller = new AbortController();
  let active = true;

  onCleanup(() => {
    active = false;
    controller.abort();
  });

  const count = await loadCount(value, { signal: controller.signal });
  if (active) emit("loaded", count);
});
</script>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/lifecycle-outside-setup

Um gancho de ciclo de vida é registrado fora de setup.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

install-title.ts

import { onMounted } from 'vue';
export function installTitle() {
  onMounted(() => { document.title = 'Mounted application'; });
}

Incorreto

O ponto de entrada chama installTitle() antes de montar uma aplicação, de modo que onMounted é registrado sem um contexto de setup de componente ativo.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
import { installTitle } from './install-title';
installTitle();
createApp(App).mount('#app');

App.vue

<script setup lang="ts">

</script>

<template>
<p>Application</p>
</template>

Correto

Chamar o mesmo auxiliar de forma síncrona no setup de App vincula a função de retorno do ciclo de vida à montagem dessa instância.

App.vue

<script setup lang="ts">
import { installTitle } from './install-title';
installTitle();
</script>

<template>
<p>Application</p>
</template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/lifecycle-without-cleanup

Um gancho de ciclo de vida inicia um trabalho e nunca faz sua limpeza.

Severidade padrão: warning
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

A montagem registra um ouvinte de redimensionamento de window, mas a desmontagem nunca remove a mesma função de retorno.

App.vue

<script setup lang="ts">
import { onMounted } from "vue";
const resize = () => {};
onMounted(() => { window.addEventListener("resize", resize); });
</script>
<template><p>Content</p></template>

Correto

onUnmounted remove o ouvinte com o mesmo nome de evento e a mesma identidade de função usados por addEventListener.

App.vue

<script setup lang="ts">
import { onMounted, onUnmounted } from "vue";
const resize = () => {};
onMounted(() => { window.addEventListener("resize", resize); });
onUnmounted(() => { window.removeEventListener("resize", resize); });
</script>
<template><p>Content</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/missing-required-prop

Uma prop obrigatória não é passada.

Severidade padrão: error
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O pai renderiza <Child /> sem a prop obrigatória title: string do filho.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child /></template>

Child.vue

<script setup lang="ts">
const props = defineProps<{ title: string }>();
</script>
<template><p>{{ props.title }}</p></template>

Correto

title="Hello" fornece a prop obrigatória declarada pelo filho resolvido.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child title="Hello" /></template>

Child.vue

<script setup lang="ts">
const props = defineProps<{ title: string }>();
</script>
<template><p>{{ props.title }}</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/missing-suspense

Uma dependência assíncrona é usada fora de um limite Suspense.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

AsyncCard.vue

<script setup lang="ts">
const message = await Promise.resolve('Ready');
</script>

<template>
<p>{{ message }}</p>
</template>

Incorreto

AsyncCard tem await no nível superior, tornando seu setup assíncrono, mas App o renderiza sem um limite Suspense para coordenar essa dependência.

App.vue

<script setup lang="ts">
import AsyncCard from './AsyncCard.vue';
</script>

<template>
<AsyncCard />
</template>

Correto

App envolve o filho assíncrono em Suspense e fornece conteúdo alternativo de carregamento até que o setup do filho seja resolvido.

App.vue

<script setup lang="ts">
import { Suspense } from 'vue';
import AsyncCard from './AsyncCard.vue';
</script>

<template>
<Suspense><AsyncCard /><template #fallback><p>Loading…</p></template></Suspense>
</template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/module-scope-reactive

O estado reativo é criado no escopo do módulo e compartilhado por todos os chamadores.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Estado reativo no escopo de módulo é válido para stores intencionais da aplicação. Este exemplo pressupõe isolamento por componente ou requisição; o contrato publicado atualmente não tem produtor.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

App.vue

<script setup lang="ts">
import Counter from './Counter.vue';
</script>

<template>
<Counter /><Counter />
</template>

Counter.vue

<script setup lang="ts">
import { createCounter } from './counter';
const { count } = createCounter();
</script>

<template>
<button @click="count++">{{ count }}</button>
</template>

Incorreto

O módulo inicializa count uma vez, e as duas instâncias de Counter recebem a mesma ref. Clicar em uma altera os dois contadores, embora este exemplo pretenda manter um estado independente por instância.

counter.ts

import { ref } from 'vue';
const count = ref(0);
export function createCounter() { return { count }; }

Correto

Criar a ref dentro de createCounter dá a cada chamada síncrona de setup um objeto de estado separado, de modo que cada botão possui seu contador.

counter.ts

import { ref } from 'vue';
export function createCounter() {
  const count = ref(0);
  return { count };
}

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/multi-root-attrs

Um componente com múltiplas raízes recebe atributos e não tem onde colocá-los.

Severidade padrão: warning
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O filho tem raízes <main> e <aside>, de modo que o Vue não tem uma raiz única que possa receber automaticamente a classe do pai.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child class="notice" /></template>

Child.vue

<template><main>Content</main><aside>Help</aside></template>

Correto

Encaminhe $attrs explicitamente para <main>, mantendo a segunda raiz.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child class="notice" /></template>

Child.vue

<template><main v-bind="$attrs">Content</main><aside>Help</aside></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/mutated-after-escape

Um objeto reativo é alterado depois de escapar de seu proprietário.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Esta é uma política explícita de propriedade de histórico imutável, e não uma proibição geral de passar objetos reativos ou de modificá-los depois. Nenhum produtor atual emite este contrato.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

archive.ts

export interface Profile { name: string }
const records: Readonly<Profile>[] = [];
export function publish(profile: Readonly<Profile>): void { records.push(profile); }
export function latestName(): string { return records.at(-1)?.name ?? ''; }

App.vue

<script setup lang="ts">
import { publishProfile } from './profile';
import { latestName } from './archive';
publishProfile();
const archivedName = latestName();
</script>

<template>
<p>Archived name: {{ archivedName }}</p>
</template>

Incorreto

O arquivo retém o mesmo objeto passado a publish. Seu proprietário então altera o nome, mudando retroativamente o registro supostamente histórico para Grace. O parâmetro Readonly do TypeScript não copia o objeto.

profile.ts

import { reactive } from 'vue';
import { publish } from './archive';
export function publishProfile(): void {
  const profile = reactive({ name: 'Ada' });
  publish(profile);
  profile.name = 'Grace';
}

Correto

Publicar uma cópia simples separa o registro arquivado de Ada das edições posteriores do perfil reativo. A política de instantâneos do arquivo agora é mantida.

profile.ts

import { reactive } from 'vue';
import { publish } from './archive';
export function publishProfile(): void {
  const profile = reactive({ name: 'Ada' });
  publish({ ...profile });
  profile.name = 'Grace';
}

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/non-reactive-provide

Um valor fornecido não é reativo, portanto os descendentes não verão atualizações.

Severidade padrão: dependente do contexto
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/non-reactive-provide": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./ThemeProvider.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

ThemeProvider.vue fornece um objeto simples. Alterar os campos desse objeto não dá ao consumidor que o injeta uma dependência reativa do Vue.

keys/theme.ts

export const ThemeKey = Symbol("theme");

ThemeProvider.vue

<script setup lang="ts">
import { provide } from "vue";
import ThemeLabel from "./ThemeLabel.vue";
import { ThemeKey } from "./keys/theme";

const theme = { color: "blue" };
provide(ThemeKey, theme);
</script>

<template>
  <ThemeLabel />
</template>

ThemeLabel.vue

<script setup lang="ts">
import { inject } from "vue";
import { ThemeKey } from "./keys/theme";

const theme = inject(ThemeKey);
</script>

Correto

O provedor envolve o tema em ref; a mesma referência injetada pode rastrear mudanças posteriores.

keys/theme.ts

export const ThemeKey = Symbol("theme");

ThemeProvider.vue

<script setup lang="ts">
import { provide, ref } from "vue";
import ThemeLabel from "./ThemeLabel.vue";
import { ThemeKey } from "./keys/theme";

const theme = ref({ color: "blue" });
provide(ThemeKey, theme);
</script>

<template>
  <ThemeLabel />
</template>

ThemeLabel.vue

<script setup lang="ts">
import { inject } from "vue";
import { ThemeKey } from "./keys/theme";

const theme = inject(ThemeKey);
</script>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/non-unique-id

Um id de elemento dentro de um laço não é único por item.

Severidade padrão: error
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/non-unique-id": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./ResultsList.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

Cada iteração de v-for renderiza o mesmo ID literal result-title; a chave do laço não torna os IDs do DOM únicos.

ResultsList.vue

<script setup lang="ts">
const results = [{ id: "first", title: "First result" }, { id: "second", title: "Second result" }];
</script>
<template>
  <article v-for="result in results" :key="result.id">
    <h2 id="result-title">{{ result.title }}</h2>
  </article>
</template>

Correto

O ID do título inclui o ID estável do resultado, produzindo um identificador distinto no documento para cada item.

ResultsList.vue

<script setup lang="ts">
const results = [{ id: "first", title: "First result" }, { id: "second", title: "Second result" }];
</script>
<template>
  <article v-for="result in results" :key="result.id">
    <h2 :id="`result-${result.id}-title`">{{ result.title }}</h2>
  </article>
</template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/object-identity-comparison

Um objeto reativo é comparado por identidade, que muda ao remover os invólucros.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

O exemplo pressupõe que os IDs identifiquem os registros de forma única. Comparar duas referências ao mesmo proxy reativo continua válido; este contrato não tem produtor atual.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

user.ts

import { reactive } from 'vue';
export function makeUser() {
  const raw = { id: 7, name: 'Ada' };
  return { raw, proxy: reactive(raw) };
}

Incorreto

proxy === raw compara a identidade do invólucro, portanto é falso mesmo que ambos representem o mesmo registro de usuário. A aplicação pretendia comparar a identidade do registro, não a do invólucro do objeto.

App.vue

<script setup lang="ts">
import { makeUser } from './user';
const { raw, proxy } = makeUser();
const sameRecord = proxy === raw;
</script>

<template>
<p>Same record: {{ sameRecord }}</p>
</template>

Correto

Comparar o id estável do registro responde à pergunta pretendida sem depender de o objeto ser bruto ou estar envolvido por um proxy.

App.vue

<script setup lang="ts">
import { makeUser } from './user';
const { raw, proxy } = makeUser();
const sameRecord = proxy.id === raw.id;
</script>

<template>
<p>Same record: {{ sameRecord }}</p>
</template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/pinia-getter

Uma função de leitura do Pinia é lida sem storeToRefs, portanto não permanecerá reativa.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Pinia deve estar instalado, e main.ts instala seu plugin antes da montagem. Ler store.doubled diretamente dentro de um cálculo rastreado ou de um template é válido; o defeito aqui é capturar um valor instantâneo comum. Este contrato atualmente não tem produtor.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
createApp(App).use(createPinia()).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

counter-store.ts

import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: { doubled: state => state.count * 2 },
});

Incorreto

const doubled = store.doubled copia o número atual da função de leitura durante setup. O número copiado não acompanha atualizações posteriores de store.count.

App.vue

<script setup lang="ts">
import { useCounterStore } from './counter-store';
const store = useCounterStore();
const doubled = store.doubled;
</script>

<template>
<button @click="store.count++">{{ store.count }}</button><p>{{ doubled }}</p>
</template>

Correto

storeToRefs(store) fornece uma ref reativa da função de leitura que pode ser desestruturada e desembrulhada pelo template, mantendo a conexão com o armazenamento.

App.vue

<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { useCounterStore } from './counter-store';
const store = useCounterStore();
const { doubled } = storeToRefs(store);
</script>

<template>
<button @click="store.count++">{{ store.count }}</button><p>{{ doubled }}</p>
</template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/prop-type-mismatch

O valor de uma prop passada não corresponde ao tipo declarado.

Severidade padrão: error
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O pai passa a expressão numérica 42 para a prop title: string do filho resolvido.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child :title="42" /></template>

Child.vue

<script setup lang="ts">
const props = defineProps<{ title: string }>();
</script>
<template><p>{{ props.title }}</p></template>

Correto

O literal title="Hello" fornece uma string que corresponde à declaração do filho.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child title="Hello" /></template>

Child.vue

<script setup lang="ts">
const props = defineProps<{ title: string }>();
</script>
<template><p>{{ props.title }}</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/provide-inject-type

Um valor fornecido e sua injeção não têm o mesmo tipo.

Severidade padrão: warning
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/provide-inject-type": "warn" },
    },
  },
});
vp run lint

Esta verificação compara anotações explícitas de tipos do provedor e do consumidor, e não tipos inferidos de valores literais. Mantenha a anotação as string do provedor neste exemplo.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O provedor anota explicitamente title como string, enquanto o descendente solicita inject<number> para a mesma chave.

App.vue

<script setup lang="ts">
import { provide } from "vue";
import Child from "./Child.vue";
provide("title", "Hello" as string);
</script>
<template><Child /></template>

Child.vue

<script setup lang="ts">
import { inject } from "vue";
const title = inject<number>("title");
</script>
<template><p>Content</p></template>

Correto

O inject<string> explícito do consumidor concorda com a anotação do provedor. Mantenha as string: este produtor compara anotações explícitas, não tipos literais inferidos.

App.vue

<script setup lang="ts">
import { provide } from "vue";
import Child from "./Child.vue";
provide("title", "Hello" as string);
</script>
<template><Child /></template>

Child.vue

<script setup lang="ts">
import { inject } from "vue";
const title = inject<string>("title");
</script>
<template><p>Content</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/provide-without-symbol

provide usa uma chave comum em vez de um símbolo InjectionKey.

Severidade padrão: warning
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/provide-without-symbol": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./ThemeProvider.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

Os dois componentes usam a string "theme"; funcionalidades sem relação podem reutilizar essa chave acidentalmente.

ThemeProvider.vue

<script setup lang="ts">
import { provide, ref } from "vue";
import ThemeLabel from "./ThemeLabel.vue";

const theme = ref({ color: "blue" });
provide("theme", theme);
</script>

<template>
  <ThemeLabel />
</template>

ThemeLabel.vue

<script setup lang="ts">
import { inject } from "vue";

const theme = inject("theme");
</script>

Correto

Exporte um único símbolo tipado ThemeKey e importe esse mesmo valor nos pontos de provide e inject. Criar símbolos separados com a mesma descrição não os conectaria.

ThemeProvider.vue

<script setup lang="ts">
import { provide, ref } from "vue";
import ThemeLabel from "./ThemeLabel.vue";
import { ThemeKey, type Theme } from "./keys/theme";

const theme = ref<Theme>({ color: "blue" });
provide(ThemeKey, theme);
</script>

<template>
  <ThemeLabel />
</template>

ThemeLabel.vue

<script setup lang="ts">
import { inject } from "vue";
import { ThemeKey } from "./keys/theme";

const theme = inject(ThemeKey);
</script>

keys/theme.ts

import type { InjectionKey, Ref } from "vue";

export interface Theme {
  color: string;
}

export const ThemeKey: InjectionKey<Ref<Theme>> = Symbol("theme");

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/reactive-export

O estado reativo é exportado pelo módulo.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Stores da aplicação compartilhados intencionalmente podem exportar estado reativo. Este cenário exige estado isolado e não afirma que toda exportação reativa seja inválida. Nenhum produtor atual emite este contrato.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

Incorreto

O módulo exporta um único objeto reativo inicializado, de modo que todo importador recebe a mesma contagem. Em um módulo SSR compartilhado entre requisições, isso impede o isolamento de estado por instância ou requisição pretendido no exemplo.

state.ts

import { reactive } from 'vue';
export const state = reactive({ count: 0 });

App.vue

<script setup lang="ts">
import { state } from './state';
</script>

<template>
<button @click="state.count++">{{ state.count }}</button>
</template>

Correto

O módulo exporta uma fábrica, e App a chama dentro de setup. Cada instância obtém uma nova contagem reativa em vez da instância única exportada.

state.ts

import { reactive } from 'vue';
export function createState() { return reactive({ count: 0 }); }

App.vue

<script setup lang="ts">
import { createState } from './state';
const state = createState();
</script>

<template>
<button @click="state.count++">{{ state.count }}</button>
</template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/reactivity-outside-setup

Uma API reativa é chamada fora de setup.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

O Vue permite ref/reactive/computed fora do setup de componente. O risco aqui é a propriedade ou o compartilhamento indesejados sob uma política explícita de isolamento de instâncias, e não um uso inválido da API. Este contrato não tem produtor atual.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

App.vue

<script setup lang="ts">
import Counter from './Counter.vue';
</script>

<template>
<Counter /><Counter />
</template>

Counter.vue

<script setup lang="ts">
import { useCounter } from './use-counter';
const { count, doubled } = useCounter();
</script>

<template>
<button @click="count++">{{ count }}</button><p>{{ doubled }}</p>
</template>

Incorreto

As duas APIs reativas são executadas durante o carregamento do módulo. As duas instâncias de Counter, portanto, compartilham uma ref e um valor computado, apesar da intenção de manter contadores independentes.

use-counter.ts

import { computed, ref } from 'vue';
const count = ref(0);
const doubled = computed(() => count.value * 2);
export function useCounter() { return { count, doubled }; }

Correto

useCounter cria a ref e o valor computado de forma síncrona dentro de cada chamada de setup de componente, dando a cada componente visual seu próprio estado e sua própria derivação rastreada.

use-counter.ts

import { computed, ref } from 'vue';
export function useCounter() {
  const count = ref(0);
  const doubled = computed(() => count.value * 2);
  return { count, doubled };
}

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/reassignment-breaks-reactivity

Reatribuir uma variável reativa a substitui por um valor simples.

Severidade padrão: error
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/reassignment-breaks-reactivity": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./UserPage.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O filho cria uma ref de prop e depois sobrescreve a variável com props.user, descartando a conexão dessa ref.

UserPage.vue

<script setup lang="ts">
import { reactive } from "vue";
import UserSummary from "./UserSummary.vue";

const user = reactive({ name: "Ada" });
</script>

<template>
  <UserSummary :user="user" />
</template>

UserSummary.vue

<script setup lang="ts">
import { toRef } from "vue";

const props = defineProps<{ user: { name: string } }>();
let user = toRef(props, "user");

user = props.user;
</script>

Correto

Mantenha o toRef em uma variável const e remova a reatribuição que o substitui.

UserPage.vue

<script setup lang="ts">
import { reactive } from "vue";
import UserSummary from "./UserSummary.vue";

const user = reactive({ name: "Ada" });
</script>

<template>
  <UserSummary :user="user" />
</template>

UserSummary.vue

<script setup lang="ts">
import { toRef } from "vue";

const props = defineProps<{ user: { name: string } }>();
const user = toRef(props, "user");
</script>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/reference-escapes-scope

Uma referência reativa escapa do escopo responsável por seu ciclo de vida.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Refs podem legitimamente ser retornadas por composables ou compartilhadas entre escopos. Este exemplo exige explicitamente um cache de valores instantâneos; não afirma que a desmontagem invalide uma ref. Nenhum produtor atual emite este contrato.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

saved.ts

import type { Ref } from 'vue';
let saved: Ref<number> | number | undefined;
export function remember(value: Ref<number> | number): void { saved = value; }
export function remembered(): Ref<number> | number | undefined { return saved; }

Incorreto

O cache no nível do processo retém a ref ativa de contagem do componente. Ele pode manter o estado dessa instância alcançável após a desmontagem e observar edições posteriores, embora esse cache deva armazenar um instantâneo.

App.vue

<script setup lang="ts">
import { ref } from 'vue';
import { remember } from './saved';
const count = ref(0);
remember(count);
</script>

<template>
<button @click="count++">{{ count }}</button>
</template>

Correto

O cache recebe o número simples atual, de modo que mantém um instantâneo sem reter a ref pertencente ao componente.

App.vue

<script setup lang="ts">
import { ref } from 'vue';
import { remember } from './saved';
const count = ref(0);
remember(count.value);
</script>

<template>
<button @click="count++">{{ count }}</button>
</template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/setup-context-violation

O contexto de setup é usado de uma forma que o Vue não permite.

Severidade padrão: dependente do contexto
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

ref(0) é criada no escopo do módulo de um script comum, fora do contexto de setup por instância representado por este cenário do analisador.

App.vue

<script lang="ts">
import { ref } from "vue";
const count = ref(0);
export default {};
</script>
<template><p>Count</p></template>

Correto

Mova a variável para script setup, onde cada instância de componente possui sua contagem e o template pode lê-la.

App.vue

<script setup lang="ts">
import { ref } from "vue";
const count = ref(0);
</script>
<template><p>{{ count }}</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/shallow-deep-access

Uma propriedade profunda de um valor shallowReactive ou shallowRef é lida como se fosse rastreada.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

App.vue

<script setup lang="ts">
import { makeProfile } from './profile';
const profile = makeProfile();
</script>

<template>
<p>{{ profile.user.name }}</p><button @click="profile.user.name = 'Grace'">Rename</button>
</template>

Incorreto

shallowReactive rastreia a propriedade raiz user, mas deixa o objeto aninhado bruto. Alterar profile.user.name não notifica o template como uma mutação profunda rastreada.

profile.ts

import { shallowReactive } from 'vue';
export function makeProfile() { return shallowReactive({ user: { name: 'Ada' } }); }

Correto

O reactive profundo envolve o objeto de usuário aninhado, de modo que a mesma atribuição de nome pode acionar a atualização do nome exibido.

profile.ts

import { reactive } from 'vue';
export function makeProfile() { return reactive({ user: { name: 'Ada' } }); }

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/spread-breaks-reactivity

Espalhar um objeto reativo copia seus valores e perde o rastreamento.

Severidade padrão: error
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/spread-breaks-reactivity": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./UserPage.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

UserSummary.vue espalha props.user em um novo objeto, criando um instantâneo dos dados reativos recebidos.

UserPage.vue

<script setup lang="ts">
import { reactive } from "vue";
import UserSummary from "./UserSummary.vue";

const user = reactive({ name: "Ada", role: "admin" });
</script>

<template>
  <UserSummary :user="user" />
</template>

UserSummary.vue

<script setup lang="ts">
const props = defineProps<{ user: { name: string; role: string } }>();
const copiedUser = { ...props.user };
</script>

Correto

toRef(props, "user") mantém uma referência à prop recebida em vez de copiar seus campos.

UserPage.vue

<script setup lang="ts">
import { reactive } from "vue";
import UserSummary from "./UserSummary.vue";

const user = reactive({ name: "Ada", role: "admin" });
</script>

<template>
  <UserSummary :user="user" />
</template>

UserSummary.vue

<script setup lang="ts">
import { toRef } from "vue";

const props = defineProps<{ user: { name: string; role: string } }>();
const user = toRef(props, "user");
</script>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/suspense-no-fallback

<Suspense> não tem conteúdo alternativo.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Suspense sem conteúdo alternativo é uma sintaxe válida do Vue. Esta é uma convenção escolhida de interface de carregamento, e não um erro do compilador; o contrato publicado não tem produtor atual.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

AsyncCard.vue

<script setup lang="ts">
const message = await Promise.resolve('Ready');
</script>

<template>
<p>{{ message }}</p>
</template>

Incorreto

O limite Suspense tem um filho assíncrono, mas não tem conteúdo alternativo, deixando este exemplo sem conteúdo de carregamento durante o estado pendente.

App.vue

<script setup lang="ts">
import { Suspense } from 'vue';
import AsyncCard from './AsyncCard.vue';
</script>

<template>
<Suspense><AsyncCard /></Suspense>
</template>

Correto

O slot #fallback fornece um parágrafo explícito de carregamento até que o filho assíncrono seja resolvido.

App.vue

<script setup lang="ts">
import { Suspense } from 'vue';
import AsyncCard from './AsyncCard.vue';
</script>

<template>
<Suspense><AsyncCard /><template #fallback><p>Loading…</p></template></Suspense>
</template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/template-ref-timing

Uma ref de template é lida antes da montagem do componente.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

focus-input.ts

export function focusInput(input: HTMLInputElement | null): void { input?.focus(); }

Incorreto

Setup lê a ref de template antes da montagem, quando seu valor ainda é nulo. Por isso, a chamada opcional de foco não realiza nenhuma ação de foco.

App.vue

<script setup lang="ts">
import { ref } from 'vue';
import { focusInput } from './focus-input';
const input = ref<HTMLInputElement | null>(null);
focusInput(input.value);
</script>

<template>
<input ref="input" aria-label="Name" />
</template>

Correto

onMounted adia a leitura até que o Vue tenha atribuído o elemento de entrada à ref de template, permitindo que o auxiliar de foco atue sobre ele.

App.vue

<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { focusInput } from './focus-input';
const input = ref<HTMLInputElement | null>(null);
onMounted(() => { focusInput(input.value); });
</script>

<template>
<input ref="input" aria-label="Name" />
</template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/toraw-mutation

toRaw é usado e o objeto bruto é alterado em seguida.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

App.vue

<script setup lang="ts">
import { makeProfile, rename } from './profile';
const profile = makeProfile();
</script>

<template>
<p>{{ profile.name }}</p><button @click="rename(profile)">Rename</button>
</template>

Incorreto

rename obtém o objeto-alvo bruto e escreve em raw.name, contornando a função de escrita do proxy que notificaria o nome reativo exibido.

profile.ts

import { reactive, toRaw } from 'vue';
export function makeProfile() { return reactive({ name: 'Ada' }); }
export function rename(profile: { name: string }): void {
  const raw = toRaw(profile);
  raw.name = 'Grace';
}

Correto

Escrever em profile.name por meio do proxy reativo passado preserva a mesma renomeação e notifica seus dependentes.

profile.ts

import { reactive } from 'vue';
export function makeProfile() { return reactive({ name: 'Ada' }); }
export function rename(profile: { name: string }): void {
  profile.name = 'Grace';
}

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/uncaught-error

Um componente pode lançar um erro e nenhum limite de erro o captura.

Severidade padrão: info
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/uncaught-error": "warn" },
    },
  },
});
vp run lint

O produtor atual examina expressões de template, como JSON.parse(input). Não reporta uma instrução throw que exista apenas no bloco script.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O template do filho chama JSON.parse com uma entrada malformada, e o pai alcançável não tem um limite de captura de erros.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child /></template>

Child.vue

<script setup lang="ts">
const input = "{";
</script>
<template><button @click="JSON.parse(input)">Parse</button></template>

Correto

O pai registra onErrorCaptured ao redor desse filho. Retornar false interrompe a propagação; um limite de erro em produção também deve apresentar uma interface útil para recuperação.

App.vue

<script setup lang="ts">
import { onErrorCaptured } from "vue";
import Child from "./Child.vue";
onErrorCaptured(() => false);
</script>
<template><Child /></template>

Child.vue

<script setup lang="ts">
const input = "{";
</script>
<template><button @click="JSON.parse(input)">Parse</button></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/undeclared-emit

O componente emite um evento que não foi declarado.

Severidade padrão: error
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O filho chama emit("save"), mas seu contrato defineEmits declara apenas cancel.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child /></template>

Child.vue

<script setup lang="ts">
const emit = defineEmits<{ cancel: [] }>();
emit("save");
</script>
<template><p>Content</p></template>

Correto

Declare save com sua tupla vazia de argumentos para que o evento emitido concorde com o contrato do componente.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child /></template>

Child.vue

<script setup lang="ts">
const emit = defineEmits<{ save: [] }>();
emit("save");
</script>
<template><p>Content</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/undeclared-prop

Um pai passa uma prop que o filho não declara.

Severidade padrão: warning
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O pai passa typo, embora o filho resolvido declare apenas title. Essa convenção do analisador é distinta do comportamento geral de herança automática de atributos do Vue.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child title="Hello" :typo="true" /></template>

Child.vue

<script setup lang="ts">
const props = defineProps<{ title: string }>();
</script>
<template><p>{{ props.title }}</p></template>

Correto

Remova a vinculação não intencional de typo e mantenha a prop declarada title.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child title="Hello" /></template>

Child.vue

<script setup lang="ts">
const props = defineProps<{ title: string }>();
</script>
<template><p>{{ props.title }}</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/undefined-slot

Um pai preenche um slot que o filho não expõe.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

Card.vue

<script setup lang="ts">
defineSlots<{ header(): unknown }>();
</script>

<template>
<article><header><slot name="header" /></header></article>
</template>

Incorreto

App fornece um slot footer, mas Card declara e renderiza apenas header. O conteúdo de Notice fornecido não tem um ponto de renderização de slot correspondente nesse filho.

App.vue

<script setup lang="ts">
import Card from './Card.vue';
</script>

<template>
<Card><template #footer>Notice</template></Card>
</template>

Correto

App fornece header, correspondendo tanto à declaração tipada de slot do filho quanto ao seu ponto de renderização, de modo que Notice aparece ali.

App.vue

<script setup lang="ts">
import Card from './Card.vue';
</script>

<template>
<Card><template #header>Notice</template></Card>
</template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/unhandled-event

Um filho emite um evento que nenhum pai trata.

Severidade padrão: info
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

Child.vue emite save, mas seu invólucro imediato não escuta esse evento; eventos de componentes não se propagam automaticamente por invólucros.

App.vue

<script setup lang="ts">
import Wrapper from "./Wrapper.vue";
</script>
<template><Wrapper /></template>

Wrapper.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child /></template>

Child.vue

<script setup lang="ts">
const emit = defineEmits<{ save: [] }>();
emit("save");
</script>
<template><p>Content</p></template>

Correto

Wrapper.vue associa um ouvinte de save ao seu filho direto. A função de retorno vazia demonstra o tratamento para esta regra, não uma implementação completa de salvamento.

App.vue

<script setup lang="ts">
import Wrapper from "./Wrapper.vue";
</script>
<template><Wrapper /></template>

Wrapper.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child @save="() => {}" /></template>

Child.vue

<script setup lang="ts">
const emit = defineEmits<{ save: [] }>();
emit("save");
</script>
<template><p>Content</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/unmatched-inject

inject nomeia uma chave que nenhum ancestral fornece.

Severidade padrão: error / warning (com valor padrão)
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/unmatched-inject": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

ThemeLabel.vue injeta ThemeKey, mas seu ancestral alcançável App.vue nunca fornece essa chave.

keys/theme.ts

import type { InjectionKey, Ref } from "vue";

export interface Theme {
  color: string;
}

export const ThemeKey: InjectionKey<Ref<Theme>> = Symbol("theme");

App.vue

<script setup lang="ts">
import ThemeLabel from "./ThemeLabel.vue";
</script>

<template>
  <ThemeLabel />
</template>

ThemeLabel.vue

<script setup lang="ts">
import { inject } from "vue";
import { ThemeKey } from "./keys/theme";

const theme = inject(ThemeKey);
</script>

Correto

App.vue fornece um tema reativo usando o mesmo ThemeKey exportado, antes de renderizar o descendente que o injeta.

keys/theme.ts

import type { InjectionKey, Ref } from "vue";

export interface Theme {
  color: string;
}

export const ThemeKey: InjectionKey<Ref<Theme>> = Symbol("theme");

App.vue

<script setup lang="ts">
import { provide, ref } from "vue";
import ThemeLabel from "./ThemeLabel.vue";
import { ThemeKey, type Theme } from "./keys/theme";

const theme = ref<Theme>({ color: "blue" });
provide(ThemeKey, theme);
</script>

<template>
  <ThemeLabel />
</template>

ThemeLabel.vue

<script setup lang="ts">
import { inject } from "vue";
import { ThemeKey } from "./keys/theme";

const theme = inject(ThemeKey);
</script>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/unmatched-listener

Um pai escuta um evento que o filho não emite.

Severidade padrão: warning
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O pai escuta save, enquanto o filho resolvido declara apenas cancel.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child @save="() => {}" /></template>

Child.vue

<script setup lang="ts">
const emit = defineEmits<{ cancel: [] }>();
</script>
<template><p>Content</p></template>

Correto

O filho declara e emite save, correspondendo ao nome do ouvinte do pai.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child @save="() => {}" /></template>

Child.vue

<script setup lang="ts">
const emit = defineEmits<{ save: [] }>();
emit("save");
</script>
<template><p>Content</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/unregistered-component

Um template usa um componente que não foi registrado nem importado.

Severidade padrão: error
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

Existe um arquivo Child.vue, mas o pai não importa nem registra Child de outra forma para seu template.

App.vue

<template><Child /></template>

Child.vue

<template><p>Child</p></template>

Correto

Importe Child no script setup do pai para que o template resolva a variável do componente.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child /></template>

Child.vue

<template><p>Child</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/unresolved-import

Uma importação não resolve para um módulo.

Severidade padrão: error
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O pai importa ./Missing.vue, mas o projeto contém Child.vue em vez desse caminho.

App.vue

<script setup lang="ts">
import Child from "./Missing.vue";
</script>
<template><Child /></template>

Child.vue

<template><p>Child</p></template>

Correto

Aponte a importação para o arquivo existente ./Child.vue, mantendo a mesma variável no template.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child /></template>

Child.vue

<template><p>Child</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/unused-attrs

Atributos de herança automática são passados para um componente com múltiplas raízes que não os usa.

Severidade padrão: info
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O tracking-code do pai não é consumido como prop nem encaminhado pelo filho com múltiplas raízes.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child tracking-code="notice" /></template>

Child.vue

<template><main>Content</main><aside>Help</aside></template>

Correto

Vincular $attrs a <main> dá um destino explícito a esse atributo de herança automática.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child tracking-code="notice" /></template>

Child.vue

<template><main v-bind="$attrs">Content</main><aside>Help</aside></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/unused-emit

Um evento declarado nunca é emitido.

Severidade padrão: warning
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

O CrossFileAnalyzer experimental em Rust tem um produtor para este código. A etapa da CLI não emite este código individualmente; configurar seu ID não ativa essa etapa Rust. Estes cenários descrevem o grafo e os fatos suportados pelo analisador, e não uma promessa do Vite+.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O filho declara save, mas nunca chama a função de emissão de eventos com esse nome.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child /></template>

Child.vue

<script setup lang="ts">
const emit = defineEmits<{ save: [] }>();
</script>
<template><p>Content</p></template>

Correto

O exemplo chama emit("save"), fazendo com que o evento declarado seja usado. Interações reais devem emiti-lo quando a ação correspondente ocorrer.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child /></template>

Child.vue

<script setup lang="ts">
const emit = defineEmits<{ save: [] }>();
emit("save");
</script>
<template><p>Content</p></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/unused-provide

Uma chave fornecida nunca é injetada.

Severidade padrão: warning
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/unused-provide": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

keys/theme.ts

import type { InjectionKey, Ref } from "vue";
export interface Theme { color: string; }
export const ThemeKey: InjectionKey<Ref<Theme>> = Symbol("theme");

Incorreto

App.vue fornece ThemeKey, mas sua subárvore renderizada de Dashboard.vue não tem nenhum consumidor dessa chave.

App.vue

<script setup lang="ts">
import { provide, ref } from "vue";
import Dashboard from "./Dashboard.vue";
import { ThemeKey, type Theme } from "./keys/theme";

const theme = ref<Theme>({ color: "blue" });
provide(ThemeKey, theme);
</script>

<template>
  <Dashboard />
</template>

Dashboard.vue

<template>
  <h1>Dashboard</h1>
</template>

Correto

O painel agora renderiza ThemeLabel.vue, que injeta a identidade exata de ThemeKey do ancestral.

App.vue

<script setup lang="ts">
import { provide, ref } from "vue";
import Dashboard from "./Dashboard.vue";
import { ThemeKey, type Theme } from "./keys/theme";

const theme = ref<Theme>({ color: "blue" });
provide(ThemeKey, theme);
</script>

<template>
  <Dashboard />
</template>

Dashboard.vue

<script setup lang="ts">
import ThemeLabel from "./ThemeLabel.vue";
</script>

<template>
  <ThemeLabel />
</template>

ThemeLabel.vue

<script setup lang="ts">
import { inject } from "vue";
import { ThemeKey } from "./keys/theme";

const theme = inject(ThemeKey);
</script>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/value-extraction-breaks-reactivity

Ler um valor reativo para uma variável local perde as atualizações posteriores.

Severidade padrão: error
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/value-extraction-breaks-reactivity": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./UserPage.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O item reativo desestruturado do Vue 3.5 é lido para itemSnapshot uma vez; substituições posteriores da prop não atualizam esse instantâneo.

UserPage.vue

<script setup lang="ts">
import { reactive } from "vue";
import UserSummary from "./UserSummary.vue";

const user = reactive({ name: "Ada" });
</script>

<template>
  <UserSummary :item="user" />
</template>

UserSummary.vue

<script setup lang="ts">
const { item } = defineProps<{ item: { name: string } }>();
const itemSnapshot = item;
</script>

Correto

Leia item dentro de computed, para que a transformação de desestruturação reativa de props do Vue possa rastrear cada avaliação.

UserPage.vue

<script setup lang="ts">
import { reactive } from "vue";
import UserSummary from "./UserSummary.vue";

const user = reactive({ name: "Ada" });
</script>

<template>
  <UserSummary :item="user" />
</template>

UserSummary.vue

<script setup lang="ts">
import { computed } from "vue";

const { item } = defineProps<{ item: { name: string } }>();
const itemView = computed(() => item);
</script>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/watch-can-be-computed

Um observador apenas copia um valor para o estado e pode ser um valor computado.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Isto ilustra a preferência publicada por estado puramente derivado. Watchers continuam adequados para efeitos externos ou estado gravável de forma independente; nenhum produtor atual emite este contrato.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

App.vue

<script setup lang="ts">
import { useDouble } from './use-double';
const { count, doubled } = useDouble();
</script>

<template>
<button @click="count++">{{ count }}</button><p>{{ doubled }}</p>
</template>

Incorreto

O observador não produz nenhum efeito externo; ele apenas mantém uma segunda ref gravável sincronizada com o dobro de count. Este exemplo não tem escritas independentes nesse valor derivado.

use-double.ts

import { ref, watch } from 'vue';
export function useDouble() {
  const count = ref(0);
  const doubled = ref(0);
  watch(count, next => { doubled.value = next * 2; }, { immediate: true });
  return { count, doubled };
}

Correto

Uma função de leitura computada expressa diretamente a mesma derivação e remove a sincronização manual e o estado gravável adicional.

use-double.ts

import { computed, ref } from 'vue';
export function useDouble() {
  const count = ref(0);
  const doubled = computed(() => count.value * 2);
  return { count, doubled };
}

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vize:croquis/cf/watcheffect-async

watchEffect inicia uma tarefa assíncrona e não consegue limpar a execução anterior.

Severidade padrão: error
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "croquis/cf/watcheffect-async": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./SearchPage.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

api.ts

export interface Result { items: string[]; }
export async function load(query: string, options?: { signal?: AbortSignal }): Promise<Result> {
  const response = await fetch(`/search?q=${encodeURIComponent(query)}`, options);
  return response.json();
}

Incorreto

O watchEffect assíncrono mistura a coleta implícita de dependências com uma requisição aguardada e sem uma proteção contra invalidação.

SearchPage.vue

<script setup lang="ts">
import { ref } from "vue";
import SearchResults from "./SearchResults.vue";

const query = ref("");
</script>

<template>
  <SearchResults :query="query" />
</template>

SearchResults.vue

<script setup lang="ts">
import { load, type Result } from "./api";
import { ref, watchEffect } from "vue";

const props = defineProps<{ query: string }>();
const result = ref<Result | null>(null);

watchEffect(async () => {
  result.value = await load(props.query);
});
</script>

Correto

Um watch(() => props.query, ...) explícito declara a origem, registra a limpeza da requisição e recusa uma resposta desatualizada após a invalidação.

SearchPage.vue

<script setup lang="ts">
import { ref } from "vue";
import SearchResults from "./SearchResults.vue";

const query = ref("");
</script>

<template>
  <SearchResults :query="query" />
</template>

SearchResults.vue

<script setup lang="ts">
import { load, type Result } from "./api";
import { ref, watch } from "vue";

const props = defineProps<{ query: string }>();
const result = ref<Result | null>(null);

watch(
  () => props.query,
  async (value, _oldValue, onCleanup) => {
    const controller = new AbortController();
    let active = true;

    onCleanup(() => {
      active = false;
      controller.abort();
    });

    const next = await load(value, { signal: controller.signal });
    if (active) result.value = next;
  },
);
</script>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Produtor

Índice de verificações entre arquivos

vize:croquis/cf/watcher-outside-setup

watch ou watchEffect é chamado fora de setup.

Severidade padrão: Não emitido
Aplicável a: Grafo de componentes analisado e fatos suportados descritos abaixo
Correção automática: Nenhuma; revise os arquivos relacionados e aplique a correção
Opções: Sem opções por código; os diagnósticos suportados pela CLI aceitam substituições de severidade

Este é um contrato de diagnóstico publicado sem produtor atual. O cenário incorreto e correto abaixo explica o risco e a correção; nenhuma opção atualmente faz este código ser emitido.

Watchers no escopo de módulo são válidos quando seu responsável mantém e chama uma função de interrupção ou lhes atribui intencionalmente a duração da aplicação. Este exemplo exige ciclos de vida pertencentes ao componente; o contrato não tem produtor atual.

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

index.html

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><title>Contract scenario</title></head>
<body><div id="app"></div><script type="module" src="/main.ts"></script></body></html>

App.vue

<script setup lang="ts">
import Observer from './Observer.vue';
</script>

<template>
<Observer /><Observer />
</template>

Observer.vue

<script setup lang="ts">
import { useObserver } from './use-observer';
const { count, observed } = useObserver();
</script>

<template>
<button @click="count++">{{ count }}</button><p>{{ observed }}</p>
</template>

Incorreto

O observador é criado no carregamento do módulo, fora do setup de qualquer uma das instâncias de Observer, e as duas instâncias compartilham suas refs. Ele não é interrompido automaticamente quando uma instância específica de Observer é desmontada.

use-observer.ts

import { ref, watch } from 'vue';
const count = ref(0);
const observed = ref(0);
watch(count, next => { observed.value = next; });
export function useObserver() { return { count, observed }; }

Correto

Cada chamada síncrona de setup cria suas próprias refs e seu próprio observador dentro de useObserver. O Vue associa esse observador ao ciclo de vida do componente que o chama.

use-observer.ts

import { ref, watch } from 'vue';
export function useObserver() {
  const count = ref(0);
  const observed = ref(0);
  watch(count, next => { observed.value = next; });
  return { count, observed };
}

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Explicação pública

Índice de verificações entre arquivos

vue/cross-file-attrs-fallthrough

Um pai passa atributos para um filho resolvido cuja raiz não pode herdá-los e que não usa $attrs explicitamente.

Severidade padrão: warning
Aplicável a: Declarações alcançáveis do projeto e componentes importados
Opções: crossFile; severidade da regra (off/warn/error)
Correção automática: Nenhuma

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  vue: {
    linter: { preset: "incremental", crossFile: true,
      rules: { "vue/cross-file-attrs-fallthrough": "warn" },
    },
  },
});
vp run lint

Arquivos compartilhados do projeto

Use estes arquivos sem alterações nos exemplos incorreto e correto. Instale os pacotes importados no projeto: Vue, além de vue-router ou Pinia onde indicado. Siga as notas de suporte específicas de versão. A raiz de entrada torna explícita a relação entre os componentes.

main.ts

import { createApp } from "vue";
import Root from "./App.vue";
createApp(Root).mount("#app");

index.html

<div id="app"></div>
<script type="module" src="/main.ts"></script>

Incorreto

O pai passa class="notice" para um filho resolvido com raiz em fragmento, que não tem um destino automático para atributos e nunca lê $attrs.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child class="notice" /></template>

Child.vue

<template><main>Content</main><aside>Help</aside></template>

Correto

O filho escolhe <main> como destino vinculando $attrs ali; seu irmão <aside> permanece separado.

App.vue

<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child class="notice" /></template>

Child.vue

<template><main v-bind="$attrs">Content</main><aside>Help</aside></template>

Os arquivos do exemplo correto demonstram a alteração descrita acima; outros diagnósticos ainda podem se aplicar ao projeto completo.

Índice de verificações entre arquivos