Jaki jest właściwy sposób przekazywania rekwizytów jako danych początkowych w Vue.js 2?


101

Chcę więc przekazać właściwości do komponentu Vue, ale spodziewam się, że te właściwości zmienią się w przyszłości od wewnątrz tego komponentu, np. Kiedy zaktualizuję ten komponent Vue od wewnątrz za pomocą AJAX. Więc służą tylko do inicjalizacji komponentu.

Mój cars-listelement komponentu Vue, w którym przekazuję rekwizyty z właściwościami początkowymi do 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>

Sposób, w jaki to zrobić teraz to, że w moim single-carskładnika jestem przypisanie this.initialPropertiesdo mojego this.data.propertiesna created()haku inicjalizacji. Działa i jest reaktywny.

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

Ale mój problem z tym polega na tym, że nie wiem, czy to jest właściwy sposób? Czy to nie sprawi mi kłopotów na drodze? Czy jest na to lepszy sposób?


13
Jest to najbardziej mylące rzeczą Vue moim zdaniem: Każdy datajest two-wayzwiązana, ale nie można przejść datado części składowych, można przejść props, ale nie można zmienić odebrany propsani przekonwertować propsdo data. Więc co? Nauczyłem się, że należy przekazywać propsi wywoływać zdarzenia. Oznacza to, że jeśli komponent chce zmienić propsotrzymaną wiadomość, powinien wywołać zdarzenie i zostać „ponownie wyrenderowany”. Ale wtedy zostaje ci one-waywiązanie dokładnie takie jak React i nie widzę wtedy zastosowania data. Dość zagmatwane.
André Pena

1
Dane są przeznaczone przede wszystkim do prywatnego użytku komponentu. Wszystko, co jest na nim umieszczone w kontekście komponentu, jest reaktywne i może być z nim związane. Koncepcja z właściwościami polega na przekazywaniu wartości do komponentu, ale uniemożliwić komponentowi ciche wprowadzanie zmian stanu do elementu nadrzędnego poprzez zmianę przekazanej wartości. Jak wskazałeś, lepiej jest to wyraźnie zaznaczyć w wydarzeniu. To była zmiana filozofii z Vue 1.0 na 2.0.
David K. Hess

Dzisiaj próbowałem założyć wątek tutaj: forum.vuejs.org/t/...
bgraves

Odpowiedzi:


109

Dzięki temu https://github.com/vuejs/vuejs.org/pull/567 już znam odpowiedź.

Metoda 1

Przekaż początkowy rekwizyt bezpośrednio do danych. Podobnie jak przykład w zaktualizowanych dokumentach:

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

Pamiętaj jednak, że jeśli przekazana właściwość jest obiektem lub tablicą, która jest używana w stanie komponentu nadrzędnego, każda modyfikacja tej właściwości spowoduje zmianę stanu tego komponentu nadrzędnego.

Ostrzeżenie : ta metoda nie jest zalecana. Sprawi, że twoje komponenty będą nieprzewidywalne. Jeśli chcesz ustawić dane nadrzędne z komponentów podrzędnych, użyj zarządzania stanem, takiego jak Vuex, lub użyj „v-model”. https://vuejs.org/v2/guide/components.html#Using-v-model-on-Components

Metoda 2

Jeśli twoją początkową właściwością jest obiekt lub tablica i jeśli nie chcesz, aby zmiany w stanie dzieci były przenoszone do stanu nadrzędnego, po prostu użyj np. Vue.util.extend[1], aby utworzyć kopię właściwości zamiast wskazywać ją bezpośrednio na dane potomne, na przykład:

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

Metoda 3

Możesz również użyć operatora rozprzestrzeniania, aby sklonować rekwizyty. Więcej szczegółów w odpowiedzi Igor: https://stackoverflow.com/a/51911118/3143704

Pamiętaj jednak, że operatory rozprzestrzeniania nie są obsługiwane w starszych przeglądarkach i dla lepszej kompatybilności musisz przetransponować kod, np babel. Używając .

Przypisy

[1] Pamiętaj, że jest to wewnętrzne narzędzie Vue i może się zmienić wraz z nowymi wersjami. Możesz użyć innych metod do skopiowania tej właściwości, zobacz „ Jak poprawnie sklonować obiekt JavaScript? ”.

Moje skrzypce, na których testowałem: https://jsfiddle.net/sm4kx7p9/3/


1
więc czy propagowanie zmian do komponentu nadrzędnego jest dobrą praktyką?
igor

1
@igor osobiście starałbym się tego unikać tak bardzo, jak tylko możesz. Sprawi, że twoje komponenty będą nieprzewidywalne. Myślę, że lepszym sposobem uzyskania zmian w komponencie nadrzędnym jest użycie metody "v-model", w której emitujesz zmiany z komponentu podrzędnego do komponentu nadrzędnego. vuejs.org/v2/guide/components.html#Using-v-model-on-Components
Dominik Serafin

tak, ale co z głębokimi obiektami? powinienem głęboko skopiować? czy powinienem przeprojektować moje komponenty, aby nie używały głębokiego obiektu? czyli rekwizyt z: { ok: 1, notOk: { meh: 2 } }jeśli ustawiono na dane wewnętrzne za pomocą dowolnej z poprzednich metod klonowania, nadal zmieniłby propnotOk.meh
Nikso

2
Dzięki za doskonałe pytanie i odpowiedź @DominikSerafin. To doskonale ilustruje uzdrowienie achillesa Vue.js. Jako framework tak dobrze radzi sobie z komponentowaniem, ale przekazywanie danych między kompozycjami jest głównym problemem PITA. Wystarczy spojrzeć na stworzony koszmar nomenklatury. Więc teraz muszę poprzedzić wszystkie moje rekwizyty, initialaby móc ich używać w mojej kompozycji. Byłoby o wiele czystsze, gdybyśmy mogli po prostu przekazać datawłaściwość wywołaną do dowolnego kompilacji i automagicznie propagować zlokalizowane dane bez całego tego initialPropNameszaleństwa.
AJB

1
@DominikSerafin Słyszę, co mówisz, i nie mylisz się. Ale napisz aplikację do tworzenia formularzy za pomocą Vue.js, a szybko zrozumiesz, o co mi chodzi. W rzeczywistości jestem w trakcie zmiany mojej techniki udostępniania danych z prop --> comp.datana używanie Vue-Stash dla wszystkich danych (lub Vuex też by działał). Ale teraz po prostu odbijam moje dane od windowobiektu, tak jak to robiłem, zanim „nowoczesne” frameworki JS narzuciły mi swoją opinię. Nasuwa się więc pytanie: po co w ogóle jest właściwość comp.data?
AJB

29

Wraz z odpowiedzią @ dominik-serafin:

Jeśli przekazujesz obiekt, możesz go łatwo sklonować za pomocą operatora spreadu (składnia ES6):

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

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

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

Ale najważniejsze jest, aby pamiętać o używaniu opt. 2 w przypadku, gdy przekazujesz wartość obliczoną lub więcej niż wartość asynchroniczną. W przeciwnym razie wartość lokalna nie zostanie zaktualizowana.

Próbny:

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/


Hej @ igor-parra, wygląda na to, że przesłałeś zduplikowane odpowiedzi. Warto też wspomnieć, że operator rozprzestrzeniania nie jest obsługiwany we wszystkich przeglądarkach i może wymagać kroku transpilacji dla lepszej kompatybilności. W przeciwnym razie wygląda świetnie - dzięki za dodanie tej alternatywnej metody!
Dominik Serafin

@ dominik-serafin Tak, masz rację, dodałem odniesienie do ES6. W aplikacjach opartych na webpacku tak łatwo jest mieć wstępnie skonfigurowany babel, aby w pełni wykorzystać nowoczesny javascript.
Igor Parra

recordLocal: [...this.record](w moim przypadku rekord jest tablicą) nie działał u mnie - po załadowaniu i sprawdzeniu komponentu vue, recordzawiera pozycje i recordLocalbyła pustą tablicą.
Christian

Kiedy używam go przez właściwość COMPUTED, idzie dobrze, dla mnie, gdy próbuję ustawić wewnątrz danych, nie działa = (... ale mam to używająccomputed
felipe muner

Bądź ostrożny. Operator rozpowszechniania tylko płytkie klony, więc w przypadku obiektów zawierających obiekty lub tablice nadal będziesz kopiować wskaźniki zamiast pobierać nową kopię. Używam cloneDeep z lodash.
Cindy Conway

14

Wierzę, że robisz to dobrze, ponieważ tak jest w dokumentach.

Zdefiniuj lokalną właściwość danych, która używa wartości początkowej właściwości jako wartości początkowej

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


4
W przypadku rekwizytów typu pass-by-value to jest w porządku. W tym przypadku jest to obiekt, więc zmiana będzie miała wpływ na rodzica. Z tej strony: „Zauważ, że obiekty i tablice w JavaScript są przekazywane przez referencje, więc jeśli właściwość jest tablicą lub obiektem, mutacja obiektu lub samej tablicy wewnątrz komponentu potomnego wpłynie na stan rodzica”.
Jake

To działa dla mnie i jest jasno udokumentowane. ta linia w powyższej odpowiedzi jest w konflikcie z dokumentacją vue, o ile wiem "Ostrzeżenie: ta metoda nie jest zalecana. Spowoduje to, że twoje komponenty będą nieprzewidywalne. Jeśli musisz ustawić dane nadrzędne dla komponentów podrzędnych, użyj zarządzania stanem, takiego jak Vuex, albo użyj „v-model”. ”
Nathaniel Rink

1
Tak więc po 4 latach i setkach godzin uczenia się wróciłem do miejsca, w którym zacząłem po prostu odbijać vary od windowobiektu.
AJB
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.