Каков правильный способ передачи реквизита в качестве исходных данных в 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>