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の初心者なので、修正方法がまったくわかりません。誰がどうやってそれを修正するのか(そしてその理由を説明してください)アイデアがありますか?
これは、プロップをローカルで変更することは、Vue 2のアンチパターンと見なされるという事実と関係しています。
プロップをローカルで変更したい場合、ここですべきことは、data
値を使用するprops
のフィールドを宣言することです。その初期値とコピーを変更します:
Vue.component('task', {
template: '#task-template',
props: ['list'],
data: function () {
return {
mutableList: JSON.parse(this.list);
}
}
});
詳細については、 Vue.js公式ガイド で読むことができます。
注1:あなたはnotprop
とdata
に同じ名前を使用します。つまり:
data: function () { return { list: JSON.parse(this.list) } // WRONG!!
注2:から、[////]に関して混乱があると感じていますprops
およびreactivity、 this threadをご覧になることをお勧めします
Vueはあなたに警告します:あなたはコンポーネントのプロップを変更します、しかし親コンポーネントが再レンダリングされるとき、「リスト」は上書きされ、あなたはすべてのあなたの変更を失います。だからそうするのは危険です。
次のように代わりに計算プロパティを使用します。
Vue.component('task', {
template: '#task-template',
props: ['list'],
computed: {
listJson: function(){
return JSON.parse(this.list);
}
}
});
小道具、イベントが上がる。それがVueのパターンです。重要なのは、親から渡された小道具を変更しようとした場合です。それは動作しませんし、親コンポーネントによって繰り返し上書きされるだけです。子コンポーネントは、親コンポーネントにsthを実行するよう通知するイベントのみを発行できます。これらの制限が気に入らなければ、VUEXを使うことができます(実際にはこのパターンは複雑なコンポーネント構造を吸い込むので、VUEXを使うべきです!)
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)
}
}
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 プロパティは、どのprop
がv-model
に関連付けられ、どのイベントが変更時に発行されるかを定義します。その後、次のように親からこのコンポーネントを呼び出すことができます。
<Task v-model="parentList"></Task>
listLocal
の計算プロパティは、コンポーネント内に単純なgetterおよびsetterインタフェースを提供します(これはプライベート変数であると考えてください)。 #task-template
内でlistLocal
をレンダリングすることができ、それは反応性を保ちます(すなわち、parentList
が変更されるとそれはTask
コンポーネントを更新します)。 setterを呼び出すことでlistLocal
を変更することもできます(例:this.listLocal = newList
)。それは親に変更を発行します。
このパターンの優れた点は、listLocal
をTask
の子コンポーネントに(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
を使用して他の子コンポーネント(フォーム要素など)を呼び出すこともできます。
子コンポーネントの小道具の値を変更しないでください。本当にそれを変更する必要があるなら、.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'
})
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を使います、従ってあなたはあなたのリストプロップをコンポーネント内で安全に保ちます。
components.ppsを直接変更しないでください。変更する必要がある場合は、次のように新しいプロパティを設定します。
data () {
return () {
listClone: this.list
}
}
そしてlistCloneの値を変更してください。
私もこの問題に直面しました。警告は私が$on
と$emit
を使った後に消えました。子コンポーネントから親コンポーネントにデータを送信するために推奨されるuse $on
および$emit
のようなものです。
一方向のデータフロー、 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()
}
}
小道具を変えたいのであれば - objectを使ってください。
<component :model="global.price"></component>
成分:
props: ['model'],
methods: {
changeValue: function() {
this.model.value = "new value";
}
}
このような計算方法を追加する必要があります。
component.vue
props: ['list'],
computed: {
listJson: function(){
return JSON.parse(this.list);
}
}
ベストアンサーに追加する
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
に空のオブジェクトを取得します。
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'
})
多分これはあなたのニーズを満たすでしょう。
これは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にして返します。
note:data
とcomputed
の両方のプロパティはテンプレート内で同じように参照されます。
<pre>{{parsedList}}</pre>
<pre>{{filteredJSONList}}</pre>
(メソッドである)computed
プロパティを呼び出す必要があると考えるのは簡単かもしれませんが…そうではありません
上記に加えて、以下の問題を抱えている他の人たちのために:
"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>
Vue.jsはこれをアンチパターンと考えています。たとえば、次のようないくつかの小道具を宣言して設定します。
this.propsVal = 'new Props Value'
そのため、この問題を解決するには、次のように、小道具からVueインスタンスのデータまたは計算プロパティに値を取り込む必要があります。
props: ['propsVal'],
data: function() {
return {
propVal: this.propsVal
};
},
methods: {
...
}
これは間違いなくうまくいきます。