جاوااسکریپت - راه درست برای ارسال props به عنوان داده اولیه در Vue.js 2 چیست؟
پس میخواهم props را به یک کامپوننت Vue منتقل کنم، اما انتظار دارم این props در آینده از داخل همان کامپوننت تغییر کنند، مثلاً وقتی که آن کامپوننت Vue را از داخل با استفاده از AJAX بهروزرسانی میکنم. بنابراین آنها فقط برای مقداردهی اولیه کامپوننت هستند.
عنصر کامپوننت Vue من cars-list جایی که خواص را با ویژگیهای اولیه به 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>
اما مشکل من با این است که نمیدانم آیا این روش درست برای انجام آن است؟ آیا باعث ایجاد مشکلاتی برای من در طول مسیر نمیشود؟ یا راه بهتری برای انجام آن وجود دارد؟
پاسخ شماره ۱
به لطف این https://github.com/vuejs/vuejs.org/pull/567 حالا جواب را میدانم.
روش ۱
ویژگی اولیه را مستقیماً به دادهها منتقل کنید. مانند مثال در اسناد بهروز شده:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
اما به خاطر داشته باشید که اگر پراپی که منتقل شده یک شی یا آرایه باشد که در وضعیت (state) کامپوننت والد استفاده میشود، هر تغییری در آن پراپ منجر به تغییر در وضعیت آن کامپوننت والد خواهد شد.
هشدار: این روش توصیه نمیشود. این کار باعث غیرقابل پیشبینی شدن کامپوننتهای شما خواهد شد. اگر نیاز دارید دادههای والد را از کامپوننتهای فرزند تنظیم کنید، یا از مدیریت وضعیت مانند Vuex استفاده کنید یا از "v-model". https://v2.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)
}
}
روش ۳
شما همچنین میتوانید از عملگر اسپرد برای کلون کردن پراپسها استفاده کنید. جزئیات بیشتر در پاسخ ایگور: https://stackoverflow.com/a/51911118/3143704
اما به یاد داشته باشید که عملگرهای گسترش در مرورگرهای قدیمی پشتیبانی نمیشوند و برای سازگاری بهتر نیاز است کد را مثلاً با استفاده از babel ترنسپایل کنید.
پاورقیها
[1] به خاطر داشته باشید که این یک ابزار داخلی Vue است و ممکن است با نسخههای جدید تغییر کند. ممکن است بخواهید از روشهای دیگری برای کپی کردن آن prop استفاده کنید، ببینید چگونه میتوانم یک شیء JavaScript را به درستی کلون کنم؟
فیدل من جایی که داشتم آن را تست میکردم: 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}
}
},
اما مهمترین نکته این است که به یاد داشته باشید در صورتی که مقدار محاسبهشدهای را ارسال میکنید، یا حتی بیشتر از آن، یک مقدار ناهمگام، از گزینه ۲ استفاده کنید. وگرنه مقدار محلی بهروزرسانی نخواهد شد.
نمایش:
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 /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/
پاسخ شماره ۳
من معتقد هستم که شما این کار را درست انجام میدهید زیرا این همان چیزی است که در مدارک ذکر شده است.
یک ویژگی داده محلی تعریف کنید که از مقدار اولیه prop به عنوان مقدار اولیه خود استفاده کند
https://vuejs.org/guide/components.html#One-Way-Data-Flow
