ホームページ > ウェブフロントエンド > jsチュートリアル > Vue で v-model でオブジェクトを使用する方法

Vue で v-model でオブジェクトを使用する方法

Susan Sarandon
リリース: 2025-01-20 02:34:39
オリジナル
424 人が閲覧しました

How to use an object with v-model in Vue

コンポーネント間の双方向のデータ バインディングを実装する Vue.js の v-model ディレクティブについては誰もがよく知っています。ただし、カスタム コンポーネントの v-model を手動で実装すると、通常、いくつかの問題が発生します。

通常のアプローチは次のとおりです:

const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
<template></template>
ログイン後にコピー
ログイン後にコピー

コンポーネント内の modelValue プロパティの値は変更しないことに注意してください。代わりに、更新された値を emit メソッド経由で親コンポーネントに返し、親コンポーネントが実際の変更を行います。その理由は次のとおりです。子コンポーネントは親コンポーネントの状態に影響を与えるべきではありません。これにより、データ フローが複雑になり、デバッグが困難になります。

Vue のドキュメントに記載されているように、props は子コンポーネント内で変更すべきではありません。これを行うと、Vue はコンソールに警告を出します。

対象者の調子はどうですか?

JavaScript のオブジェクトと配列は参照によって渡されるため、特殊なケースです。これは、コンポーネントがオブジェクト プロパティのネストされたプロパティを直接変更できることを意味します。ただし、Vue は、ネストされたオブジェクトのプロパティの変更について警告しません (これらの変更を追跡すると、パフォーマンスが低下します)。したがって、このような予期しない変更により、アプリケーションで検出やデバッグが困難な問題が発生する可能性があります。

ほとんどの場合、基本値を v-model として使用します。ただし、フォームコンポーネントを構築する場合など、場合によっては、オブジェクトを処理できるカスタム v-model が必要になる場合があります。これは重要な疑問につながります:

上記の落とし穴を回避しながらオブジェクトを処理するカスタム v-model を実装するにはどうすればよいですか?

ディスカッションの質問

1 つの方法は、書き込み可能な計算プロパティまたは defineModelヘルパー関数を使用することです。ただし、どちらのソリューションにも重大な欠点があります。元のオブジェクトを直接変更するため、明確なデータ フローを維持するという目的が損なわれます。

この問題を説明するために、「フォーム」コンポーネントの例を見てみましょう。このコンポーネントは、フォーム内の値が変更されたときに、オブジェクトの更新されたコピーを親コンポーネントに返すように設計されています。書き込み可能な計算プロパティを使用してこれを実現しようとします。

この例では、書き込み可能な計算プロパティによって元のオブジェクトが変更されます。

import { computed } from 'vue';
import { cloneDeep } from 'lodash-es';

type Props = {
  modelValue: { name: string; email: string; };
};

const props = withDefaults(defineProps<Props>(), {
  modelValue: () => ({ name: '', email: '' }),
});

const emit = defineEmits<{
  'update:modelValue': [value: Props['modelValue']];
}>();


const formData = computed({
  // 返回的getter对象仍然是可变的
  get() {
    return props.modelValue;
  },
  // 注释掉setter仍然会修改prop
  set(newValue) {
    emit('update:modelValue', cloneDeep(newValue));
  },
});
ログイン後にコピー
ログイン後にコピー

ゲッターから返されたオブジェクトはまだ 変更可能であるため、これは 機能しません。元のオブジェクトが予期せず変更されてしまいます。

defineModel 同じことです。 update:modelValue はコンポーネントから出力されないため、オブジェクトのプロパティは警告なしに変更されます。

解決策

この状況に対処する「Vue の方法」は、内部リアクティブ値を使用してオブジェクトを表し、2 つのオブザーバーを実装することです。

  1. オブザーバーは、modelValue プロパティの変更を監視し、内部値を更新します。これにより、内部状態が親コンポーネントによって渡された最新の prop 値を反映するようになります。
  2. オブザーバーは内部値の変化を観察します。内部値が更新されると、元のオブジェクトが直接変更されるのを避けるために、オブジェクトの新しいクローン バージョンが親コンポーネントに出力されます。

これら 2 つのオブザーバー間の無限のフィードバック ループを防ぐには、modelValue プロパティの更新によって内部値のオブザーバーが誤って再トリガーされないようにする必要があります。

const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
<template></template>
ログイン後にコピー
ログイン後にコピー

「これはやりすぎだ!」と思っていることはわかります。さらに簡素化する方法を見てみましょう。

VueUse でソリューションを簡素化します

このロジックを再利用可能な合成関数に抽出することは、プロセスを簡素化する優れた方法です。しかし良いニュースは、その必要さえないということです。 VueUse の useVModel 結合関数は、この問題に対処するのに役立ちます。

VueUse は強力な Vue ユーティリティ ライブラリであり、複合ユーティリティの「スイス アーミー ナイフ」とも呼ばれます。完全にツリーシェイク可能なので、パッケージのサイズが大きくなる心配がなく、必要な部分だけを使用できます。

useVModel を使用してリファクタリングする前の例は次のとおりです:

import { computed } from 'vue';
import { cloneDeep } from 'lodash-es';

type Props = {
  modelValue: { name: string; email: string; };
};

const props = withDefaults(defineProps<Props>(), {
  modelValue: () => ({ name: '', email: '' }),
});

const emit = defineEmits<{
  'update:modelValue': [value: Props['modelValue']];
}>();


const formData = computed({
  // 返回的getter对象仍然是可变的
  get() {
    return props.modelValue;
  },
  // 注释掉setter仍然会修改prop
  set(newValue) {
    emit('update:modelValue', cloneDeep(newValue));
  },
});
ログイン後にコピー
ログイン後にコピー

はるかに簡単です!

それだけです!子コンポーネントから直接変更せずに、Vue で v-model を含むオブジェクトを適切に使用する方法を検討してきました。オブザーバーを使用したり、VueUse の useVModel のような構成関数を活用したりすることで、アプリケーション内で明確で予測可能な状態管理を維持できます。

この記事のすべての例を含む Stackblitz リンクは次のとおりです。自由に探索して実験してください。

読んでいただきありがとうございます。コーディングを楽しんでください!

以上がVue で v-model でオブジェクトを使用する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート