在 Vue.js2中将道具作为初始数据传递的正确方法是什么?

所以我想把道具传递给 Vue 组件,但是我希望这些道具在未来从该组件内部改变,例如当我使用 AJAX 从内部更新 Vue 组件时。因此它们仅用于组件的初始化。

我的 cars-list Vue 组件元素,其中我将具有初始属性的道具传递给 single-car:

// cars-list.vue


<script>
export default {
data: function() {
return {
cars: [
{
color: 'red',
maxSpeed: 200,
},
{
color: 'blue',
maxSpeed: 195,
},
]
}
},
}
</script>


<template>
<div>
<template v-for="car in cars">
<single-car :initial-properties="car"></single-car>
</template>
</div>
</template>

我现在做的方法是,在我的 single-car组件中,我将 this.initialProperties分配给 created()初始化钩子上的 this.data.properties。它起作用了,而且是反应性的。

// single-car.vue


<script>
export default {
data: function() {
return {
properties: {},
}
},
created: function(){
this.data.properties = this.initialProperties;
},
}
</script>


<template>
<div>Car is in {{properties.color}} and has a max speed of {{properties.maxSpeed}}</div>
</template>

但我的问题是我不知道这样做是否正确?这不会给我带来一些麻烦吗?还是有更好的办法?

130304 次浏览

我相信你这样做是对的,因为这就是文件中所说的。

定义一个本地数据属性,该属性使用道具的初始值作为其初始值

Https://vuejs.org/guide/components.html#one-way-data-flow

多亏了这个 https://github.com/vuejs/vuejs.org/pull/567,我现在知道答案了。

方法1

将初始道具直接传递给数据,如更新文档中的示例所示:

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

但要记住,如果传递的道具是在父组件状态中使用的对象或数组,对该道具的任何修改都将导致该父组件状态的更改。

警告 : 不建议使用此方法。它会让你的组件变得不可预测。如果需要从子组件设置父数据,可以使用 Vuex 之类的状态管理,或者使用“ v-model”。< a href = “ https://v2.vuejs.org/v2/guide/Component ents.html # using-v-model-on-Component”rel = “ nofollow norefrer”> https://v2.vuejs.org/v2/guide/components.html#using-v-model-on-components

方法2

如果你最初的道具是一个对象或数组,如果你不想让子状态的变化传播到父状态,那么只需要使用例如 Vue.util.extend[1]来复制道具,而不是直接指向子数据,像这样:

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

方法3

你也可以使用扩展操作符来克隆道具。更多的细节在 Igor 答案: https://stackoverflow.com/a/51911118/3143704

但是要记住,在旧的浏览器中扩展操作符不受支持,为了更好的兼容性,你需要移动代码,例如使用 babel

脚注

[1]请记住,这是一个内部 Vue 实用程序,它可能会随着新版本而改变。您可能需要使用其他方法来复制该道具,请参见 如何正确地克隆 JavaScript 对象?

我测试的小提琴: Https://jsfiddle.net/sm4kx7p9/3/

配合@dominik-serafin 的回答:

如果要传递对象,可以使用扩展运算符(ES6语法)轻松地克隆它:

 props: {
record: {
type: Object,
required: true
}
},


data () { // opt. 1
return {
recordLocal: {...this.record}
}
},


computed: { // opt. 2
recordLocal () {
return {...this.record}
}
},

但最重要的是要记住使用 opt。如果传递的是计算值,或者超过了异步值,则使用2。否则本地值将不会更新。

演示:

Vue.component('card', {
template: '#app2',
props: {
test1: null,
test2: null
},
data () { // opt. 1
return {
test1AsData: {...this.test1}
}
},
computed: { // opt. 2
test2AsComputed () {
return {...this.test2}
}
}
})


new Vue({
el: "#app1",
data () {
return {
test1: {1: 'will not update'},
test2: {2: 'will update after 1 second'}
}
},
mounted () {
setTimeout(() => {
this.test1 = {1: 'updated!'}
this.test2 = {2: 'updated!'}
}, 1000)
}
})
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>


<div id="app1">
<card :test1="test1" :test2="test2"></card>
</div>


<template id="app2">
<div>
test1 as data: \{\{test1AsData}}
<hr />
test2 as computed: \{\{test2AsComputed}}
</div>
</template>

Https://jsfiddle.net/nomikos3/eywraw8t/281070/

作为另一种方法,我是通过子组件中的观察者来完成的。

这种方法非常有用,特别是在传递异步值时,以及在子组件中希望将传递的值绑定到 v-model 时。

另外,为了使其具有反应性,我在另一个观察器中向父级发出本地值。

例如:

  data() {
return {
properties: {},
};
},
props: {
initial-properties: {
type: Object,
default: {},
},
},
watch: {
initial-properties: function(newVal) {
this.properties = {...newVal};
},
properties: function(newVal) {
this.$emit('propertiesUpdated', newVal);
},
},

这样我就有了更多的控制力,也少了意想不到的行为。例如,当父级传递的道具是异步的时候,它可能在创建或挂载生命周期时不可用。因此,您可以像@Igor-Parra 提到的那样使用计算属性,或者观察道具,然后发出它。

第二次或第三次,我遇到这个问题,回到一个旧的价值项目。

不知道为什么它的价值如此复杂,但我们可以通过 看好了:

export default {


props: ["username"],


data () {
return {
usernameForLabel: "",
}
},


watch: {
username: {
immediate: true,
handler (newVal, oldVal) {
this.usernameForLabel = newVal;
}
},
},

接下来是辛迪对另一个答案的评论:

小心。扩展操作符只是浅克隆,对象也是如此 包含对象或数组时,您仍将复制指针,而不是复制 买一本新的。

的确如此。即使使用扩展运算符,数组内对象内的更改仍然会传播到组件。

以下是我的解决方案(使用组合 API) :

setup() {
properties = ref([])


onMounted(() => {
properties.value = props.initialProperties.map((obj) => ({ ...obj }));
})
}

这样可以设置值并防止它们被更改,即使数据在父组件中被更改。