我开始了https://laracasts.com/series/learning-vue-step-by-step系列。我在 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的新手,所以我完全不知道如何解决它。任何人都知道如何(并请解释原因)来解决它?
答案 0 :(得分:188)
这与 mutating a prop locally is considered an anti-pattern in Vue 2
这一事实有关如果您想在本地改变道具,您现在应该做的是在props
中声明一个使用Vue.component('task', {
template: '#task-template',
props: ['list'],
data: function () {
return {
mutableList: JSON.parse(this.list);
}
}
});
值作为其初始值的字段值,然后改变副本:
data: function () { return { list: JSON.parse(this.list) } // WRONG!!
上详细了解相关信息
注1:请注意 you should not use the same name for your prop
and data
,即:
props
注2:关于NULL
和反应性 I feel there is some confusion以来,我建议您查看this主题< / p>
答案 1 :(得分:29)
Vue只是警告你:你改变组件中的prop,但是当父组件重新渲染时,“list”将被覆盖,你将丢失所有的更改。所以这样做很危险。
使用计算属性,如下所示:
PropertyChanged
答案 2 :(得分:17)
Props down,事件发生了。这是Vue的模式。关键是,如果你试图改变从父母传递的道具。它不会工作,只是被父组件重复覆盖。子组件只能发出一个事件来通知父组件做某事。如果您不喜欢这些限制,您可以使用VUEX(实际上这种模式会吸引复杂的组件结构,您应该使用VUEX!)
答案 3 :(得分:17)
如果你正在使用Lodash,你可以在返回之前克隆道具。如果您在父项和子项上修改该道具,则此模式很有用。
假设我们在组件 grid 上有 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)
}
}
答案 4 :(得分:12)
Vue模式很简单:向下props
,向上events
。听起来很简单,但是在编写自定义组件时很容易忘记。
从Vue 2.2.0开始,您可以使用v-model(与computed properties一起使用)。我发现这种组合在组件之间创建了一个简单,干净且一致的接口:
props
仍然是响应式的(即,它没有被克隆,也不需要watch
函数来在检测到更改时更新本地副本)。一个计算属性允许对setter和getter进行分别定义。这样可以将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
组件)。您还可以通过调用设置器(例如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
发出更改,它将在set()
上适当调用listLocal
,从而将事件传播到顶层。同样,EditTask
组件也可以使用v-model
来调用其他子组件(例如表单元素)。
答案 5 :(得分:11)
您不应更改子组件中道具的值。
如果您确实需要更改它,可以使用.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'
})
答案 6 :(得分:6)
根据VueJs 2.0,你不应该改变组件内的prop。他们只是由父母变异。因此,您应该在具有不同名称的数据中定义变量,并通过观察实际道具来更新它们。 如果父级更改了列表道具,您可以解析它并将其分配给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渲染您的模板,因此您可以在列表中保持列表支持安全。
答案 7 :(得分:5)
不要直接在components中更改道具。如果你需要更改,它会设置一个新属性,如下所示:
data () {
return () {
listClone: this.list
}
}
并更改listClone的值。
答案 8 :(得分:4)
我也遇到过这个问题。使用$on
和$emit
后警告消失了。
这类似于使用$on
和$emit
建议将数据从子组件发送到父组件。
答案 9 :(得分:3)
你需要像这样添加计算方法
<强> component.vue 强>
props: ['list'],
computed: {
listJson: function(){
return JSON.parse(this.list);
}
}
答案 10 :(得分:3)
单向数据流, 根据{{3}},该组件遵循单向 数据流, 所有道具形成了子属性和父属性之间的单向向下绑定,当父属性更新时,它将向下流向子而不是相反,这可以防止子组件意外地改变父级&#39 ; s,这可以使您的应用程序的数据流更难理解。
此外,每次父组件更新所有道具 在子组件中将刷新最新值。这意味着您不应该尝试改变子组件内的prop。如果你这样做.vue会警告你 控制台。
通常有两种情况可能会使支柱变异: prop用于传递初始值;子组件之后想要将它用作本地数据属性。 prop是作为需要转换的原始值传递的。 这些用例的正确答案是: 定义使用prop的初始值作为其初始值的本地数据属性:
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
定义根据prop的值计算的计算属性:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
答案 11 :(得分:3)
如果你想改变道具 - 使用对象。
<component :model="global.price"></component>
成分:
props: ['model'],
methods: {
changeValue: function() {
this.model.value = "new value";
}
}
答案 12 :(得分: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'
})
也许这会满足您的需求。
答案 13 :(得分: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);
}
}
});
此方式,你ATLEAST得到在一个空对象mutableList
如果未定义,而不是JSON.parse错误。
答案 14 :(得分:2)
Vue.js道具不会被改变,因为这被视为Vue中的反模式。
您需要采取的方法是在组件上创建一个引用 list 的原始 prop 属性的数据属性
//get user preferences
Spinner spinnerCurrency = (Spinner) findViewById(R.id.spinnerCurrency);
Spinner spinnerRefresh = (Spinner) findViewById(R.id.spinnerRefresh);
currency = spinnerCurrency.getSelectedItem().toString();
refresh = spinnerRefresh.getSelectedItem().toString();
Button saveButton = findViewById(R.id.saveChangesButton);
saveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
preferences=getSharedPreferences("UserPreference", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("currency",currency);
editor.putString("refresh",refresh);
editor.commit();
}
});
现在,您传递给组件的列表结构将通过组件的props: ['list'],
data: () {
return {
parsedList: JSON.parse(this.list)
}
}
属性进行引用和变异: - )
如果您希望做的不仅仅是解析列表属性,那么请使用Vue组件&#39; data
财产。
这样你就可以对你的道具进行更深入的突变。
computed
上面的示例解析了您的列表道具,并将其过滤为仅有效列表 - tems,将其记录为用于schnitts和giggles 退货。
note :props: ['list'],
computed: {
filteredJSONList: () => {
let parsedList = JSON.parse(this.list)
let filteredList = parsedList.filter(listItem => listItem.active)
console.log(filteredList)
return filteredList
}
}
&amp; data
属性在模板中引用相同的内容,例如
computed
可以很容易地认为需要调用<pre>{{parsedList}}</pre>
<pre>{{filteredJSONList}}</pre>
属性(作为方法)...它没有
答案 15 :(得分:1)
答案很简单,您应该通过将值分配给一些局部组件变量(可以是数据属性,使用getter,setter或watcher计算)来破坏直接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)),并将其分配给输入变量,一旦接收到输入,我们就可以捕获该动作并将输入发送给父级,提示输入了数据来自表单元素。
答案 16 :(得分:1)
下面是小吃店组件,当我将 snackbar 变量直接输入v模型时,如果这样可以正常工作,但在控制台中,它将给出错误消息
避免直接更改prop,因为每当父组件重新渲染时,该值都会被覆盖。而是根据道具的值使用数据或计算属性。
<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>
摆脱这种突变错误的正确方法是使用 watcher
<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>
这对我有用
答案 17 :(得分:1)
Vue3有一个非常好的解决方案。花了几个小时到达那里。但这确实很好。
在父模板上
<user-name
v-model:first-name="firstName"
v-model:last-name="lastName"
></user-name>
子组件
app.component('user-name', {
props: {
firstName: String,
lastName: String
},
template: `
<input
type="text"
:value="firstName"
@input="$emit('update:firstName',
$event.target.value)">
<input
type="text"
:value="lastName"
@input="$emit('update:lastName',
$event.target.value)">
`
})
这是唯一进行双向绑定的解决方案。我喜欢前两个答案都很好地解决了使用SYNC和发射更新事件以及计算属性getter设置器的问题,但这真是一件难事,而且我不喜欢这么辛苦。
答案 18 :(得分: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>
答案 19 :(得分:0)
我想给出这个答案,这有助于避免使用大量代码,观察程序和计算属性。在某些情况下,这可能是一个很好的解决方案:
道具旨在提供单向通信。
当您使用带有道具的模式show/hide
按钮时,对我来说最好的解决方案是发出一个事件:
<button @click="$emit('close')">Close Modal</button>
然后将侦听器添加到模式元素:
<modal :show="show" @close="show = false"></modal>
(在这种情况下,道具show
可能是不必要的,因为您可以直接在基本模式下使用简单的v-if="show"
)
答案 20 :(得分:0)
我个人总是建议您是否需要对道具进行突变,首先将其传递给计算属性,然后从那里返回,此后,您就可以轻松地对道具进行突变,即使您可以追踪道具的突变,也可能是从其他组件变异而来的,或者我们也可以观看。
答案 21 :(得分:0)
因为Vue道具是数据流的一种方式,所以可以防止子组件意外改变父级的状态。
从Vue官方文档中,我们将找到两种解决此问题的方法
如果子组件要使用props作为本地数据,则最好定义一个本地数据属性。
props: ['list'],
data: function() {
return {
localList: JSON.parse(this.list);
}
}
道具作为需要转换的原始值传入。在这种情况下,最好使用prop的值定义一个计算属性:
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
}
答案 22 :(得分:0)
是!,vue2中的变异属性是反模式。但...
只需使用其他规则来打破规则,然后继续前进即可!
您需要在paret范围内的组件属性中添加.sync修饰符。
<your-awesome-components :custom-attribute-as-prob.sync="value" />
?很简单,我们杀死了蝙蝠侠?
答案 23 :(得分: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;
}