web-dev-qa-db-ja.com

Vue 2 - 変化する小道具vue-warn

https://laracasts.com/series/learning-vue-step-by-step seriesを起動しました。このエラーで Vue、Laravel、およびAJAX のレッスンをやめました。

vue.js:2574 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "list" (found in component <task>)

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

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

リストプロップを上書きすると、問題が created() にあることはわかっていますが、Vueの初心者なので、修正方法がまったくわかりません。誰がどうやってそれを修正するのか(そしてその理由を説明してください)アイデアがありますか?

119

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

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

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

詳細については、 Vue.js公式ガイド で読むことができます。


注1:あなたはnotpropdataに同じ名前を使用します。つまり:

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

注2:から、[////]に関して混乱があると感じていますpropsおよびreactivitythis threadをご覧になることをお勧めします

204
ira

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

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

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

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

17
edison xue

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

コンポーネント grid にprop list があるとしましょう。

親コンポーネント内

<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)
    }
}
17
For the Name

Vueパターンは単純です:props downおよびevents up。簡単に思えますが、カスタムコンポーネントを作成するときは忘れがちです。

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

  • コンポーネントに渡された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)
            }
        }
    }
})  

model プロパティは、どのpropv-modelに関連付けられ、どのイベントが変更時に発行されるかを定義します。その後、次のように親からこのコンポーネントを呼び出すことができます。

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

listLocalの計算プロパティは、コンポーネント内に単純なgetterおよびsetterインタフェースを提供します(これはプライベート変数であると考えてください)。 #task-template内でlistLocalをレンダリングすることができ、それは反応性を保ちます(すなわち、parentListが変更されるとそれはTaskコンポーネントを更新します)。 setterを呼び出すことで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が変更を発した場合は、listLocalに対してset()を適切に呼び出し、それによってイベントをトップレベルに伝播します。同様に、EditTaskコンポーネントはv-modelを使用して他の子コンポーネント(フォーム要素など)を呼び出すこともできます。

11
chris

子コンポーネントの小道具の値を変更しないでください。本当にそれを変更する必要があるなら、.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'
})
11
Potato Running

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を使います、従ってあなたはあなたのリストプロップをコンポーネント内で安全に保ちます。

6
burak buruk

components.ppsを直接変更しないでください。変更する必要がある場合は、次のように新しいプロパティを設定します。

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

そしてlistCloneの値を変更してください。

5
Tango5614

私もこの問題に直面しました。警告は私が$on$emitを使った後に消えました。子コンポーネントから親コンポーネントにデータを送信するために推奨されるuse $onおよび$emitのようなものです。

4
ihsanberahim

一方向のデータフロー、 https://vuejs.org/v2/guide/components.html に従って、コンポーネントは一方向のデータフローに従います。すべての小道具は子との間で一方向のバインディングを形成します。これは、子コンポーネントが誤って親コンポーネントを変更してしまうため、アプリのデータフローが理解しにくくなる可能性があります。

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

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

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

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

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

小道具を変えたいのであれば - objectを使ってください。

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

成分:

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

このような計算方法を追加する必要があります。

component.vue

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

ベストアンサーに追加する

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

配列でプロップを設定することはdev/prototypingのためのものです、プロダクションではプロップタイプ( 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);
        }
    }
});

このようにして、未定義の場合、少なくともJSON.parseエラーの代わりにmutableListに空のオブジェクトを取得します。

2
YuuwakU
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
D.P

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

必要なアプローチは、listの元のpropプロパティを参照するデータプロパティをコンポーネント上に作成することです。

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

コンポーネントに渡されたリスト構造は、コンポーネントのdataプロパティを介して参照および変更されます。

単にあなたのlistプロパティを解析する以上のことをしたいのなら、Vueコンポーネントのcomputedプロパティを利用してください。これはあなたがあなたの小道具にもっと徹底的な突然変異を加えることを可能にします。

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

上記の例では、listプロップを解析し、それをactive list-temsのみに絞り込み、ログアウトしてschnittsとgigglesにして返します。

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

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

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

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

2
Francis Leigh

上記に加えて、以下の問題を抱えている他の人たちのために:

"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
Maroun Melhem

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

this.propsVal = 'new Props Value'

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

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

これは間違いなくうまくいきます。

0
iamsangeeth