Règles inter-fichiers
Chaque règle présente sur cette page son objectif, sa configuration et ses exemples complets Mauvais et Bon, accompagnés de leurs explications. Les lignes surlignées montrent les modifications ; le code copié conserve la source complète. Les limites de prise en charge actuelles sont précisées avant les exemples concernés.
Les 66 exemples de projets réunissent ici les fichiers communs et les exemples complets Mauvais et Bon. Utilisez les fichiers communs des deux côtés. Les 60 codes d’analyse conservent leurs limites réelles : 19 codes CLI (18 paires de sources qualifiées et un projet illustratif avec son graphe réactif conservé), 16 codes de l’analyseur Rust expérimental que cette passe CLI n’émet pas individuellement, et 25 contrats sans producteur actuel. Configurer un identifiant n’active pas un producteur indisponible.
Le CLI public expose la même passe avec vize lint --cross-file. Les codes affichés vize:croquis/cf/* utilisent croquis/cf/* dans vue.linter.rules (sans vize:). Les diagnostics de niveau information/hint deviennent des avertissements CLI. Les emplacements associés expliquent la relation entre la source et le consommateur.
| Règle | Exemples | Objectif | Prise en charge actuelle |
|---|---|---|---|
ecosystem/vue-router-extra-param |
Mauvais · Bon | La route ne déclare pas tab ; Vue Router l’ignore. | Identifiant de lint propre au projet |
ecosystem/vue-router-missing-param |
Mauvais · Bon | Le paramètre obligatoire postId manque ; dépendre de la route actuelle est fragile. | Identifiant de lint propre au projet |
ecosystem/vue-router-param-type |
Mauvais · Bon | postId n’est pas répétable ; un tableau est donc invalide. | Identifiant de lint propre au projet |
ecosystem/vue-router-unknown-route |
Mauvais · Bon | Le nom est absent du routeur installé complet. | Identifiant de lint propre au projet |
html/cross-component-nesting |
Mauvais · Bon | Vérifier l’imbrication HTML réelle après la composition des composants importés. | Identifiant de lint propre au projet |
vize:croquis/cf/array-mutation |
Mauvais · Bon | Un tableau est modifié par index, ce qu’un tableau réactif ne suit pas. | Contrat ; aucun producteur actuel |
vize:croquis/cf/async-boundary |
Mauvais · Bon | Un état réactif traverse une frontière asynchrone et peut être observé dans un état périmé. | CLI |
vize:croquis/cf/async-no-suspense |
Mauvais · Bon | Un composant asynchrone est rendu sans frontière Suspense. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/browser-api-ssr |
Mauvais · Bon | Une API réservée au navigateur est utilisée alors que le composant peut être rendu sur le serveur. | CLI |
vize:croquis/cf/circular-dep |
Mauvais · Bon | Des composants s’importent mutuellement en formant un cycle. | Contrat ; aucun producteur actuel |
vize:croquis/cf/circular-reactive-dependency |
Mauvais · Bon | Des calculs réactifs dépendent les uns des autres en formant un cycle. | CLI |
vize:croquis/cf/closure-captures-reactive |
Mauvais · Bon | Une fermeture capture une valeur réactive et ne verra pas les mises à jour ultérieures. | Contrat ; aucun producteur actuel |
vize:croquis/cf/composable-outside-setup |
Mauvais · Bon | Un composable est appelé en dehors de setup. |
Contrat ; aucun producteur actuel |
vize:croquis/cf/computed-side-effects |
Mauvais · Bon | L’accesseur d’une propriété calculée écrit dans l’état ou produit un autre effet de bord. | Contrat ; aucun producteur actuel |
vize:croquis/cf/deep-import |
Mauvais · Bon | Une chaîne d’imports est plus profonde que ce que le projet autorise. | Contrat ; aucun producteur actuel |
vize:croquis/cf/destructuring-breaks-reactivity |
Mauvais · Bon | La déstructuration d’un objet réactif copie ses champs et supprime leur suivi. | CLI |
vize:croquis/cf/di-outside-setup |
Mauvais · Bon | provide ou inject est appelé en dehors de setup. |
Contrat ; aucun producteur actuel |
vize:croquis/cf/dom-access-without-next-tick |
Mauvais · Bon | Le DOM est lu avant que Vue ait appliqué la mise à jour. | Contrat ; aucun producteur actuel |
vize:croquis/cf/duplicate-id |
Mauvais · Bon | Le même identifiant d’élément est utilisé dans plusieurs composants. | CLI |
vize:croquis/cf/event-listener-leak |
Mauvais · Bon | Un écouteur d’événement est enregistré sans jamais être supprimé. | Contrat ; aucun producteur actuel |
vize:croquis/cf/event-modifier |
Mauvais · Bon | Un écouteur d’événement utilise un modificateur que l’événement émis ne prend pas en charge. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/hydration-risk |
Mauvais · Bon | Ce code regroupe plusieurs diagnostics de réactivité, dont une prop copiée dans une ref. Il ne signifie pas que chaque expression Date.now() est détectée par la passe inter-fichiers. | CLI |
vize:croquis/cf/inherit-attrs-unused |
Mauvais · Bon | inheritAttrs: false est défini et le composant ne lit jamais les attributs. |
Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/inject-without-symbol |
Mauvais · Bon | inject utilise une clé ordinaire au lieu d’un symbole InjectionKey. |
CLI |
vize:croquis/cf/injected-async-mutation-race |
Mauvais · Bon | Une valeur injectée est modifiée par une tâche asynchrone susceptible de provoquer une condition de concurrence. | CLI |
vize:croquis/cf/lifecycle-outside-setup |
Mauvais · Bon | Un hook de cycle de vie est enregistré en dehors de setup. |
Contrat ; aucun producteur actuel |
vize:croquis/cf/lifecycle-without-cleanup |
Mauvais · Bon | Un hook de cycle de vie lance un travail sans jamais le nettoyer. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/missing-required-prop |
Mauvais · Bon | Une prop obligatoire n’est pas transmise. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/missing-suspense |
Mauvais · Bon | Une dépendance asynchrone est utilisée en dehors d’une frontière Suspense. | Contrat ; aucun producteur actuel |
vize:croquis/cf/module-scope-reactive |
Mauvais · Bon | Un état réactif est créé au niveau du module et partagé par tous les appelants. | Contrat ; aucun producteur actuel |
vize:croquis/cf/multi-root-attrs |
Mauvais · Bon | Un composant à plusieurs racines reçoit des attributs sans avoir d’endroit où les placer. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/mutated-after-escape |
Mauvais · Bon | Un objet réactif est modifié après avoir échappé à son propriétaire. | Contrat ; aucun producteur actuel |
vize:croquis/cf/non-reactive-provide |
Mauvais · Bon | Une valeur fournie n’est pas réactive ; les descendants ne verront donc pas les mises à jour. | CLI |
vize:croquis/cf/non-unique-id |
Mauvais · Bon | L’identifiant d’un élément dans une boucle n’est pas unique pour chaque entrée. | CLI |
vize:croquis/cf/object-identity-comparison |
Mauvais · Bon | Un objet réactif est comparé par identité, laquelle change lorsque l’enveloppe est retirée. | Contrat ; aucun producteur actuel |
vize:croquis/cf/pinia-getter |
Mauvais · Bon | Un getter Pinia est lu sans storeToRefs ; il ne restera donc pas réactif. |
Contrat ; aucun producteur actuel |
vize:croquis/cf/prop-type-mismatch |
Mauvais · Bon | La valeur d’une prop transmise ne correspond pas au type déclaré. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/provide-inject-type |
Mauvais · Bon | Une valeur fournie et son injection n’ont pas le même type. | CLI |
vize:croquis/cf/provide-without-symbol |
Mauvais · Bon | provide utilise une clé ordinaire au lieu d’un symbole InjectionKey. |
CLI |
vize:croquis/cf/reactive-export |
Mauvais · Bon | Un état réactif est exporté depuis le module. | Contrat ; aucun producteur actuel |
vize:croquis/cf/reactivity-outside-setup |
Mauvais · Bon | Une API réactive est appelée en dehors de setup. |
Contrat ; aucun producteur actuel |
vize:croquis/cf/reassignment-breaks-reactivity |
Mauvais · Bon | Réaffecter une liaison réactive la remplace par une valeur ordinaire. | CLI |
vize:croquis/cf/reference-escapes-scope |
Mauvais · Bon | Une référence réactive échappe à la portée qui gère sa durée de vie. | Contrat ; aucun producteur actuel |
vize:croquis/cf/setup-context-violation |
Mauvais · Bon | Le contexte setup est utilisé d’une manière que Vue n’autorise pas. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/shallow-deep-access |
Mauvais · Bon | Une propriété profonde d’une valeur shallowReactive ou shallowRef est lue comme si elle était suivie. |
Contrat ; aucun producteur actuel |
vize:croquis/cf/spread-breaks-reactivity |
Mauvais · Bon | Décomposer un objet réactif avec l’opérateur spread copie ses valeurs et supprime leur suivi. | CLI |
vize:croquis/cf/suspense-no-fallback |
Mauvais · Bon | <Suspense> n’a aucun contenu de secours. |
Contrat ; aucun producteur actuel |
vize:croquis/cf/template-ref-timing |
Mauvais · Bon | Une référence de template est lue avant le montage du composant. | Contrat ; aucun producteur actuel |
vize:croquis/cf/toraw-mutation |
Mauvais · Bon | toRaw est utilisé, puis l’objet brut est modifié. |
Contrat ; aucun producteur actuel |
vize:croquis/cf/uncaught-error |
Mauvais · Bon | Un composant peut lever une exception sans qu’aucune frontière d’erreur ne la capture. | CLI |
vize:croquis/cf/undeclared-emit |
Mauvais · Bon | Le composant émet un événement qui n’est pas déclaré. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/undeclared-prop |
Mauvais · Bon | Un parent transmet une prop que l’enfant ne déclare pas. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/undefined-slot |
Mauvais · Bon | Un parent remplit un slot que l’enfant n’expose pas. | Contrat ; aucun producteur actuel |
vize:croquis/cf/unhandled-event |
Mauvais · Bon | Un enfant émet un événement qu’aucun parent ne gère. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/unmatched-inject |
Mauvais · Bon | inject nomme une clé qu’aucun ancêtre ne fournit. |
CLI |
vize:croquis/cf/unmatched-listener |
Mauvais · Bon | Un parent écoute un événement que l’enfant n’émet pas. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/unregistered-component |
Mauvais · Bon | Un template utilise un composant qui n’est ni enregistré ni importé. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/unresolved-import |
Mauvais · Bon | Un import ne se résout pas vers un module. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/unused-attrs |
Mauvais · Bon | Des attributs à transmettre automatiquement sont passés à un composant à plusieurs racines qui ne les utilise pas. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/unused-emit |
Mauvais · Bon | Un événement déclaré n’est jamais émis. | Analyseur Rust expérimental ; code non émis individuellement par le CLI |
vize:croquis/cf/unused-provide |
Mauvais · Bon | Une clé fournie n’est jamais injectée. | CLI |
vize:croquis/cf/value-extraction-breaks-reactivity |
Mauvais · Bon | Extraire une valeur réactive dans une variable locale fait perdre les mises à jour ultérieures. | CLI |
vize:croquis/cf/watch-can-be-computed |
Mauvais · Bon | Un observateur ne fait que copier une valeur dans l’état et peut être remplacé par une propriété calculée. | Contrat ; aucun producteur actuel |
vize:croquis/cf/watcheffect-async |
Mauvais · Bon | watchEffect lance une tâche asynchrone et ne peut pas nettoyer l’exécution précédente. |
CLI |
vize:croquis/cf/watcher-outside-setup |
Mauvais · Bon | watch ou watchEffect est appelé en dehors de setup. |
Contrat ; aucun producteur actuel |
vue/cross-file-attrs-fallthrough |
Mauvais · Bon | Un parent transmet des attributs à un enfant résolu dont la racine ne peut pas en hériter et qui n’utilise pas explicitement $attrs. | Identifiant de lint propre au projet |
Toutes les règles · Options des règles · Correspondance de migration ESLint · Vérifications du projet · Attributs entre composants
ecosystem/vue-router-extra-param
La route ne déclare pas tab ; Vue Router l’ignore.
Gravité par défaut: error
Champ d’application: Déclarations du projet accessibles et composants importés
Options: crossFile ; gravité de la règle (off/warn/error)
Correction automatique: Aucune
Le routeur installé complet doit être accessible depuis createApp(...).use(router) dans l’application. Des tables de routes inconnues ou dynamiques ne prouvent pas les diagnostics de noms inconnus. Les paramètres manquants sont des avertissements, car la navigation peut hériter d’une valeur de la route actuelle.
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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 }],
});
Mauvais
Le chemin user-post déclare userId et postId, mais la navigation fournit également tab, non déclaré, comme paramètre de chemin.
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>
Bon
Supprimer tab de params et conserver uniquement les clés présentes dans le chemin de la route. Utiliser query séparément si l’application a besoin de sélectionner un onglet.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
ecosystem/vue-router-missing-param
Le paramètre obligatoire postId manque ; dépendre de la route actuelle est fragile.
Gravité par défaut: warning
Champ d’application: Déclarations du projet accessibles et composants importés
Options: crossFile ; gravité de la règle (off/warn/error)
Correction automatique: Aucune
Le routeur installé complet doit être accessible depuis createApp(...).use(router) dans l’application. Des tables de routes inconnues ou dynamiques ne prouvent pas les diagnostics de noms inconnus. Les paramètres manquants sont des avertissements, car la navigation peut hériter d’une valeur de la route actuelle.
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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 }],
});
Mauvais
La navigation omet le paramètre obligatoire postId du chemin user-post. Il s’agit d’un avertissement, car Vue Router peut hériter d’une valeur de la route actuelle.
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>
Bon
Passer explicitement userId et postId pour que la navigation ne dépende pas de l’état des paramètres de la route actuelle.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
ecosystem/vue-router-param-type
postId n’est pas répétable ; un tableau est donc invalide.
Gravité par défaut: error
Champ d’application: Déclarations du projet accessibles et composants importés
Options: crossFile ; gravité de la règle (off/warn/error)
Correction automatique: Aucune
Le routeur installé complet doit être accessible depuis createApp(...).use(router) dans l’application. Des tables de routes inconnues ou dynamiques ne prouvent pas les diagnostics de noms inconnus. Les paramètres manquants sont des avertissements, car la navigation peut hériter d’une valeur de la route actuelle.
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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 }],
});
Mauvais
postId est un paramètre de chemin scalaire, mais la navigation lui donne le tableau ["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>
Bon
Passer la valeur scalaire "2" pour le segment postId non répétable.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
ecosystem/vue-router-unknown-route
Le nom est absent du routeur installé complet.
Gravité par défaut: error
Champ d’application: Déclarations du projet accessibles et composants importés
Options: crossFile ; gravité de la règle (off/warn/error)
Correction automatique: Aucune
Le routeur installé complet doit être accessible depuis createApp(...).use(router) dans l’application. Des tables de routes inconnues ou dynamiques ne prouvent pas les diagnostics de noms inconnus. Les paramètres manquants sont des avertissements, car la navigation peut hériter d’une valeur de la route actuelle.
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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 }],
});
Mauvais
Le routeur installé accessible déclare user-post, mais la navigation utilise le nom mal orthographié 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>
Bon
Utiliser le nom enregistré user-post tout en conservant les deux paramètres de chemin déclarés.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
html/cross-component-nesting
Vérifier l’imbrication HTML réelle après la composition des composants importés.
Gravité par défaut: warning
Champ d’application: Déclarations du projet accessibles et composants importés
Options: crossFile ; gravité de la règle (off/warn/error)
Correction automatique: Aucune
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le <p> du parent contient un enfant résolu dont la racine est <div>, ce qui produit une imbrication paragraphe/bloc invalide après composition.
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>
Bon
Utiliser un conteneur <section> pouvant contenir l’élément de bloc de l’enfant ; l’enfant reste inchangé.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/array-mutation
Un tableau est modifié par index, ce qu’un tableau réactif ne suit pas.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Uniquement pour l’ancien Vue 2.7 : utilisez des dépendances Vue 2.7 et de compilateur SFC correspondantes pour ce scénario. Les proxies de Vue 3 suivent les affectations aux indices des tableaux ; items[0] = next est donc réactif en Vue 3 et ne constitue pas un défaut de Vue 3. Ce code publié n’a actuellement aucun producteur de diagnostics.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Dans ce projet historique Vue 2.7, items[0] = next modifie le tableau sans avertir l’observateur de tableaux de Vue 2 ; le premier élément affiché peut donc ne pas être mis à jour.
replace-first.ts
export function replaceFirst(items: string[], next: string): void {
items[0] = next;
}
Bon
splice(0, 1, next) utilise la méthode de modification de tableau observée par Vue 2, permettant au même remplacement de mettre à jour la vue.
replace-first.ts
export function replaceFirst(items: string[], next: string): void {
items.splice(0, 1, next);
}
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/async-boundary
Un état réactif traverse une frontière asynchrone et peut être observé dans un état périmé.
Gravité par défaut: error
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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();
}
Mauvais
Une ancienne requête plus lente peut se terminer après une requête plus récente et écraser result, car l’observateur n’effectue aucun nettoyage lors de l’invalidation.
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>
Bon
Enregistrer le nettoyage avant d’attendre : annuler l’ancienne requête et invalider son indicateur active, puis affecter uniquement une réponse encore active.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/async-no-suspense
Un composant asynchrone est rendu sans frontière Suspense.
Gravité par défaut: warning
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Prise en charge actuelle: no-source-async-fact
Le producteur de diagnostics de cette frontière lit macros.is_async(), mais l’analyse du code source enregistre actuellement le await de premier niveau dans la portée script-setup à la place. La paire complète de sources Mauvais/Bon ci-dessous ne produit donc aucun diagnostic async-no-suspense via le CLI actuel. Elle explique la convention Suspense ; fournir le fait de macro manquant reste un travail d’implémentation à réaliser.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
L’enfant contient un await au niveau supérieur, mais son parent ne fournit aucune frontière <Suspense>. L’analyse actuelle du code source ne fournit pas l’information de macro nécessaire pour émettre ce code de diagnostic.
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>
Bon
Le parent enveloppe le même enfant asynchrone dans <Suspense> avec un contenu de secours pendant le chargement. Cela illustre la convention ; aucune des deux variantes de code source ne produit ce diagnostic dans la passe actuelle.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/browser-api-ssr
Une API réservée au navigateur est utilisée alors que le composant peut être rendu sur le serveur.
Gravité par défaut: warning
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
window.innerWidth s’exécute pendant setup, alors qu’un environnement SSR ne dispose pas du window du navigateur.
App.vue
<script setup lang="ts">
const width = window.innerWidth;
</script>
<template><p>Content</p></template>
Bon
Initialiser une ref avec une valeur sûre pour le serveur et lire window dans onMounted, qui s’exécute après le montage côté client.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/circular-dep
Des composants s’importent mutuellement en formant un cycle.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Cela illustre un cycle concret d’initialisation immédiate. Un composant Vue récursif ou un import circulaire n’est pas automatiquement erroné. Aucun producteur actuel n’émet ce code de contrat.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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';
Mauvais
a.ts importe b.ts, qui importe à son tour a.ts. Tous deux initialisent immédiatement une constante à partir de la constante encore non initialisée de l’autre module, provoquant une erreur de zone morte temporelle.
a.ts
import { bLabel } from './b';
export const aLabel = 'A' + bLabel;
b.ts
import { aLabel } from './a';
export const bLabel = 'B' + aLabel;
Bon
Les deux modules lisent des préfixes initialisés dans le module indépendant labels.ts, supprimant le cycle et la lecture croisée immédiate.
a.ts
import { bPrefix } from './labels';
export const aLabel = 'A' + bPrefix;
b.ts
import { aPrefix } from './labels';
export const bLabel = 'B' + aPrefix;
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/circular-reactive-dependency
Des calculs réactifs dépendent les uns des autres en formant un cycle.
Gravité par défaut: Selon le contexte
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Qualification de l’exemple: illustrative-source-pair
Le projet Vue complet ci-dessous illustre une boucle de rétroaction de mises à jour et sa correction. Il ne constitue pas un témoin qualifié de diagnostic CLI : le producteur de diagnostics exige des identités de références et des arêtes de flux réactif conservées, comme le montre le graphe joint. Ces sources ne prouvent pas que le traitement actuel du code source émettra ce code précis. Les contrôles dédiés de diagnostics sur les graphes à identifiants suivis restent distincts des contrôles de grammaire du code source.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
App possède et fournit count (A). CycleView en dérive nextCount (B), puis réécrit immédiatement chaque valeur dérivée dans le même count injecté. Chaque écriture modifie à nouveau l’entrée du calcul, créant une boucle de mises à jour A → B → A. Les identités du graphe conservé ci-dessous représentent ces deux références, et non des liaisons sans rapport portant les mêmes noms.
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
Bon
Supprimer l’observateur qui réécrit B dans A. App conserve la propriété de count et ne le modifie que par son action explicite Increment ; CycleView lit la valeur dérivée nextCount sans réinjecter le résultat. Les mêmes références ne conservent que la dépendance 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
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/closure-captures-reactive
Une fermeture capture une valeur réactive et ne verra pas les mises à jour ultérieures.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
makeReader copie count.value avant de créer la fermeture. Le lecteur calculé renvoie ensuite ce nombre initial sans lire de dépendance réactive.
reader.ts
import type { Ref } from 'vue';
export function makeReader(count: Ref<number>): () => number {
const captured = count.value;
return () => captured;
}
Bon
La fermeture lit count.value lorsqu’elle est appelée ; l’accesseur calculé peut donc suivre la ref et mettre à jour shown après les incréments.
reader.ts
import type { Ref } from 'vue';
export function makeReader(count: Ref<number>): () => number {
return () => count.value;
}
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/composable-outside-setup
Un composable est appelé en dehors de setup.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Le problème concerne ce composable dépendant du cycle de vie ; il ne s’agit pas d’une interdiction générale des fonctions utilitaires ordinaires ou de tous les appels à la Composition API en dehors du setup. Ce contrat n’a actuellement aucun producteur de diagnostics.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
L’import de use-title.ts enregistre onMounted avant qu’un setup de composant soit actif. Appeler sa fonction exportée plus tard ne fait que renvoyer cette ref au niveau du module ; cela ne peut pas rétablir l’appartenance manquée au cycle de vie.
use-title.ts
import { onMounted, ref } from 'vue';
const title = ref('Before mount');
onMounted(() => { title.value = 'Mounted'; });
export function useTitle() { return title; }
Bon
La création de l’état et l’enregistrement du hook sont tous deux déplacés dans useTitle, qu’App appelle de manière synchrone dans setup. Le hook de montage appartient désormais à cette instance d’App.
use-title.ts
import { onMounted, ref } from 'vue';
export function useTitle() {
const title = ref('Before mount');
onMounted(() => { title.value = 'Mounted'; });
return title;
}
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/computed-side-effects
L’accesseur d’une propriété calculée écrit dans l’état ou produit un autre effet de bord.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
L’évaluation de doubled écrit dans lastCalculated ; lire une valeur calculée modifie donc aussi un état distinct. Cela lie l’effet de bord au moment où l’accesseur paresseux est lu.
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 };
}
Bon
L’accesseur renvoie uniquement le nombre dérivé. Un observateur distinct se charge d’écrire dans lastCalculated lorsque count change, y compris pour sa valeur initiale.
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 };
}
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/deep-import
Une chaîne d’imports est plus profonde que ce que le projet autorise.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Il s’agit d’une politique d’organisation du projet choisie explicitement ; elle n’invente ni seuil de profondeur ni option pris en charge. Aucun producteur de diagnostics actuel n’existe pour ce contrat.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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';
Mauvais
Le point d’entrée fait transiter une valeur simple par level-one, level-two et level-three, créant une chaîne d’imports inutilement profonde pour un projet qui souhaite une interface publique peu profonde.
entry.ts
export { label } from './level-one';
Bon
Le point d’entrée utilise public-api.ts, qui réexporte directement la valeur. Le consommateur conserve le même nom importé tandis que la chaîne se raccourcit.
entry.ts
export { label } from './public-api';
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/destructuring-breaks-reactivity
La déstructuration d’un objet réactif copie ses champs et supprime leur suivi.
Gravité par défaut: error
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
La déstructuration ordinaire de l’objet props copie sa valeur actuelle de item ; elle est distincte de la déstructuration directe de defineProps() dans 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>
Bon
toRef(props, "item") conserve le lien avec la propriété de 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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/di-outside-setup
provide ou inject est appelé en dehors de setup.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Cet exemple utilise provide/inject dans les composants. app.provide et l’injection prise en charge avec app.runWithContext sont d’autres contextes de propriété valides, que ce scénario n’interdit pas. Aucun producteur actuel n’émet ce code de contrat.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
main.ts appelle le provide de composant sans instance de composant active. Le inject de l’enfant ne peut donc pas recevoir cette valeur prévue de l’ancêtre et utilise 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>
Bon
App appelle le fournisseur depuis son setup avant de rendre l’enfant. L’enfant hérite désormais de la valeur dark de son ancêtre composant.
App.vue
<script setup lang="ts">
import ThemedText from './ThemedText.vue';
import { provideTheme } from './theme';
provideTheme();
</script>
<template>
<ThemedText />
</template>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/dom-access-without-next-tick
Le DOM est lu avant que Vue ait appliqué la mise à jour.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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 ?? '';
}
Mauvais
Le gestionnaire de clic incrémente count et lit immédiatement le paragraphe rendu, avant que Vue applique la mise à jour du DOM planifiée. sampled peut contenir le décompte précédent.
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>
Bon
Attendre nextTick() après l’écriture de l’état permet à Vue de mettre à jour le paragraphe avant que readLabel relève son texte.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/duplicate-id
Le même identifiant d’élément est utilisé dans plusieurs composants.
Gravité par défaut: warning
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Les composants de livraison et de facturation accessibles rendent tous deux id="postal-code" ; leurs libellés partagent donc une cible ambiguë dans le document.
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>
Bon
Chaque composant appelle useId() et lie sa propre valeur au libellé et au champ de saisie, conservant leur association sans répéter un identifiant littéral.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/event-listener-leak
Un écouteur d’événement est enregistré sans jamais être supprimé.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le montage ajoute un écouteur de redimensionnement de window qui capture la ref de largeur du composant, mais le démontage ne le supprime jamais. Des montages répétés peuvent conserver des écouteurs et un état inutilisés.
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;
}
Bon
onUnmounted supprime exactement la même fonction resize que celle enregistrée au montage, mettant fin à la durée de vie de l’écouteur externe de cette instance.
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;
}
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/event-modifier
Un écouteur d’événement utilise un modificateur que l’événement émis ne prend pas en charge.
Gravité par défaut: info
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
.stop suppose que l’événement personnalisé save de l’enfant possède la méthode de propagation d’un événement natif, alors que son argument n’est pas nécessairement un DOM Event.
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>
Bon
Supprimer .stop de l’écouteur d’événement personnalisé ; gérer la propagation native au niveau de l’écouteur DOM réel lorsque nécessaire.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/hydration-risk
Ce code regroupe plusieurs diagnostics de réactivité, dont une prop copiée dans une ref. Il ne signifie pas que chaque expression Date.now() est détectée par la passe inter-fichiers.
Gravité par défaut: error
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
L’enfant initialise ref(props.count) une seule fois ; son décompte local ne suit donc plus les changements ultérieurs de la prop du parent. Il s’agit du producteur actuel pour la copie d’une prop dans une ref, et non d’un exemple général de SSR non déterministe.
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>
Bon
toRef(props, "count") pointe vers la prop au lieu de copier sa valeur initiale dans un état indépendant.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/inherit-attrs-unused
inheritAttrs: false est défini et le composant ne lit jamais les attributs.
Gravité par défaut: warning
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
L’enfant définit inheritAttrs: false, mais ne transmet jamais l’attribut class="notice" du parent.
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>
Bon
Conserver le contrôle explicite de l’héritage et lier $attrs à la cible <main> prévue.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/inject-without-symbol
inject utilise une clé ordinaire au lieu d’un symbole InjectionKey.
Gravité par défaut: warning
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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");
Mauvais
Le consommateur injecte la clé chaîne non typée "theme", qui ne fournit aucune identité de symbole partagée avec le fournisseur.
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>
Bon
Le consommateur et le fournisseur importent le même ThemeKey au lieu de dupliquer des noms sous forme de chaînes.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/injected-async-mutation-race
Une valeur injectée est modifiée par une tâche asynchrone susceptible de provoquer une condition de concurrence.
Gravité par défaut: error
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
CountLoader.vue écrit directement le résultat obtenu après attente dans le store injecté partagé avec CountSummary.vue, permettant à un travail périmé d’affecter les deux consommateurs.
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>
Bon
Le chargeur annule le travail invalidé et n’émet qu’un résultat actif. Le fournisseur se charge de modifier le store via 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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/lifecycle-outside-setup
Un hook de cycle de vie est enregistré en dehors de setup.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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'; });
}
Mauvais
Le point d’entrée appelle installTitle() avant de monter une application ; onMounted est donc enregistré sans contexte setup de composant actif.
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>
Bon
Appeler le même utilitaire de manière synchrone depuis le setup d’App associe la fonction de rappel de cycle de vie au montage de cette instance.
App.vue
<script setup lang="ts">
import { installTitle } from './install-title';
installTitle();
</script>
<template>
<p>Application</p>
</template>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/lifecycle-without-cleanup
Un hook de cycle de vie lance un travail sans jamais le nettoyer.
Gravité par défaut: warning
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le montage enregistre un écouteur de redimensionnement de window, mais le démontage ne supprime jamais cette même fonction de rappel.
App.vue
<script setup lang="ts">
import { onMounted } from "vue";
const resize = () => {};
onMounted(() => { window.addEventListener("resize", resize); });
</script>
<template><p>Content</p></template>
Bon
onUnmounted supprime l’écouteur avec le même nom d’événement et la même identité de fonction que ceux utilisés par 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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/missing-required-prop
Une prop obligatoire n’est pas transmise.
Gravité par défaut: error
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le parent rend <Child /> sans la prop obligatoire title: string de l’enfant.
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>
Bon
title="Hello" fournit la prop obligatoire déclarée par l’enfant résolu.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/missing-suspense
Une dépendance asynchrone est utilisée en dehors d’une frontière Suspense.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
AsyncCard contient un await au niveau supérieur, ce qui rend son setup asynchrone, mais App le rend sans frontière Suspense pour coordonner cette dépendance.
App.vue
<script setup lang="ts">
import AsyncCard from './AsyncCard.vue';
</script>
<template>
<AsyncCard />
</template>
Bon
App enveloppe l’enfant asynchrone dans Suspense et fournit un contenu de secours pendant le chargement, jusqu’à ce que le setup de l’enfant soit terminé.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/module-scope-reactive
Un état réactif est créé au niveau du module et partagé par tous les appelants.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Un état réactif à la portée du module est autorisé pour des stores applicatifs conçus à cet effet. Cet exemple suppose une isolation par composant ou requête ; le contrat publié n’a actuellement aucun producteur de diagnostics.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le module initialise count une seule fois et les deux instances de Counter reçoivent la même ref. Cliquer sur l’une modifie les deux compteurs, alors que cet exemple prévoit un état indépendant pour chaque instance.
counter.ts
import { ref } from 'vue';
const count = ref(0);
export function createCounter() { return { count }; }
Bon
Créer la ref dans createCounter donne à chaque appel synchrone de setup un objet d’état distinct ; chaque bouton possède donc son propre compteur.
counter.ts
import { ref } from 'vue';
export function createCounter() {
const count = ref(0);
return { count };
}
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/multi-root-attrs
Un composant à plusieurs racines reçoit des attributs sans avoir d’endroit où les placer.
Gravité par défaut: warning
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
L’enfant a pour racines <main> et <aside> ; Vue ne dispose donc pas d’une racine unique pouvant recevoir automatiquement la classe du parent.
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>
Bon
Transmettre explicitement $attrs à <main> tout en conservant la seconde racine.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/mutated-after-escape
Un objet réactif est modifié après avoir échappé à son propriétaire.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Il s’agit d’une politique explicite de propriété d’un historique immuable, et non d’une interdiction générale de transmettre des objets réactifs ou de les modifier ensuite. Aucun producteur actuel n’émet ce contrat.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
L’archive conserve le même objet que celui passé à publish. Le propriétaire modifie ensuite le nom de cet objet, remplaçant rétroactivement par Grace le nom dans l’enregistrement censé être historique. Le paramètre Readonly de TypeScript ne copie pas l’objet.
profile.ts
import { reactive } from 'vue';
import { publish } from './archive';
export function publishProfile(): void {
const profile = reactive({ name: 'Ada' });
publish(profile);
profile.name = 'Grace';
}
Bon
Publier une copie ordinaire sépare l’enregistrement archivé d’Ada des modifications ultérieures du profil réactif. La politique d’instantanés de l’archive est désormais respectée.
profile.ts
import { reactive } from 'vue';
import { publish } from './archive';
export function publishProfile(): void {
const profile = reactive({ name: 'Ada' });
publish({ ...profile });
profile.name = 'Grace';
}
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/non-reactive-provide
Une valeur fournie n’est pas réactive ; les descendants ne verront donc pas les mises à jour.
Gravité par défaut: Selon le contexte
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
ThemeProvider.vue fournit un objet ordinaire. Modifier les champs de cet objet ne donne pas de dépendance réactive Vue au consommateur qui l’injecte.
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>
Bon
Le fournisseur enveloppe le thème dans ref ; la même référence injectée peut suivre les changements ultérieurs.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/non-unique-id
L’identifiant d’un élément dans une boucle n’est pas unique pour chaque entrée.
Gravité par défaut: error
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Chaque itération de v-for rend le même identifiant littéral result-title ; la clé de la boucle ne rend pas les identifiants DOM uniques.
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>
Bon
L’identifiant du titre inclut l’identifiant stable du résultat, produisant un identifiant distinct dans le document pour chaque entrée.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/object-identity-comparison
Un objet réactif est comparé par identité, laquelle change lorsque l’enveloppe est retirée.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
L’exemple suppose que les identifiants distinguent les enregistrements de façon unique. Comparer deux références au même proxy réactif reste valide ; ce contrat n’a actuellement aucun producteur de diagnostics.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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) };
}
Mauvais
proxy === raw compare l’identité de l’enveloppe ; le résultat est donc faux même si les deux représentent le même enregistrement utilisateur. L’application visait l’identité de l’enregistrement, et non celle de l’enveloppe de l’objet.
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>
Bon
Comparer le id stable de l’enregistrement répond à la question voulue sans dépendre du fait que l’objet soit brut ou enveloppé dans un 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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/pinia-getter
Un getter Pinia est lu sans storeToRefs ; il ne restera donc pas réactif.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Pinia doit être installé, et main.ts installe son plugin avant le montage. Lire directement store.doubled dans un calcul suivi ou un template est valide ; le défaut consiste ici à prendre un simple instantané. Ce contrat n’a actuellement aucun producteur de diagnostics.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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 },
});
Mauvais
const doubled = store.doubled copie le nombre actuel du getter pendant setup. Le nombre copié ne suit pas les mises à jour ultérieures 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>
Bon
storeToRefs(store) fournit une ref de getter réactive qui peut être déstructurée et déballée par le template tout en restant liée au store.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/prop-type-mismatch
La valeur d’une prop transmise ne correspond pas au type déclaré.
Gravité par défaut: error
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le parent passe l’expression numérique 42 à la prop title: string de l’enfant résolu.
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>
Bon
Le littéral title="Hello" fournit une chaîne correspondant à la déclaration de l’enfant.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/provide-inject-type
Une valeur fournie et son injection n’ont pas le même type.
Gravité par défaut: warning
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Ce contrôle compare les annotations de type explicites du fournisseur et du consommateur, et non les types de valeurs littérales inférés. Conservez l’annotation as string du fournisseur dans cet exemple.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le fournisseur annote explicitement title avec le type string, tandis que le descendant demande inject<number> pour la même clé.
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>
Bon
L’appel explicite inject<string> du consommateur correspond à l’annotation du fournisseur. Conserver as string : ce producteur compare les annotations explicites, et non les types littéraux inférés.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/provide-without-symbol
provide utilise une clé ordinaire au lieu d’un symbole InjectionKey.
Gravité par défaut: warning
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Les deux composants utilisent la chaîne "theme" ; des fonctionnalités sans rapport peuvent accidentellement réutiliser cette clé.
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>
Bon
Exporter un seul symbole typé ThemeKey et importer cette même valeur aux endroits où provide et inject sont appelés. Créer des symboles distincts avec la même description ne les relierait pas.
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");
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/reactive-export
Un état réactif est exporté depuis le module.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Les stores applicatifs volontairement partagés peuvent exporter un état réactif. Ce scénario exige un état isolé et ne prétend pas que tout export réactif est invalide. Aucun producteur actuel n’émet ce contrat.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le module exporte un seul objet réactif initialisé ; chaque module qui l’importe reçoit donc le même décompte. Dans un module SSR partagé entre les requêtes, cela compromet l’isolation de l’état par instance ou par requête prévue par l’exemple.
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>
Bon
Le module exporte une fabrique et App l’appelle dans setup. Chaque instance obtient un nouveau décompte réactif au lieu du singleton exporté.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/reactivity-outside-setup
Une API réactive est appelée en dehors de setup.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Vue autorise ref/reactive/computed en dehors du setup des composants. Le risque concerne ici une propriété ou un partage indésirable dans le cadre d’une politique explicite d’isolation des instances, et non un usage interdit de l’API. Ce contrat n’a actuellement aucun producteur de diagnostics.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Les deux API réactives s’exécutent pendant le chargement du module. Les deux instances de Counter partagent donc une seule ref et une seule valeur calculée, malgré l’intention d’avoir des compteurs indépendants.
use-counter.ts
import { computed, ref } from 'vue';
const count = ref(0);
const doubled = computed(() => count.value * 2);
export function useCounter() { return { count, doubled }; }
Bon
useCounter crée la ref et la valeur calculée de manière synchrone dans chaque appel de setup d’un composant, donnant à chaque widget son propre état et sa propre dérivation suivie.
use-counter.ts
import { computed, ref } from 'vue';
export function useCounter() {
const count = ref(0);
const doubled = computed(() => count.value * 2);
return { count, doubled };
}
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/reassignment-breaks-reactivity
Réaffecter une liaison réactive la remplace par une valeur ordinaire.
Gravité par défaut: error
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
L’enfant crée une ref de prop, puis écrase la variable avec props.user, supprimant le lien fourni par cette 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>
Bon
Conserver le toRef dans une liaison const et supprimer la réaffectation qui le remplace.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/reference-escapes-scope
Une référence réactive échappe à la portée qui gère sa durée de vie.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Les refs peuvent légitimement être renvoyées par des composables ou partagées entre des portées. Cet exemple exige explicitement un cache d’instantanés ; il ne prétend pas que le démontage invalide une ref. Aucun producteur actuel n’émet ce contrat.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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; }
Mauvais
Le cache au niveau du processus conserve la ref active du décompte du composant. Il peut maintenir l’état de cette instance accessible après le démontage et observer des modifications ultérieures, alors que ce cache est censé stocker un instantané.
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>
Bon
Le cache reçoit le nombre ordinaire actuel ; il conserve donc un instantané sans retenir la ref appartenant au composant.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/setup-context-violation
Le contexte setup est utilisé d’une manière que Vue n’autorise pas.
Gravité par défaut: Selon le contexte
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
ref(0) est créé au niveau du module d’un script normal, en dehors du contexte setup propre à chaque instance représenté par ce scénario d’analyse.
App.vue
<script lang="ts">
import { ref } from "vue";
const count = ref(0);
export default {};
</script>
<template><p>Count</p></template>
Bon
Déplacer la liaison dans script setup, où chaque instance de composant possède son décompte et où le template peut le lire.
App.vue
<script setup lang="ts">
import { ref } from "vue";
const count = ref(0);
</script>
<template><p>{{ count }}</p></template>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/shallow-deep-access
Une propriété profonde d’une valeur shallowReactive ou shallowRef est lue comme si elle était suivie.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
shallowReactive suit la propriété racine user, mais laisse l’objet imbriqué brut. Modifier profile.user.name n’avertit pas le template comme le ferait une modification profonde suivie.
profile.ts
import { shallowReactive } from 'vue';
export function makeProfile() { return shallowReactive({ user: { name: 'Ada' } }); }
Bon
La réactivité profonde de reactive enveloppe l’objet utilisateur imbriqué ; la même affectation du nom peut donc déclencher la mise à jour du nom affiché.
profile.ts
import { reactive } from 'vue';
export function makeProfile() { return reactive({ user: { name: 'Ada' } }); }
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/spread-breaks-reactivity
Décomposer un objet réactif avec l’opérateur spread copie ses valeurs et supprime leur suivi.
Gravité par défaut: error
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
UserSummary.vue décompose props.user dans un nouvel objet avec l’opérateur spread, prenant un instantané des données réactives reçues.
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>
Bon
toRef(props, "user") conserve une référence vers la prop reçue au lieu de copier ses champs.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/suspense-no-fallback
<Suspense> n’a aucun contenu de secours.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Suspense sans contenu de repli est une syntaxe Vue valide. Il s’agit d’une convention choisie pour l’interface de chargement, et non d’une erreur de compilation ; le contrat publié n’a actuellement aucun producteur de diagnostics.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
La frontière Suspense a un enfant asynchrone, mais aucun contenu de secours ; cet exemple n’affiche donc aucun contenu de chargement pendant l’attente.
App.vue
<script setup lang="ts">
import { Suspense } from 'vue';
import AsyncCard from './AsyncCard.vue';
</script>
<template>
<Suspense><AsyncCard /></Suspense>
</template>
Bon
Le slot #fallback fournit un paragraphe de chargement explicite jusqu’à ce que l’enfant asynchrone soit prêt.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/template-ref-timing
Une référence de template est lue avant le montage du composant.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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(); }
Mauvais
Setup lit la référence de template avant le montage, alors que sa valeur est encore null. L’appel optionnel de focus ne donne donc le focus à aucun élément.
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>
Bon
onMounted diffère la lecture jusqu’à ce que Vue ait affecté l’élément de saisie à la référence de template, permettant à l’utilitaire de focus d’agir dessus.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/toraw-mutation
toRaw est utilisé, puis l’objet brut est modifié.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
rename obtient la cible brute et écrit dans raw.name, contournant le setter du proxy qui déclencherait la mise à jour du nom réactif affiché.
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';
}
Bon
Écrire dans profile.name au moyen du proxy réactif transmis conserve le même renommage tout en notifiant ce qui en dépend.
profile.ts
import { reactive } from 'vue';
export function makeProfile() { return reactive({ name: 'Ada' }); }
export function rename(profile: { name: string }): void {
profile.name = 'Grace';
}
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/uncaught-error
Un composant peut lever une exception sans qu’aucune frontière d’erreur ne la capture.
Gravité par défaut: info
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Le producteur actuel analyse les expressions de template telles que JSON.parse(input). Il ne signale pas une instruction throw présente uniquement dans le bloc script.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le template de l’enfant appelle JSON.parse sur une entrée mal formée et le parent accessible n’a aucune frontière de capture d’erreur.
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>
Bon
Le parent enregistre onErrorCaptured autour de cet enfant. Renvoyer false arrête la propagation ; une frontière utilisée en production doit aussi présenter une interface de récupération utile.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/undeclared-emit
Le composant émet un événement qui n’est pas déclaré.
Gravité par défaut: error
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
L’enfant appelle emit("save"), mais son contrat defineEmits ne déclare que 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>
Bon
Déclarer save avec son tuple d’arguments vide pour que l’événement émis corresponde au contrat du composant.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/undeclared-prop
Un parent transmet une prop que l’enfant ne déclare pas.
Gravité par défaut: warning
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le parent transmet typo alors que l’enfant résolu ne déclare que title. Cette convention de l’analyseur est distincte du comportement général de transmission automatique des attributs de 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>
Bon
Supprimer la liaison involontaire typo et conserver la prop déclarée 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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/undefined-slot
Un parent remplit un slot que l’enfant n’expose pas.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
App fournit un slot footer, mais Card ne déclare et ne rend que header. Le contenu Notice fourni ne dispose d’aucun emplacement de slot correspondant dans cet enfant.
App.vue
<script setup lang="ts">
import Card from './Card.vue';
</script>
<template>
<Card><template #footer>Notice</template></Card>
</template>
Bon
App fournit header, correspondant à la fois à la déclaration de slot typée de l’enfant et à son emplacement rendu ; Notice apparaît donc à cet endroit.
App.vue
<script setup lang="ts">
import Card from './Card.vue';
</script>
<template>
<Card><template #header>Notice</template></Card>
</template>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/unhandled-event
Un enfant émet un événement qu’aucun parent ne gère.
Gravité par défaut: info
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Child.vue émet save, mais le composant qui l’enveloppe directement ne l’écoute pas ; les événements de composants ne remontent pas automatiquement à travers les composants intermédiaires.
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>
Bon
Wrapper.vue associe un écouteur save à son enfant direct. La fonction de rappel vide illustre la gestion de l’événement pour cette règle, et non une implémentation complète de l’enregistrement.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/unmatched-inject
inject nomme une clé qu’aucun ancêtre ne fournit.
Gravité par défaut: error / warning (avec valeur par défaut)
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
ThemeLabel.vue injecte ThemeKey, mais son ancêtre accessible App.vue ne fournit jamais cette clé.
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>
Bon
App.vue fournit un thème réactif au moyen du même ThemeKey exporté, avant de rendre le descendant qui l’injecte.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/unmatched-listener
Un parent écoute un événement que l’enfant n’émet pas.
Gravité par défaut: warning
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le parent écoute save, tandis que l’enfant résolu ne déclare que 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>
Bon
L’enfant déclare et émet save, correspondant au nom de l’écouteur du parent.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/unregistered-component
Un template utilise un composant qui n’est ni enregistré ni importé.
Gravité par défaut: error
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Un fichier Child.vue existe, mais le parent n’importe pas Child et ne l’enregistre pas non plus d’une autre manière pour son template.
App.vue
<template><Child /></template>
Child.vue
<template><p>Child</p></template>
Bon
Importer Child dans le script setup du parent pour que le template résolve la liaison du composant.
App.vue
<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child /></template>
Child.vue
<template><p>Child</p></template>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/unresolved-import
Un import ne se résout pas vers un module.
Gravité par défaut: error
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le parent importe ./Missing.vue, mais le projet contient Child.vue plutôt que ce chemin.
App.vue
<script setup lang="ts">
import Child from "./Missing.vue";
</script>
<template><Child /></template>
Child.vue
<template><p>Child</p></template>
Bon
Faire pointer l’import vers le fichier existant ./Child.vue, en conservant la même liaison dans le template.
App.vue
<script setup lang="ts">
import Child from "./Child.vue";
</script>
<template><Child /></template>
Child.vue
<template><p>Child</p></template>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/unused-attrs
Des attributs à transmettre automatiquement sont passés à un composant à plusieurs racines qui ne les utilise pas.
Gravité par défaut: info
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le tracking-code du parent n’est ni consommé comme prop ni transmis par l’enfant à plusieurs racines.
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>
Bon
Lier $attrs à <main> donne une destination explicite à cet attribut transmis automatiquement.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/unused-emit
Un événement déclaré n’est jamais émis.
Gravité par défaut: warning
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
L’analyseur Rust expérimental CrossFileAnalyzer possède un producteur pour ce code. La passe CLI n’émet pas ce code individuel ; configurer son identifiant n’active pas cette passe Rust. Ces scénarios décrivent le graphe et les faits pris en charge par l’analyseur, sans constituer une promesse de Vite+.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
L’enfant déclare save, mais n’appelle jamais la fonction d’émission d’événements avec ce nom.
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>
Bon
L’exemple appelle emit("save"), utilisant ainsi l’événement déclaré. Les interactions réelles doivent l’émettre lorsque l’action correspondante se produit.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/unused-provide
Une clé fournie n’est jamais injectée.
Gravité par défaut: warning
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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");
Mauvais
App.vue fournit ThemeKey, mais son sous-arbre rendu Dashboard.vue ne contient aucun consommateur de cette clé.
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>
Bon
Le tableau de bord rend désormais ThemeLabel.vue, qui injecte exactement la même identité ThemeKey que celle de l’ancêtre.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/value-extraction-breaks-reactivity
Extraire une valeur réactive dans une variable locale fait perdre les mises à jour ultérieures.
Gravité par défaut: error
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le item déstructuré réactif de Vue 3.5 est lu une seule fois dans itemSnapshot ; le remplacement ultérieur de la prop ne met pas à jour cet instantané.
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>
Bon
Lire item dans computed pour que la transformation de déstructuration réactive des props de Vue puisse suivre chaque évaluation.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/watch-can-be-computed
Un observateur ne fait que copier une valeur dans l’état et peut être remplacé par une propriété calculée.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Cela illustre la préférence publiée pour un état purement dérivé. Les watchers restent appropriés pour les effets externes ou les états modifiables indépendamment ; aucun producteur actuel n’émet ce contrat.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
L’observateur ne produit aucun effet externe ; il maintient uniquement une seconde ref modifiable synchronisée avec le double de count. Cet exemple n’effectue aucune écriture indépendante dans cette valeur dérivée.
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 };
}
Bon
L’accesseur d’une propriété calculée exprime directement la même dérivation et supprime la synchronisation manuelle ainsi que l’état modifiable supplémentaire.
use-double.ts
import { computed, ref } from 'vue';
export function useDouble() {
const count = ref(0);
const doubled = computed(() => count.value * 2);
return { count, doubled };
}
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/watcheffect-async
watchEffect lance une tâche asynchrone et ne peut pas nettoyer l’exécution précédente.
Gravité par défaut: error
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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();
}
Mauvais
Le watchEffect asynchrone mêle la collecte implicite des dépendances à une requête dont il attend le résultat, sans garde contre l’invalidation.
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>
Bon
Un watch(() => props.query, ...) explicite déclare la source, enregistre le nettoyage de la requête et refuse une réponse périmée après invalidation.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vize:croquis/cf/watcher-outside-setup
watch ou watchEffect est appelé en dehors de setup.
Gravité par défaut: Non émis
Champ d’application: Graphe de composants analysé et faits pris en charge décrits ci-dessous
Correction automatique: Aucune ; examinez les fichiers concernés et appliquez la correction
Options: Aucune option propre à un code ; les diagnostics CLI pris en charge acceptent des changements de gravité
Il s’agit d’un contrat de diagnostic publié sans producteur actuel. Le scénario Mauvais/Bon ci-dessous explique le risque et la correction ; aucune option ne permet actuellement de déclencher ce code.
Les watchers à la portée du module sont valides lorsque leur propriétaire conserve et appelle une fonction d’arrêt, ou leur donne volontairement la durée de vie de l’application. Cet exemple exige des durées de vie appartenant au composant ; le contrat n’a actuellement aucun producteur de diagnostics.
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
L’observateur est créé au chargement du module, en dehors du setup de chacune des deux instances d’Observer, et les deux instances partagent ses refs. Il n’est pas automatiquement arrêté lorsqu’une instance donnée d’Observer est démontée.
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 }; }
Bon
Chaque appel synchrone de setup crée ses propres refs et son propre observateur dans useObserver. Vue associe cet observateur à la durée de vie du composant appelant.
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 };
}
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.
Index des règles inter-fichiers
vue/cross-file-attrs-fallthrough
Un parent transmet des attributs à un enfant résolu dont la racine ne peut pas en hériter et qui n’utilise pas explicitement $attrs.
Gravité par défaut: warning
Champ d’application: Déclarations du projet accessibles et composants importés
Options: crossFile ; gravité de la règle (off/warn/error)
Correction automatique: Aucune
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
Fichiers communs du projet
Utilisez ces fichiers inchangés dans les exemples Mauvais et Bon. Installez les packages importés dans le projet : Vue, ainsi que vue-router ou Pinia lorsqu’ils sont indiqués. Respectez toute note de prise en charge propre à une version. Le point d’entrée racine rend explicite la relation entre les composants.
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>
Mauvais
Le parent transmet class="notice" à un enfant résolu constitué d’un fragment, qui n’a aucune cible automatique pour les attributs et ne lit jamais $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>
Bon
L’enfant choisit <main> comme cible en y liant $attrs ; son frère <aside> reste distinct.
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>
Les fichiers de l’exemple Bon montrent la modification décrite ci-dessus ; d’autres diagnostics peuvent encore s’appliquer au projet complet.