Apa cara yang benar untuk melewatkan props sebagai data awal di Vue.js 2?

101

Jadi saya ingin meneruskan props ke sebuah komponen Vue, tapi saya berharap props ini berubah di masa depan dari dalam komponen itu misalnya ketika saya mengupdate komponen Vue dari dalam menggunakan AJAX. Jadi mereka hanya untuk inisialisasi komponen.

cars-listElemen komponen Vue saya tempat saya meneruskan props dengan properti awal ke 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>

Cara saya melakukannya sekarang ini bahwa di dalam saya single-carkomponen Aku menugaskan this.initialPropertieske saya this.data.propertiesdi created()inisialisasi kail. Dan itu bekerja dan reaktif.

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

Tapi masalah saya dengan itu adalah saya tidak tahu apakah itu cara yang benar untuk melakukannya? Tidakkah itu akan membuat saya mengalami beberapa masalah di sepanjang jalan? Atau apakah ada cara yang lebih baik untuk melakukannya?

Dominik Serafin
sumber
13
Ini adalah hal yang membingungkan yang paling tentang Vue menurut saya: Setiap datayang two-wayterikat, tetapi Anda tidak bisa lewat datake komponen, Anda lulus props, tetapi Anda tidak dapat mengubah diterima propsatau mengkonversi propske data. Lalu apa? Satu hal yang saya pelajari adalah Anda harus propsmenurunkan dan memicu peristiwa. Artinya, jika komponen ingin mengubah propspenerimaannya, itu harus memanggil peristiwa dan "dirender". Tapi kemudian Anda pergi dengan one-waypengikatan persis seperti React dan saya tidak melihat gunanya untuk dataitu. Cukup membingungkan.
André Pena
1
Data terutama ditujukan untuk penggunaan pribadi komponen. Segala sesuatu yang ditempatkan di dalamnya dalam konteks komponen bersifat reaktif dan dapat terikat. Konsep dengan props adalah meneruskan nilai ke dalam sebuah komponen tetapi menjaga agar komponen tersebut tidak dapat secara diam-diam memperkenalkan perubahan status pada induknya dengan mengubah nilai yang diteruskan. Lebih baik membuatnya eksplisit dalam acara seperti yang Anda tunjukkan. Ini adalah perubahan filosofi dari Vue 1.0 ke 2.0.
David K. Hess
Hari ini saya telah mencoba untuk memulai utas di sini: forum.vuejs.org/t/…
bgraves

Jawaban:

109

Berkat https://github.com/vuejs/vuejs.org/pull/567 ini saya tahu jawabannya sekarang.

Metode 1

Meneruskan prop awal langsung ke data. Seperti contoh di dokumen yang diperbarui:

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

Namun perlu diingat jika prop yang diteruskan adalah objek atau larik yang digunakan dalam status komponen induk, modifikasi apa pun pada prop itu akan mengakibatkan perubahan dalam status komponen induk tersebut.

Peringatan : metode ini tidak disarankan. Ini akan membuat komponen Anda tidak dapat diprediksi. Jika Anda perlu menyetel data induk dari komponen anak baik gunakan manajemen negara seperti Vuex atau gunakan "v-model". https://vuejs.org/v2/guide/components.html#Using-v-model-on-Components

Metode 2

Jika prop awal Anda adalah sebuah objek atau larik dan jika Anda tidak ingin perubahan dalam status anak disebarkan ke status induk maka cukup gunakan eg Vue.util.extend[1] untuk membuat salinan dari props alih-alih mengarahkannya langsung ke data anak, seperti ini:

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

Metode 3

Anda juga dapat menggunakan operator penyebaran untuk mengkloning alat peraga. Detail lebih lanjut di jawaban Igor: https://stackoverflow.com/a/51911118/3143704

Namun perlu diingat bahwa operator penyebaran tidak didukung di browser lama dan untuk kompatibilitas yang lebih baik Anda perlu mentranspilasi kode, misalnya menggunakan babel.

Catatan kaki

[1] Ingatlah bahwa ini adalah utilitas Vue internal dan dapat berubah dengan versi baru. Anda mungkin ingin menggunakan metode lain untuk menyalin properti itu, lihat " Bagaimana cara saya mengkloning objek JavaScript dengan benar? ".

Biola saya tempat saya mengujinya: https://jsfiddle.net/sm4kx7p9/3/

Dominik Serafin
sumber
1
jadi ok untuk mempraktikkan perubahan yang disebarkan ke komponen induk?
igor
1
@igor secara pribadi saya akan mencoba menghindarinya sebanyak yang Anda bisa. Ini akan membuat komponen Anda tidak dapat diprediksi. Saya pikir cara yang lebih baik untuk mendapatkan perubahan dalam komponen induk adalah dengan menggunakan metode "v-model" di mana Anda $ mengeluarkan perubahan dari komponen anak Anda ke komponen induk. vuejs.org/v2/guide/components.html#Using-v-model-on-Components
Dominik Serafin
ya tapi bagaimana dengan benda dalam? haruskah saya menyalin dalam? haruskah saya mendesain ulang komponen saya agar tidak menggunakan objek dalam? yaitu prop dengan: { ok: 1, notOk: { meh: 2 } }jika disetel ke data internal dengan salah satu metode kloning sebelumnya masih akan mengubah propnotOk.meh
Nikso
2
Terima kasih atas pertanyaan dan jawaban yang luar biasa @DominikSerafin. Ini dengan sempurna menggambarkan penyembuhan achilles dari Vue.js. Sebagai framework, ia menangani komponenisasi dengan sangat baik, tetapi meneruskan data di antara comps adalah PITA utama. Lihat saja mimpi buruk nomenklatur yang dibuat. Jadi sekarang saya perlu mengawali semua props saya dengan initialhanya untuk dapat menggunakannya dalam comp saya. Akan jauh lebih bersih jika kita bisa melewatkan prop yang dipanggil datake comp mana pun dan membuatnya secara otomatis menyebarkan data yang dilokalkan di dalamnya tanpa semua initialPropNamekegilaan ini .
AJB
1
@DominikSerafin Saya mendengar apa yang Anda katakan, dan Anda tidak salah. Tapi tulislah aplikasi pembuat formulir menggunakan Vue.js dan Anda akan segera mengerti maksud saya. Saya sebenarnya sedang dalam proses mengubah teknik berbagi data saya dari prop --> comp.datamenggunakan Vue-Stash untuk semua data (atau Vuex juga akan berfungsi). Tapi sekarang saya hanya memantulkan data saya dari windowobjek seperti yang biasa saya lakukan sebelum framework JS "modern" memberlakukan opini mereka pada saya. Jadi ini menimbulkan pertanyaan: Apa gunanya properti comp.data?
AJB
29

Dalam pendamping jawaban @ dominik-serafin:

Jika Anda mengirimkan objek, Anda dapat dengan mudah mengkloningnya menggunakan operator penyebaran (Sintaks ES6):

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

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

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

Tapi yang paling penting adalah ingat untuk menggunakan opt. 2 jika Anda meneruskan nilai yang dihitung, atau lebih dari itu nilai asynchronous. Jika tidak, nilai lokal tidak akan diperbarui.

Demo:

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>

https://jsfiddle.net/nomikos3/eywraw8t/281070/

Igor Parra
sumber
Hai @ igor-parra, sepertinya Anda mengirimkan jawaban duplikat. Dan juga mungkin bagus untuk menyebutkan bahwa operator penyebaran tidak didukung di semua browser dan mungkin memerlukan langkah transpiling untuk kompatibilitas yang lebih baik. Kalau tidak, tampilannya bagus - terima kasih telah menambahkan metode alternatif ini!
Dominik Serafin
@ dominik-serafin Ya, benar, saya menambahkan referensi ke ES6. Dalam aplikasi berbasis webpack, sangat mudah untuk memiliki babel yang telah dikonfigurasi sebelumnya untuk sepenuhnya menggunakan javascript modern.
Igor Parra
recordLocal: [...this.record](dalam catatan kasus saya adalah array) tidak berfungsi untuk saya - setelah memuat dan memeriksa komponen vue, recordberisi item dan recordLocalmerupakan array kosong.
Christian
Ketika saya menggunakannya melalui properti COMPUTED, itu berjalan dengan baik, bagi saya ketika saya mencoba untuk mengatur di dalam data itu tidak berfungsi = (... Tapi saya mendapatkannya menggunakancomputed
felipe muner
Hati-hati. Operator penyebaran hanya klon dangkal, jadi untuk objek yang berisi objek atau array Anda masih akan menyalin pointer alih-alih mendapatkan salinan baru. Saya menggunakan cloneDeep dari lodash.
Cindy Conway
14

Saya yakin Anda melakukannya dengan benar karena itulah yang dinyatakan dalam dokumen.

Tentukan properti data lokal yang menggunakan nilai awal prop sebagai nilai awalnya

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

jonan.pineda
sumber
4
Untuk alat peraga pass-by-value itu bagus. Dalam hal ini itu adalah objek, jadi mengubahnya akan mempengaruhi induknya. Dari halaman itu: "Perhatikan bahwa objek dan larik di JavaScript diteruskan oleh referensi, jadi jika prop adalah larik atau objek, mutasi objek atau larik itu sendiri di dalam komponen anak akan memengaruhi status induk."
Jake
Ini bekerja untuk saya dan didokumentasikan dengan jelas. baris ini dalam jawaban di atas bertentangan dengan dokumentasi vue sejauh yang saya tahu "Peringatan: metode ini tidak disarankan. Ini akan membuat komponen Anda tidak dapat diprediksi. Jika Anda perlu menyetel data induk dari komponen turunan, gunakan manajemen status seperti Vuex atau gunakan "v-model". "
Nathaniel Rink
2
Jadi setelah 4 tahun dan ratusan jam kurva pembelajaran, saya kembali ke tempat saya mulai memantulkan vars dari windowobjek.
AJB