Vue 2 - 变异道具 vue-warn
Vue 2 - Mutating props vue-warn
我开始了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 )
我在main.js
中有这个代码
Vue.component('task', {
template: '#task-template',
props: ['list'],
created() {
this.list = JSON.parse(this.list);
}
});
new Vue({
el: '.container'
})
我知道问题出在 created() 当我覆盖 list prop 时,但我是 Vue 的新手,所以我完全不知道如何解决它。任何人都知道如何(并请解释为什么)修复它?
Vue 只是警告您:您更改了组件中的 prop,但是当父组件重新渲染时,"list" 将被覆盖并且您将丢失所有更改。所以这样做很危险。
像这样使用计算 属性:
Vue.component('task', {
template: '#task-template',
props: ['list'],
computed: {
listJson: function(){
return JSON.parse(this.list);
}
}
});
这与以下事实有关 mutating a prop locally is considered an anti-pattern in Vue 2
如果您想在本地改变道具,您现在应该做的是在您的data
中声明一个使用props
值作为其初始值,然后对副本进行变异:
Vue.component('task', {
template: '#task-template',
props: ['list'],
data: function () {
return {
mutableList: JSON.parse(this.list);
}
}
});
上阅读更多相关信息
注1:请注意you should not use the same name for your prop
and data
,即:
data: function () { return { list: JSON.parse(this.list) } } // WRONG!!
注2:自I feel there is some confusion regarding props
and reactivity, I suggest you to have a look on this线程
Vue.js 认为这是一个反模式。例如,声明和设置一些像
这样的道具
this.propsVal = 'new Props Value'
因此,要解决此问题,您必须从 props 中获取一个值到数据或 Vue 实例的计算 属性,如下所示:
props: ['propsVal'],
data: function() {
return {
propVal: this.propsVal
};
},
methods: {
...
}
这绝对有效。
如果你想改变道具 - 使用对象。
<component :model="global.price"></component>
组件:
props: ['model'],
methods: {
changeValue: function() {
this.model.value = "new value";
}
}
我也遇到过这个问题。在我使用 $on
和 $emit
后警告消失了。
这类似于使用 $on
和 $emit
推荐将数据从子组件发送到父组件。
如果您使用的是 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)
}
}
道具下降,活动上升。这就是 Vue 的模式。关键是,如果您尝试改变从父级传递的道具。它不会工作,它只会被父组件重复覆盖。子组件只能发出事件通知父组件做某事。如果你不喜欢这些限制,你可以使用VUEX(实际上这种模式会吸收复杂的组件结构,你应该使用VUEX!)
您不应该在子组件中更改道具的值。
如果你真的需要改变它,你可以使用 .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'
})
您需要像这样添加计算方法
component.vue
props: ['list'],
computed: {
listJson: function(){
return JSON.parse(this.list);
}
}
Vue.js props 不能改变,因为这被认为是 Vue 中的反模式。
您需要采取的方法是在您的组件上创建一个数据 属性,它引用 的原始 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
}
}
上面的例子解析了你的 list 道具并将其过滤为仅 active 列表项,将其注销 对于 schnitts 和咯咯笑声 和 returns 它。
注意:data
和 computed
属性在模板中引用相同,例如
<pre>{{parsedList}}</pre>
<pre>{{filteredJSONList}}</pre>
很容易认为 computed
属性(作为一个方法)需要被调用...它不需要
单向数据流,
根据 https://vuejs.org/v2/guide/components.html,组件遵循单向
数据流,
所有 props 在子 属性 和父 属性 之间形成单向向下绑定,当父 属性 更新时,它会向下流向子而不是相反,这防止了子意外改变父组件,这会使您的应用程序的数据流更难理解。
此外,每次父组件更新所有道具
子组件中的 将使用最新值进行刷新。这意味着您不应该尝试改变子组件内的道具。如果你这样做,.vue 会在
控制台。
通常有两种情况很容易改变道具:
prop 用于传入一个初始值;子组件之后想将其用作本地数据 属性。
该道具作为需要转换的原始值传入。
这些用例的正确答案是:
定义一个本地数据属性,使用prop的初始值作为其初始值:
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
定义一个计算 属性,它是根据 prop 的值计算的:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
根据 VueJs 2.0,你不应该改变组件内部的 prop。它们仅由它们的 parent 变异。因此,您应该在数据中定义不同名称的变量,并通过观察实际道具来保持它们的更新。
如果列表属性被 parent 更改,您可以解析它并将其分配给 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.if 中更改道具,您需要像这样更改它设置一个新的 属性:
data () {
return () {
listClone: this.list
}
}
并更改 listClone 的值。
Vue 模式 props
向下,events
向上。听起来很简单,但是写自定义组件的时候很容易忘记。
从 Vue 2.2.0 开始,您可以使用 v-model (with 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
computed 属性 在组件中提供了一个简单的 getter 和 setter 接口(将其视为私有变量)。在 #task-template
中,您可以渲染 listLocal
并且它将保持反应性(即,如果 parentList
发生变化,它将更新 Task
组件)。您还可以通过调用 setter(例如 this.listLocal = newList
)来改变 listLocal
,它会将更改发送给父级。
此模式的优点在于您可以将 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
.
调用其他子组件(例如表单元素)
除上述问题外,其他人有以下问题:
"If the props value is not required and thus not always returned, the passed data would return undefined
(instead of empty)"。这可能会弄乱 <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.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.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);
}
}
});
如果未定义,这样您至少会在 mutableList
中得到一个空对象而不是 JSON.parse 错误。
我想给出这个答案,这有助于避免使用大量代码、观察者和计算属性。在某些情况下,这可能是一个很好的解决方案:
道具旨在提供单向交流。
当你有一个带道具的模态 show/hide
按钮时,对我来说最好的解决方案是发出一个事件:
<button @click="$emit('close')">Close Modal</button>
然后将侦听器添加到模态元素:
<modal :show="show" @close="show = false"></modal>
(在这种情况下,道具 show
可能是不必要的,因为您可以直接在基本模态上使用简单的 v-if="show"
)
我个人总是建议,如果你需要改变道具,首先将它们传递给计算的 属性 和 return 从那里,然后可以很容易地改变道具,即使你可以跟踪 prop 突变,如果它们也从另一个组件发生突变,或者我们也可以观看。
因为 Vue props 是数据流的一种方式,这可以防止子组件意外改变父组件的状态。
从Vue官方文档中,我们会找到2种方法来解决这个问题
如果子组件要使用props作为本地数据,最好定义一个本地数据属性.
props: ['list'],
data: function() {
return {
localList: JSON.parse(this.list);
}
}
prop 作为需要转换的原始值传入。在这种情况下,最好使用 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
}
答案很简单,您应该通过将值分配给一些局部组件变量(可以是数据 属性,使用 getter、setter 或 watchers 计算)来打破直接 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))将被价值观察者观察并分配给输入变量,一旦接收到输入,我们可以捕获该动作并向父级发出输入,表明数据已输入来自表单元素。
是的!,在 vue2 中改变属性是反模式的。但...
只需使用其他规则打破规则,然后继续前进!
您需要的是将 .sync 修饰符添加到 paret 范围内的组件属性。
<your-awesome-components :custom-attribute-as-prob.sync="value" />
我们杀死蝙蝠侠很简单
当 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;
}
下面是一个小吃店组件,当我像这样将 snackbar 变量直接赋给 v-model 时,如果可以,但在控制台中,它会给出一个错误
避免直接修改 prop,因为只要父组件重新渲染,该值就会被覆盖。相反,使用数据或根据 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>
这对我有用
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 和 Emitting 更新事件,并计算 属性 getter setter,但这是一项艰巨的工作,我没有喜欢努力工作。
一个潜在的解决方案是使用全局变量。
import { Vue } from "nuxt-property-decorator";
export const globalStore = new Vue({
data: {
list: [],
},
}
export function setupGlobalsStore() {
Vue.prototype.$globals = globalStore;
}
那么你会使用:
$globals.list
任何你需要改变它或呈现它的地方。
将道具分配给新变量。
data () {
return {
listClone: this.list
}
}
你应该始终避免在 vue 或任何其他框架中改变 props。您可以采取的方法是将其复制到另一个变量中。
例如。
// instead of replacing the value of this.list use a different variable
this.new_data_variable = JSON.parse(this.list)
我开始了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 )
我在main.js
中有这个代码Vue.component('task', {
template: '#task-template',
props: ['list'],
created() {
this.list = JSON.parse(this.list);
}
});
new Vue({
el: '.container'
})
我知道问题出在 created() 当我覆盖 list prop 时,但我是 Vue 的新手,所以我完全不知道如何解决它。任何人都知道如何(并请解释为什么)修复它?
Vue 只是警告您:您更改了组件中的 prop,但是当父组件重新渲染时,"list" 将被覆盖并且您将丢失所有更改。所以这样做很危险。
像这样使用计算 属性:
Vue.component('task', {
template: '#task-template',
props: ['list'],
computed: {
listJson: function(){
return JSON.parse(this.list);
}
}
});
这与以下事实有关 mutating a prop locally is considered an anti-pattern in Vue 2
如果您想在本地改变道具,您现在应该做的是在您的data
中声明一个使用props
值作为其初始值,然后对副本进行变异:
Vue.component('task', {
template: '#task-template',
props: ['list'],
data: function () {
return {
mutableList: JSON.parse(this.list);
}
}
});
上阅读更多相关信息
注1:请注意you should not use the same name for your prop
and data
,即:
data: function () { return { list: JSON.parse(this.list) } } // WRONG!!
注2:自I feel there is some confusion regarding props
and reactivity, I suggest you to have a look on this线程
Vue.js 认为这是一个反模式。例如,声明和设置一些像
这样的道具this.propsVal = 'new Props Value'
因此,要解决此问题,您必须从 props 中获取一个值到数据或 Vue 实例的计算 属性,如下所示:
props: ['propsVal'],
data: function() {
return {
propVal: this.propsVal
};
},
methods: {
...
}
这绝对有效。
如果你想改变道具 - 使用对象。
<component :model="global.price"></component>
组件:
props: ['model'],
methods: {
changeValue: function() {
this.model.value = "new value";
}
}
我也遇到过这个问题。在我使用 $on
和 $emit
后警告消失了。
这类似于使用 $on
和 $emit
推荐将数据从子组件发送到父组件。
如果您使用的是 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)
}
}
道具下降,活动上升。这就是 Vue 的模式。关键是,如果您尝试改变从父级传递的道具。它不会工作,它只会被父组件重复覆盖。子组件只能发出事件通知父组件做某事。如果你不喜欢这些限制,你可以使用VUEX(实际上这种模式会吸收复杂的组件结构,你应该使用VUEX!)
您不应该在子组件中更改道具的值。
如果你真的需要改变它,你可以使用 .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'
})
您需要像这样添加计算方法
component.vue
props: ['list'],
computed: {
listJson: function(){
return JSON.parse(this.list);
}
}
Vue.js props 不能改变,因为这被认为是 Vue 中的反模式。
您需要采取的方法是在您的组件上创建一个数据 属性,它引用 的原始 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
}
}
上面的例子解析了你的 list 道具并将其过滤为仅 active 列表项,将其注销 对于 schnitts 和咯咯笑声 和 returns 它。
注意:data
和 computed
属性在模板中引用相同,例如
<pre>{{parsedList}}</pre>
<pre>{{filteredJSONList}}</pre>
很容易认为 computed
属性(作为一个方法)需要被调用...它不需要
单向数据流, 根据 https://vuejs.org/v2/guide/components.html,组件遵循单向 数据流, 所有 props 在子 属性 和父 属性 之间形成单向向下绑定,当父 属性 更新时,它会向下流向子而不是相反,这防止了子意外改变父组件,这会使您的应用程序的数据流更难理解。
此外,每次父组件更新所有道具 子组件中的 将使用最新值进行刷新。这意味着您不应该尝试改变子组件内的道具。如果你这样做,.vue 会在 控制台。
通常有两种情况很容易改变道具: prop 用于传入一个初始值;子组件之后想将其用作本地数据 属性。 该道具作为需要转换的原始值传入。 这些用例的正确答案是: 定义一个本地数据属性,使用prop的初始值作为其初始值:
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
定义一个计算 属性,它是根据 prop 的值计算的:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
根据 VueJs 2.0,你不应该改变组件内部的 prop。它们仅由它们的 parent 变异。因此,您应该在数据中定义不同名称的变量,并通过观察实际道具来保持它们的更新。 如果列表属性被 parent 更改,您可以解析它并将其分配给 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.if 中更改道具,您需要像这样更改它设置一个新的 属性:
data () {
return () {
listClone: this.list
}
}
并更改 listClone 的值。
Vue 模式 props
向下,events
向上。听起来很简单,但是写自定义组件的时候很容易忘记。
从 Vue 2.2.0 开始,您可以使用 v-model (with 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
computed 属性 在组件中提供了一个简单的 getter 和 setter 接口(将其视为私有变量)。在 #task-template
中,您可以渲染 listLocal
并且它将保持反应性(即,如果 parentList
发生变化,它将更新 Task
组件)。您还可以通过调用 setter(例如 this.listLocal = newList
)来改变 listLocal
,它会将更改发送给父级。
此模式的优点在于您可以将 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
.
除上述问题外,其他人有以下问题:
"If the props value is not required and thus not always returned, the passed data would return undefined
(instead of empty)"。这可能会弄乱 <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.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.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);
}
}
});
如果未定义,这样您至少会在 mutableList
中得到一个空对象而不是 JSON.parse 错误。
我想给出这个答案,这有助于避免使用大量代码、观察者和计算属性。在某些情况下,这可能是一个很好的解决方案:
道具旨在提供单向交流。
当你有一个带道具的模态 show/hide
按钮时,对我来说最好的解决方案是发出一个事件:
<button @click="$emit('close')">Close Modal</button>
然后将侦听器添加到模态元素:
<modal :show="show" @close="show = false"></modal>
(在这种情况下,道具 show
可能是不必要的,因为您可以直接在基本模态上使用简单的 v-if="show"
)
我个人总是建议,如果你需要改变道具,首先将它们传递给计算的 属性 和 return 从那里,然后可以很容易地改变道具,即使你可以跟踪 prop 突变,如果它们也从另一个组件发生突变,或者我们也可以观看。
因为 Vue props 是数据流的一种方式,这可以防止子组件意外改变父组件的状态。
从Vue官方文档中,我们会找到2种方法来解决这个问题
如果子组件要使用props作为本地数据,最好定义一个本地数据属性.
props: ['list'], data: function() { return { localList: JSON.parse(this.list); } }
prop 作为需要转换的原始值传入。在这种情况下,最好使用 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 }
答案很简单,您应该通过将值分配给一些局部组件变量(可以是数据 属性,使用 getter、setter 或 watchers 计算)来打破直接 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))将被价值观察者观察并分配给输入变量,一旦接收到输入,我们可以捕获该动作并向父级发出输入,表明数据已输入来自表单元素。
是的!,在 vue2 中改变属性是反模式的。但...
只需使用其他规则打破规则,然后继续前进!
您需要的是将 .sync 修饰符添加到 paret 范围内的组件属性。
<your-awesome-components :custom-attribute-as-prob.sync="value" />
我们杀死蝙蝠侠很简单
当 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;
}
下面是一个小吃店组件,当我像这样将 snackbar 变量直接赋给 v-model 时,如果可以,但在控制台中,它会给出一个错误
避免直接修改 prop,因为只要父组件重新渲染,该值就会被覆盖。相反,使用数据或根据 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>
这对我有用
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 和 Emitting 更新事件,并计算 属性 getter setter,但这是一项艰巨的工作,我没有喜欢努力工作。
一个潜在的解决方案是使用全局变量。
import { Vue } from "nuxt-property-decorator";
export const globalStore = new Vue({
data: {
list: [],
},
}
export function setupGlobalsStore() {
Vue.prototype.$globals = globalStore;
}
那么你会使用:
$globals.list
任何你需要改变它或呈现它的地方。
将道具分配给新变量。
data () {
return {
listClone: this.list
}
}
你应该始终避免在 vue 或任何其他框架中改变 props。您可以采取的方法是将其复制到另一个变量中。
例如。
// instead of replacing the value of this.list use a different variable
this.new_data_variable = JSON.parse(this.list)