Vize

vize:croquis/cf/circular-reactive-dependency

reactive な計算が互いに循環して依存しています。

既定の重大度: context-dependent
適用範囲: 解析対象のコンポーネント構成と、下記に示す対応済みの情報
自動修正: なし。関連ファイルを確認して修正してください
オプション: コード固有のオプションはありません。対応済みの CLI 検出は重大度を変更できます

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

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

Example qualification: illustrative-source-pair

以下の完全な Vue プロジェクトは、更新の循環とその修正を具体的に示します。このソースによる CLI の検出は検証済みではありません。診断の生成元には、併記したグラフのように保持された参照の ID と流れが必要です。このソースだけで現在の処理がこの診断コードを生成すると断定しません。参照 ID を保持したグラフに対する専用の検出検証と、ソースの構文検査は分けて扱います。

共通のプロジェクト ファイル

以下のファイルは悪い例・良い例で共通です。import する Vue と、例で使う場合は vue-router / Pinia をインストールしてください。バージョン固有の注意がある場合は、その前提に合わせてください。エントリー ファイルでコンポーネントの関係を明確にしています。

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>

悪い

App が count(A)を保持して provide します。CycleView は nextCount(B)を computed で求め、watch でその値を同じ inject 済みの count に書き戻します。immediate の実行後も計算の入力が更新され続けるため、A → B → A の更新の循環になります。下の参照グラフはこの二つの参照を表し、同名の無関係な変数をつなぐものではありません。

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

良い

B の値を A に書き戻す watch を取り除きます。count は App が保持し、明示的な Increment 操作でだけ変更します。CycleView は nextCount を読み取るだけで計算結果を書き戻しません。同じ二つの参照には 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

良い例のファイルは上で説明した変更を示します。プロジェクトには別の検出が残る場合があります。

公開の説明

生成元

ファイル間ルール一覧