在 Vue.js 2 中将 props 作为初始数据传递的正确方法是什么?方法 1方法 2方法 3脚注

2022-08-30 04:16:43

所以我想把 props 传递给一个 Vue 组件,但是我希望这些 props 将来会从该组件内部发生变化,例如,当我使用 AJAX 从内部更新该 Vue 组件时。因此,它们仅用于组件的初始化。

我的 Vue 组件元素,其中我将具有初始属性的 prop 传递给 :cars-listsingle-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-carthis.initialPropertiesthis.data.propertiescreated()

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

但我的问题是,我不知道这是否是正确的方法?在路上不会给我带来一些麻烦吗?还是有更好的方法来做到这一点?


答案 1

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

方法 1

将初始属性直接传递给数据。例如更新后文档中的示例:

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

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

警告:不建议使用此方法。这将使您的组件不可预测。如果需要从子组件设置父数据,请使用 Vuex 等状态管理或使用“v-model”。https://v2.vuejs.org/v2/guide/components.html#Using-v-model-on-Components

方法 2

如果你的初始 prop 是一个对象或数组,并且如果你不希望子状态的更改传播到父状态,那么只需使用例如 [1]制作道具的副本,而不是直接指向子数据,如下所示:Vue.util.extend

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

方法 3

您还可以使用传播运算符来克隆道具。伊戈尔答案中的更多细节:https://stackoverflow.com/a/51911118/3143704

但请记住,较旧的浏览器不支持传播运算符,为了获得更好的兼容性,您需要转译代码,例如使用.babel

脚注

[1] 请记住,这是一个内部的 Vue 实用程序,它可能会随着新版本的发布而改变。您可能希望使用其他方法来复制该道具,请参阅“https://stackoverflow.com/questions/728360/how-do-i-correctly-clone-a-javascript-object”。

我的小提琴在我测试的地方:https://jsfiddle.net/sm4kx7p9/3/


答案 2

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