Vue 2-ミューティングプロップvue-warn


180

https://laracasts.com/series/learning-vue-step-by-stepシリーズを開始しました。レッスンVue、Laravel、AJAXで次のエラーで停止しました:

vue.js:2574 [Vue warn]:親コンポーネントが再レンダリングされるたびに値が上書きされるため、プロップを直接変更しないでください。代わりに、プロップの値に基づいてデータまたは計算されたプロパティを使用します。変更されるプロップ: "リスト"(コンポーネントにあります)

main.jsにこのコードがあります

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    created() {
        this.list = JSON.parse(this.list);
    }
});
new Vue({
    el: '.container'
})

リストの小道具を上書きしたときの問題はcreated()にあることはわかっていますが、私はVueの初心者なので、修正方法はまったくわかりません。誰でもそれを修正する方法(そして理由を説明してください)を考えていますか?


2
推測、これは単なる警告メッセージであり、エラーではありません。
デビッドR

回答:


264

これは、プロップをローカル変異させることがVue 2のアンチパターンと見なされているという事実に関係しています

プロップをローカルで変更したい場合に今すべきdataことは、props値を初期値として使用するフィールドを宣言し、次にコピーを変更することです。

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    data: function () {
        return {
            mutableList: JSON.parse(this.list);
        }
    }
});

この詳細については、Vue.js公式ガイドご覧ください。


注1:ことをしてくださいノートで、あなたがすべきではないあなたのために同じ名前を使用propしてdata、すなわち:

data: function () { return { list: JSON.parse(this.list) } // WRONG!!

注2:props反応性について多少の混乱があると思うので、このスレッドをご覧になることをお勧めします


3
これは素晴らしい答えですが、イベントバインディングを含めるように更新する必要があるとも思います。子からトリガーされたイベントは親を更新でき、更新された小道具を子に渡します。このようにして、すべてのコンポーネントで真実のソースが維持されます。複数のコンポーネント間で共有される状態を管理するためのVuexについても言及する価値があります。
ウェスハーパー

@WesHarper 。親の更新イベントを自動的に取得するための省略形として.sync修飾子を使用することも可能です。
danb4r

48

Vueパターンがprops上下しeventsています。シンプルに聞こえますが、カスタムコンポーネントを作成するときに忘れがちです。

Vue 2.2.0以降では、vモデル計算されたプロパティ)を使用できます。この組み合わせにより、コンポーネント間にシンプルでクリーンで一貫性のあるインターフェイスが作成されることがわかりました。

  • propsコンポーネントに渡されたものはすべて無効のままです(つまり、複製されずwatch、変更が検出されたときにローカルコピーを更新する機能も必要ありません)。
  • 変更は自動的に親に送信されます。
  • 複数のレベルのコンポーネントで使用できます。

計算されたプロパティを使用すると、セッターとゲッターを別々に定義できます。これにより、Taskコンポーネントを次のように書き換えることができます。

Vue.component('Task', {
    template: '#task-template',
    props: ['list'],
    model: {
        prop: 'list',
        event: 'listchange'
    },
    computed: {
        listLocal: {
            get: function() {
                return this.list
            },
            set: function(value) {
                this.$emit('listchange', value)
            }
        }
    }
})  

モデルプロパティ定義propに関連付けられv-model、イベントが変更に放出されます。次に、このコンポーネントを親から次のように呼び出すことができます。

<Task v-model="parentList"></Task>

listLocal計算されたプロパティは、(プライベート変数であることのようにそれを考える)コンポーネント内の単純なゲッターとセッターのインターフェイスを提供します。#task-templateあなたの中でレンダリングすることができlistLocal、それは反応的なままです(つまり、parentList変更するとTaskコンポーネントを更新します)。listLocalセッター(例:)を呼び出しthis.listLocal = newListて変更することもでき、変更を親に送信します。

このパターンの優れている点はlistLocalTask(を使用してv-model)の子コンポーネントに渡すことができ、子コンポーネントからの変更が最上位コンポーネントに伝達されることです。

たとえばEditTask、タスクデータになんらかの変更を加えるための個別のコンポーネントがあるとします。同じv-modelプロパティと計算されたプロパティのパターンをlistLocal使用して、コンポーネントに(を使用してv-model)渡すことができます。

<script type="text/x-template" id="task-template">
    <div>
        <EditTask v-model="listLocal"></EditTask>
    </div>
</script>

EditTask変更を発行する場合、それは適切に呼び出さset()listLocal、それによってイベントをトップレベルに伝播します。同様に、EditTaskコンポーネントはを使用して他の子コンポーネント(フォーム要素など)を呼び出すこともできv-modelます。


1
私は同期以外は同じようなことをしています。問題は、変更イベントを発行した後に別のメソッドを実行する必要があることです。ただし、メソッドが実行されてゲッターにヒットすると、変更イベントがリスナーによってまだ取得されていないため、古い値が取得され、子に伝達されます。これは、親の更新が反映されるまでローカルデータが正しいように、小道具を変更したい場合です。これに関して何か考えはありますか?
koga73

セッターからゲッターを呼び出すのは良い習慣ではないと思います。考慮すべきことは、計算されたプロパティに監視を置き、そこにロジックを追加することです。
クリス

小道具がダウンし、イベントがアップしました。これはVueJのドキュメントのどこに説明されていますか?
nebulousGirl

@nebulousGirlこちらをご覧ください:vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow
chris

これは、変更を親コンポーネントに送信するためのより簡単な方法だと思います。
ムハンマド

36

Vueは警告するだけです。コンポーネントのプロップを変更しますが、親コンポーネントが再レンダリングされると、「リスト」が上書きされ、すべての変更が失われます。したがって、そうすることは危険です。

代わりに次のように計算されたプロパティを使用します。

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    computed: {
        listJson: function(){
            return JSON.parse(this.list);
        }
    }
});

25
2ウェイバインディングプロップについてはどうですか?
ジョシュR.17年

7
双方向のデータバインディングが必要な場合は、カスタムイベントを使用する必要があります。詳細については、vuejs.org / v2 / guide / components.html#sync-Modifierを参照してください。プロップを直接変更することはできません。イベントと関数が必要です。プロップの変更を処理します。
マルコ

22

Lodashを使用している場合は、返す前にプロップを複製できます。このパターンは、親と子の両方でその小道具を変更する場合に役立ちます。

コンポーネントグリッドにプロップリストがあるとします。

親コンポーネント内

<grid :list.sync="list"></grid>

子コンポーネント内

props: ['list'],
methods:{
    doSomethingOnClick(entry){
        let modifiedList = _.clone(this.list)
        modifiedList = _.uniq(modifiedList) // Removes duplicates
        this.$emit('update:list', modifiedList)
    }
}

19

小道具を下げ、イベントを上げます。それがVueのパターンです。ポイントは、親から渡された小道具を変異させようとする場合です。これは機能せず、親コンポーネントによって繰り返し上書きされるだけです。子コンポーネントは、親コンポーネントにsthを実行するよう通知するイベントのみを発行できます。これらの制限が気に入らない場合は、VUEXを使用できます(実際には、このパターンは複雑なコンポーネント構造を吸い込むため、VUEXを使用する必要があります!)


2
イベントバスを使用するオプションもあります
Steven Pribilinskiy

イベントバスは、vue 3 では
vuejs /

15

子コンポーネントの小道具の値を変更しないでください。本当に変更する必要がある場合は、を使用できます.sync。ちょうどこのような

<your-component :list.sync="list"></your-component>

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    created() {
        this.$emit('update:list', JSON.parse(this.list))
    }
});
new Vue({
    el: '.container'
})

7

VueJs 2.0によると、コンポーネント内のプロップを変更しないでください。彼らは両親によってのみ変異します。したがって、異なる名前でデータ内の変数を定義し、実際の小道具を見ることによってそれらを更新し続ける必要があります。リストプロップが親によって変更された場合は、それを解析してmutableListに割り当てることができます。これが完全なソリューションです。

Vue.component('task', {
    template: ´<ul>
                  <li v-for="item in mutableList">
                      {{item.name}}
                  </li>
              </ul>´,
    props: ['list'],
    data: function () {
        return {
            mutableList = JSON.parse(this.list);
        }
    },
    watch:{
        list: function(){
            this.mutableList = JSON.parse(this.list);
        }
    }
});

これはmutableListを使用してテンプレートをレンダリングするため、コンポーネント内でリストプロップを安全に保つことができます。


時計を使用するには高価ではありませんか?
Kick Buttowski

6

コンポーネントの小道具を直接変更しないでください。変更する必要がある場合は、次のように新しいプロパティを設定します。

data () {
    return () {
        listClone: this.list
    }
}

そして、listCloneの値を変更します。


6

答えは簡単です。値をいくつかのローカルコンポーネント変数(データプロパティ、ゲッター、セッター、またはウォッチャーで計算されたもの)に割り当てることにより、直接propミューテーションを解除する必要があります。

ウォッチャーを使用した簡単なソリューションを次に示します。

<template>
  <input
    v-model="input"
    @input="updateInput" 
    @change="updateInput"
  />

</template>

<script>
  export default {
  props: {
    value: {
      type: String,
      default: '',
    },
  },
  data() {
    return {
      input: '',
    };
  },
  watch: {
    value: {
      handler(after) {
        this.input = after;
      },
      immediate: true,
    },
  },
  methods: {
    updateInput() {
      this.$emit('input', this.input);
    },
  },
};
</script>

これは、データ入力コンポーネントを作成するために使用するものであり、問​​題なく機能します。親から送信された新しいデータ(v-model(ed))は値ウォッチャーによって監視され、入力変数に割り当てられます。入力が受信されると、そのアクションをキャッチして、データが入力されていることを示唆する入力を親に送信できますフォーム要素から。


5

私もこの問題に直面しました。警告は、私が使用した後行って$on$emit。これは、使用のようなもので$onあり$emit、子コンポーネントから親コンポーネントにデータを送信することが推奨されます。


4

小道具を変更したい場合-オブジェクトを使用します。

<component :model="global.price"></component>

成分:

props: ['model'],
methods: {
  changeValue: function() {
    this.model.value = "new value";
  }
}

ちょうどメモとして、オブジェクトを変更するときは注意する必要があります。JavaScriptオブジェクトは参照によって渡されますが、注意点があります。変数を値に設定すると、参照が壊れます。
ira

3

このような計算されたメソッドを追加する必要があります

component.vue

props: ['list'],
computed: {
    listJson: function(){
        return JSON.parse(this.list);
    }
}

3

一方向のデータフロー、https://vuejs.org/v2/guide/components.htmlによると、コンポーネントは一方向のデータフローに従い 、すべてのプロップは子プロパティと親の間の一方向のバインディングを形成します1つは、親プロパティが更新されると子に流れますが、逆は流れません。これにより、子コンポーネントが誤って親を変更することがなくなり、アプリのデータフローを理解しにくくすることができます。

さらに、親コンポーネントが更新されるたびに、子コンポーネントのすべての小道具が最新の値で更新されます。つまり、子コンポーネント内のプロップを変更しようとしないでください。.vueを実行すると、コンソールで警告が表示されます。

通常、プロップを変更したくなる2つのケースがあります。プロップを使用して初期値を渡します。子コンポーネントは、後でそれをローカルデータプロパティとして使用する必要があります。プロップは、変換する必要がある生の値として渡されます。これらの使用例に対する適切な答えは次のとおりです。プロップの初期値を初期値として使用するローカルデータプロパティを定義します。

props: ['initialCounter'],
data: function () {
  return { counter: this.initialCounter }
}

プロップの値から計算される計算プロパティを定義します。

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

2

これはVueのアンチパターンと見なされるため、Vue.jsプロップは変更しないでください。

あなたが取る必要があるアプローチは、リストの元のpropプロパティを参照するコンポーネントにデータプロパティを作成することです

props: ['list'],
data: () {
  return {
    parsedList: JSON.parse(this.list)
  }
}

これで、コンポーネントに渡されるリスト構造が参照され、 dataプロパティをます:-)

リストのプロパティを解析するだけではない場合は、Vueコンポーネントのcomputedプロパティを使用してください。これにより、小道具をより深く変更することができます。

props: ['list'],
computed: {
  filteredJSONList: () => {
    let parsedList = JSON.parse(this.list)
    let filteredList = parsedList.filter(listItem => listItem.active)
    console.log(filteredList)
    return filteredList
  }
}

上記の例では、リストの小道具を解析し、アクティブなリスト項目のみにフィルターをかけ、ログアウトしてシュニッと笑いをします返します。

:両方のdatacomputedプロパティはテンプレートで同じように参照されます。

<pre>{{parsedList}}</pre>

<pre>{{filteredJSONList}}</pre>

computedプロパティ(メソッド)を呼び出す必要があると考えるのは簡単です...それはしません


2
Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    computed: {
      middleData() {
        return this.list
      }
    },
    watch: {
      list(newVal, oldVal) {
        console.log(newVal)
        this.newList = newVal
      }
    },
    data() {
      return {
        newList: {}
      }
    }
});
new Vue({
    el: '.container'
})

多分これはあなたのニーズを満たすでしょう。


2

ベストアンサーに加えて、

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    data: function () {
        return {
            mutableList: JSON.parse(this.list);
        }
    }
});

配列による小道具の設定は、開発/プロトタイピング用です。本番環境では、小道具のタイプ(https://vuejs.org/v2/guide/components-props.html)を設定し、小道具が設定されていない場合はデフォルト値を設定してくださいそのように、親によって入力されました。

Vue.component('task', {
    template: '#task-template',
    props: {
      list: {
        type: String,
        default() {
          return '{}'
        }
      }
    },
    data: function () {
        return {
            mutableList: JSON.parse(this.list);
        }
    }
});

この方法でmutableListは、JSON.parseエラーが定義されていない場合に、少なくとも空のオブジェクトを取得します。


0

Vue.jsはこれをアンチパターンと見なします。たとえば、次のようないくつかの小道具を宣言して設定します

this.propsVal = 'new Props Value'

したがって、この問題を解決するには、次のように、小道具からデータまたはVueインスタンスの計算されたプロパティに値を取り込む必要があります。

props: ['propsVal'],
data: function() {
   return {
       propVal: this.propsVal
   };
},
methods: {
...
}

これは間違いなく動作します。


0

上記に加えて、次の問題がある他の人のために:

「props値が不要で常に返されるとは限らない場合、渡されたデータはundefined(空ではなく)返されます。<select>デフォルト値を台無しにする可能性があるので、次のように値が設定されているかどうかを確認してbeforeMount()(そうでない場合は設定します)、解決しました。

JS:

export default {
        name: 'user_register',
        data: () => ({
            oldDobMonthMutated: this.oldDobMonth,
        }),
        props: [
            'oldDobMonth',
            'dobMonths', //Used for the select loop
        ],
        beforeMount() {
           if (!this.oldDobMonth) {
              this.oldDobMonthMutated = '';
           } else {
              this.oldDobMonthMutated = this.oldDobMonth
           }
        }
}

HTML:

<select v-model="oldDobMonthMutated" id="dob_months" name="dob_month">

 <option selected="selected" disabled="disabled" hidden="hidden" value="">
 Select Month
 </option>

 <option v-for="dobMonth in dobMonths"
  :key="dobMonth.dob_month_slug"
  :value="dobMonth.dob_month_slug">
  {{ dobMonth.dob_month_name }}
 </option>

</select>

0

多くのコード、ウォッチャー、計算されたプロパティの使用を回避するのに役立つこの答えを出したいと思います。場合によっては、これが良い解決策になることがあります。

小道具は、一方向の通信を提供するように設計されています。

show/hideプロップ付きのモーダルボタンがある場合、私に最適な解決策はイベントを発行することです。

<button @click="$emit('close')">Close Modal</button>

次に、リスナーをモーダル要素に追加します。

<modal :show="show" @close="show = false"></modal>

(この場合、ベースモーダルで直接showイージーを使用できるため、プロップはおそらく不要ですv-if="show"


0

小道具を変更する必要がある場合は、個人的に常に提案します。最初に小道具を計算されたプロパティに渡してそこから戻ります。その後、小道具の突然変異を追跡できる場合でも、小道具を簡単に変更できます。コンポーネントも私たちもあなたが見ることができます。


0

Vueプロップは一方向のデータフローであるため、これにより、子コンポーネントが誤って親の状態を変更することが防止されます。

公式のVueドキュメントから、この問題を解決する2つの方法が見つかります

  1. 子コンポーネントがローカルデータとしてプロップを使用する場合は、ローカルデータプロパティを定義するのが最善です。

      props: ['list'],
      data: function() {
        return {
          localList: JSON.parse(this.list);
        }
      }
    
  2. プロップは、変換する必要がある生の値として渡されます。この場合、プロップの値を使用して計算されたプロパティを定義するのが最善です。

      props: ['list'],
      computed: {
        localList: function() {
           return JSON.parse(this.list);
        },
        //eg: if you want to filter this list
        validList: function() {
           return this.list.filter(product => product.isValid === true)
        }
        //...whatever to transform the list
      }
    
    

0

はい、vue2の属性の変更はアンチパターンです。しかし...他のルールを使用してルールを破って、先に進んでください!必要なのは、.sync修飾子をparetスコープのコンポーネント属性に追加することです。 <your-awesome-components :custom-attribute-as-prob.sync="value" />

🤡バットマンを殺すのは簡単です😁


0

TypeScriptが優先言語の場合。開発の

<template>
<span class="someClassName">
      {{feesInLocale}}
</span>
</template>  



@Prop({default: 0}) fees: any;

// computed are declared with get before a function
get feesInLocale() {
    return this.fees;
}

ではなく

<template>
<span class="someClassName">
      {{feesInLocale}}
</span>
</template>  



@Prop() fees: any = 0;
get feesInLocale() {
    return this.fees;
}

0

以下はスナックバーコンポーネントです。スナックバー変数をこのようにv-modelに直接指定すると、機能しますがコンソールではエラーが発生します。

親コンポーネントが再レンダリングされるたびに値が上書きされるため、プロップを直接変更しないでください。代わりに、プロップの値に基づいてデータまたは計算されたプロパティを使用します。

<template>
        <v-snackbar v-model="snackbar">
        {{ text }}
      </v-snackbar>
</template>

<script>
    export default {
        name: "loader",

        props: {
            snackbar: {type: Boolean, required: true},
            text: {type: String, required: false, default: ""},
        },

    }
</script>

この変異エラーを取り除く正しい方法は、ウォッチャーを使用することです

<template>
        <v-snackbar v-model="snackbarData">
        {{ text }}
      </v-snackbar>
</template>

<script>
/* eslint-disable */ 
    export default {
        name: "loader",
         data: () => ({
          snackbarData:false,
        }),
        props: {
            snackbar: {type: Boolean, required: true},
            text: {type: String, required: false, default: ""},
        },
        watch: { 
        snackbar: function(newVal, oldVal) { 
          this.snackbarData=!this.snackbarDatanewVal;
        }
      }
    }
</script>

したがって、このスナックバーをロードするメインコンポーネントでは、このコードを実行できます

 <loader :snackbar="snackbarFlag" :text="snackText"></loader>

これは私のために働いた

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.