在 Vue.js 2 中将道具作为初始数据传递的正确方法是什么?
What's the correct way to pass props as initial data in Vue.js 2?
所以我想将 props 传递给 Vue 组件,但我希望这些 props 将来会从该组件内部发生变化,例如当我使用 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
组件中,我在 created()
初始化挂钩上将 this.initialProperties
分配给 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>
但我的问题是我不知道这样做是否正确?不会给我路上添麻烦吧?或者有更好的方法吗?
我相信你做的是对的,因为这是文档中的说明。
Define a local data property that uses the prop’s initial value as its initial value
感谢这个 https://github.com/vuejs/vuejs.org/pull/567 我现在知道答案了。
方法一
将初始属性直接传递给数据。就像更新文档中的示例一样:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
但请记住,如果传递的 prop 是在父组件状态中使用的对象或数组,对该 prop 的任何修改都会导致该父组件状态发生变化。
警告:不推荐使用此方法。它将使您的组件不可预测。如果您需要从子组件设置父数据,请使用 Vuex 之类的状态管理或使用 "v-model"。 https://vuejs.org/v2/guide/components.html#Using-v-model-on-Components
方法二
如果您的初始道具是一个对象或数组,并且您不希望子状态的更改传播到父状态,那么只需使用例如Vue.util.extend
[1] 复制 props 而不是直接指向子数据,像这样:
props: ['initialCounter'],
data: function () {
return {
counter: Vue.util.extend({}, this.initialCounter)
}
}
方法三
您还可以使用扩展运算符来克隆道具。 Igor 回答中的更多详细信息:
但请记住,旧版浏览器不支持扩展运算符,为了更好的兼容性,您需要转换代码,例如使用 babel
.
脚注
[1] 请记住,这是一个内部 Vue 实用程序,它可能会随着新版本的变化而变化。您可能想使用其他方法复制该道具,请参阅“How do I correctly clone a JavaScript object?”。
我的 fiddle 我测试它的地方:
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>
作为另一种方法,我通过子组件中的观察者来完成。
这种方式很有用,特别是当你传递一个异步值,并且在你的子组件中你想将传递的值绑定到 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);
},
},
这样我就有了更多的控制权,也减少了意外行为。例如,当父级传递的 props 是异步的时,它可能在 created 或 mounted 生命周期时不可用。因此,您可以使用 @Igor-Parra 提到的计算 属性,或者观看道具然后发出它。
我第二次或第三次 运行 回到一个旧的 vue 项目来解决这个问题。
不知道为什么它在 vue 中如此复杂,但我们可以通过 watch:
来完成
export default {
props: ["username"],
data () {
return {
usernameForLabel: "",
}
},
watch: {
username: {
immediate: true,
handler (newVal, oldVal) {
this.usernameForLabel = newVal;
}
},
},
所以我想将 props 传递给 Vue 组件,但我希望这些 props 将来会从该组件内部发生变化,例如当我使用 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
组件中,我在 created()
初始化挂钩上将 this.initialProperties
分配给 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>
但我的问题是我不知道这样做是否正确?不会给我路上添麻烦吧?或者有更好的方法吗?
我相信你做的是对的,因为这是文档中的说明。
Define a local data property that uses the prop’s initial value as its initial value
感谢这个 https://github.com/vuejs/vuejs.org/pull/567 我现在知道答案了。
方法一
将初始属性直接传递给数据。就像更新文档中的示例一样:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
但请记住,如果传递的 prop 是在父组件状态中使用的对象或数组,对该 prop 的任何修改都会导致该父组件状态发生变化。
警告:不推荐使用此方法。它将使您的组件不可预测。如果您需要从子组件设置父数据,请使用 Vuex 之类的状态管理或使用 "v-model"。 https://vuejs.org/v2/guide/components.html#Using-v-model-on-Components
方法二
如果您的初始道具是一个对象或数组,并且您不希望子状态的更改传播到父状态,那么只需使用例如Vue.util.extend
[1] 复制 props 而不是直接指向子数据,像这样:
props: ['initialCounter'],
data: function () {
return {
counter: Vue.util.extend({}, this.initialCounter)
}
}
方法三
您还可以使用扩展运算符来克隆道具。 Igor 回答中的更多详细信息:
但请记住,旧版浏览器不支持扩展运算符,为了更好的兼容性,您需要转换代码,例如使用 babel
.
脚注
[1] 请记住,这是一个内部 Vue 实用程序,它可能会随着新版本的变化而变化。您可能想使用其他方法复制该道具,请参阅“How do I correctly clone a JavaScript object?”。
我的 fiddle 我测试它的地方: 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>
作为另一种方法,我通过子组件中的观察者来完成。
这种方式很有用,特别是当你传递一个异步值,并且在你的子组件中你想将传递的值绑定到 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);
},
},
这样我就有了更多的控制权,也减少了意外行为。例如,当父级传递的 props 是异步的时,它可能在 created 或 mounted 生命周期时不可用。因此,您可以使用 @Igor-Parra 提到的计算 属性,或者观看道具然后发出它。
我第二次或第三次 运行 回到一个旧的 vue 项目来解决这个问题。
不知道为什么它在 vue 中如此复杂,但我们可以通过 watch:
来完成export default {
props: ["username"],
data () {
return {
usernameForLabel: "",
}
},
watch: {
username: {
immediate: true,
handler (newVal, oldVal) {
this.usernameForLabel = newVal;
}
},
},