Каков правильный способ передачи реквизита в качестве исходных данных в Vue.js 2?

Поэтому я хочу передать реквизиты компоненту Vue, но я ожидаю, что эти реквизиты будут меняться в будущем изнутри этого компонента, например, когда я обновляю этот компонент Vue изнутри с помощью AJAX. Таким образом, они предназначены только для инициализации компонента.

Мой 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 свой this.data.properties на created() инициализационном крючке. И он работает и реагирует.

// 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 является объектом или массивом, который используется в состоянии родительского компонента, любая модификация этой опоры приведет к изменению состояния этого родительского компонента.

Предупреждение: этот метод не рекомендуется. Это сделает ваши компоненты непредсказуемыми. Если вам необходимо установить родительские данные из дочерних компонентов, используйте либо управление состоянием, как Vuex, либо используйте "v-model". https://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

Вы можете также использовать оператор спреда для клонирования реквизита. Подробнее в ответе Игоря: https://stackoverflow.com/a/51911118/3143704

Но имейте в виду, что операторы распространения не поддерживаются в старых браузерах, и для лучшей совместимости вам необходимо перевести код, например, с помощью babel.

Сноски

[1] Имейте в виду, что это внутренняя утилита Vue, и она может меняться с новыми версиями. Возможно, вы захотите использовать другие методы для копирования этой опоры, см. " Как правильно клонировать объект JavaScript? ".

Моя скрипка, где я ее тестировал: https://jsfiddle.net/sm4kx7p9/3/

Ответ 2

Я считаю, что вы делаете это правильно, потому что это указано в документах.

Определите свойство локальных данных, которое использует начальное значение реквизита в качестве его начального значения

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

Ответ 3

В компаньоне @dominik-serafin ответ:

Если вы передаете объект, вы можете легко клонировать его с помощью оператора спредов (ES6):

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

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

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

Но самое главное - не забыть использовать опт. 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/[email protected]/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>