134

So I want to pass props to an Vue component, but I expect these props to change in future from inside that component e.g. when I update that Vue component from inside using AJAX. So they are only for initialization of component.

My cars-list Vue component element where I pass props with initial properties to 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>

The way I do it right now it that inside my single-car component I'm assigning this.initialProperties to my this.data.properties on created() initialization hook. And it works and is reactive.

// 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>

But my problem with that is that I don't know if that's a correct way to do it? Won't it cause me some troubles along the road? Or is there a better way to do it?

4

5 回答 5

141

感谢这个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: ['initialCounter'],
data: function () {
    return {
        counter: Vue.util.extend({}, this.initialCounter)
    }
}

方法三

您还可以使用扩展运算符来克隆道具。Igor 答案中的更多详细信息:https ://stackoverflow.com/a/51911118/3143704

但请记住,旧浏览器不支持扩展运算符,为了更好的兼容性,您需要转换代码,例如使用babel.

脚注

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

我在哪里测试它的小提琴: https ://jsfiddle.net/sm4kx7p9/3/

于 2016-11-05T07:31:18.287 回答
38

伴随@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/

于 2018-08-18T17:37:58.950 回答
16

我相信您做对了,因为这是文档中所述的内容。

定义一个使用 prop 的初始值作为其初始值的本地数据属性

https://vuejs.org/guide/components.html#One-Way-Data-Flow

于 2016-11-04T10:23:54.243 回答
3

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

不知道为什么它在 vue 中如此复杂,但我们可以通过watch来完成:

export default {

  props: ["username"],

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

  watch: {
    username: {
      immediate: true,
      handler (newVal, oldVal) {
        this.usernameForLabel = newVal;
      }
    },
  },
于 2021-05-10T13:57:01.240 回答
1

就像另一种方法一样,我通过子组件中的观察者来做到这一点。

这种方式很有用,特别是当您传递异步值时,并且在您的子组件中您希望将传递的值绑定到 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 是异步的时,它可能在创建或挂载生命周期时不可用。所以你可以像@Igor-Parra 提到的那样使用计算属性,或者观察道具然后发出它。

于 2021-02-09T11:36:42.887 回答