Vue 2 Aktualizace Vlastností na Objekt v Poli z Vnořené Komponenty

0

Otázka

Pracoval jsem na Vue 2 projekt na chvíli, a na modernizaci naší linting požadavky zjistil jsem, že jsme měli prop mutace chyby v mnoha našich podřízené součásti. V našem projektu jsme se projít singleton objekt jako rekvizita do mnoha komponent a byly původně aktualizaci objektu přímo z podřízené součásti. Vue zdá se, naznačují, pomocí v-bind.sync funkce pro aktualizaci props z dětské složky (nebo pomocí ekvivalentní v-bind a v-on). To však neřeší problém prop modifikace z vnořené komponenty v matici.

Take tento (pseudo)kód, například, že používá prop mutace:

Poznámka: Předpokládejme, const sharedObject: { arrayElements: Array<{ isSelected: boolean }> } = ...

Stránka.vue

<template>
  ...
  <Component1 :input1="sharedObject" />
  ...
</template>

Component1.vue

<template>
  ...
  <template v-for="elem in sharedObject.arrayElements">
    <Component2 :input2="elem" />
  </template>
  ...
</template>

Component2.vue

<template>
  ...
  <q-btn @click="input2.isSelected = !input2.isSelected"></q-btn>
  ...
</template>

Jaký je správný způsob, jak aktualizovat vlastnost, jako input2.isSelected z vnořené komponenty v Vue 2? Všechny přístupy jsem si myslel, že jsou chybné.

Chybné Přístupy

Jsem přesvědčen , že bychom chtěli, aby bublina, že input2.isSelected byl upraven v Component2 k Page.vue, nicméně, to se zdá buď vést k chaotický kód nebo nepříjemný pocit, že jsme jen potlačení linting chyby v oklikou.


K prokázání "chaotický kód" přístup, nejprve na vědomí, že Page.vue neví index na elem v sharedObject.arrayElements. Proto, budeme muset vydávat objektu Page.vue z Component1 který obsahuje stav input2.isSelected stejně indexu elem v sharedObject.arrayElements. To dostane chaotický rychle. Co o příklad, kdy máme:

Component1.vue

<template>
  ...
  <template v-for="elem in sharedObject.arrayElements">
    <template v-for="elem2 in elem.arrayElements">
       <Component2 :input2="elem2" />
    </template>
  </template>
  ...
</template>

v tomto případě, pak bychom se mohli třeba projít až 2 indexy. To nevypadá jako udržitelné řešení pro mě.


Alternativní, že jsem si myslel, ze je funkce zpětného volání (předán jako rekvizita přes hierarchii komponentů), který bere jako vstupní element, který chceme aktualizovat a objekt, který obsahuje vlastnosti chceme aktualizovat (pomocí Object.assign).

To je pro mě velmi nepříjemný, protože já nevím pravý důvod, proč jsme nelze aktualizovat pass-by-reference prop z dětské složky. Pro mě, zdá se, je to jen oklikou aktualizace prošel-z Component2 bez linter povšimnutí. Pokud tam je nějaké kouzlo modifikace, která se stane rekvizity, když jsou předány dílčí součásti, pak jistě přecházející v objekt, který jsem obdržel v Component2 na zpětné volání funkce a úpravy v nadřazené součásti by v podstatě jen aktualizace vrtule v dětské složky, ale složitější.

Jaký je správný způsob, jak se k tomuto problému v Vue 2?

1

Nejlepší odpověď

3

Velmi dobrá otázka a analýza současného stavu to dlouhodobý problém v Vue ekosystému.

Ano, změnou "typu hodnoty" rekvizity z dítěte je problém, protože to vytváří runtime problémy (rodiče přepsání změn, když re-vykreslen), a tak Vue generuje runtime error, když se to stane...

Změna vlastnictví objekt předán jako rekvizita, je to OK "kód funguje" POV. Bohužel tam jsou některé vlivné lidi ve společenství s názorem (a mnoho lidí, kteří slepě následují), že je to anti-pattern. Nesouhlasím s tím a zvedl své argumenty mnohokrát (například zde). Popsal jsi důvodů velmi dobře - to jen vytváří zbytečné složitosti/boilerplate kódu...

Takže to, co máte co do činění s je opravdu jen linting pravidlo (vue/ne-mutuje-rekvizity). Tam je pokračující problém/diskuse , která nabízí konfigurační možnost, která by měla umožnit, aby zmírnit přísnost pravidlo s mnoha dobrými argumenty, ale je stále velmi malá pozornost od správců (klidně zvýšit hlas, tam taky)

Teď, co můžete udělat, je:

  1. Zakázat pravidlo (daleko od dokonalý ale naštěstí díky Vue runtime error, můžete chytit skutečné chyby přiřazení nesprávné přípony případech během vývoje v pohodě)
  2. Přijmout realitu a použít zástupná řešení

Řešení - použít globální stav (uložit jako Vuex nebo Pinia)

Poznámka: Pinia je výhodné, protože další verze Vuex budou mít stejné API

Obecná představa je umístit sharedObject v obchodě a používat rekvizity pouze procházet podřízené součásti na ten správný objekt - ve vašem případě Component2 obdrží index, do vrtule a získat právo prvek z obchodu pomocí to.

Obchody jsou skvělé pro sdílení celosvětového státu, ale použití je jen na překonání linting pravidlo je špatné. Také jako výsledek, komponenty jsou spolu do obchodu, tudíž obě znovupoužitelnost trpí a testování je těžší

Řešení - akce

Ano, to je možné vytvořit nepořádek a hodně často používaný kód pouze pomocí události (zvláště pokud jste hnízdo komponenty více než 2 úrovně), ale existují způsoby, jak dělat věci čistší.

Například ve vašem případě Component2 není nutné znát, jak se můžete zpracovat událost, jako je tento

// Component1
<template>
  ...
   <template v-for="elem in sharedObject.arrayElements">
    <template v-for="(elem2, index) in elem.arrayElements">
       <Component2 :input2="elem2" @update="updateElement($event, index)" />
    </template>
  </template>
  ...
</template>

Ve vašem případě, Component2 zpracovává pouze změna jednoho logická vlastnost, takže $event může být jednoduchý boolean. Pokud existuje více než jednu vlastnost, aby se změnil uvnitř Component2, $event může to být objekt, a můžete použít objekt rozšířil syntaxe "zjednodušit" Component2 (pomocí jedné události, místo více - jeden pro každou vlastnost)

// Component2
<template>
  ...
  <input v-model="someText" type="text">
  <q-btn @click="updateInput('isSelected', !input2.isSelected)"></q-btn>
  ...
</template>
<script>
export default {
  props: ['input2'],
  computed: {
    someText: {
      get() { return this.input2.someText },
      set(newVal) { updateInput('someText', newVal) }
    }
  },
  methods: {
    updateInput(propName, newValue) {
      const updated = { ...this.input2 } // make a copy of input2 object
      updated[propName] = newValue  // change selected property

      this.$emit('update', updated) // send updated object to parent
    }
  }
}
</script>

No...raději jsem jen zakázat pravidlo, a nastavit některé jasné konvence pojmenování označuje, že komponenta je zodpovědná za změnu je vstup...

Všimněte si, že existují jiná řešení, jako je použití this.$parent, inject\provide nebo událost, autobusem, ale ty jsou opravdu špatné

2021-11-24 09:42:19

V jiných jazycích

Tato stránka je v jiných jazycích

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................