Javascript — Vue: дочерний компонент не изменится после получения реквизита от родителей.

Vue: дочерний компонент не изменится после получения реквизита от родителей

Хорошо, давайте пройдемся по порядку:

  1. Родительский компонент создается, вызывая created хук и инициируя загрузку данных с сервера.
  2. Родительский компонент выполняет рендеринг, создавая дочерние компоненты. Значение prop для spec будет null поскольку данные еще не загружены, а singleProductSpec по-прежнему равен null .
  3. created крюк для single-product-spec пробегов по single-product-spec . Поскольку this.spec имеет значение null я представляю, что это приводит к ошибке, хотя в вопросе об ошибке не упоминалось.
  4. В какой-то момент в будущем загрузка данных завершается, обновляя значение singleProductSpec . Это зависимость рендеринга родительского компонента, поэтому этот компонент будет добавлен в очередь рендеринга.
  5. Родительский компонент будет перерисован. Новое значение singleProductSpec будет передано в качестве spec для single-product-spec . Новый экземпляр single-product-spec не будет создан, он просто будет повторно использовать тот, который создал первым.

В этот момент больше ничего не произойдет. created хук single-product-spec не будет перезапущен, поскольку он не был просто создан.

Когда вы редактируете исходный код дочернего компонента, он запускает горячую перезагрузку этого компонента. Точный эффект такого изменения будет отличаться, но часто это приведет к воссозданию этого потомка без воссоздания родителя. Поскольку родительский объект уже загрузил данные с сервера, вновь созданному дочернему элементу будет передано полностью заполненное значение spec . Это позволяет читать его в created хуке.

Есть несколько способов решить эту проблему.

Во-первых, мы могли бы избежать создания single-product-spec пока данные не будут готовы:

 {amp}lt;product-spec v-if="singleProductSpec" :spec="singleProductSpec" /{amp}gt; 

Это просто позволит избежать создания компонента во время первоначального рендеринга, так что при запуске created дочерним хука он будет иметь доступ к нужным данным. Это, вероятно, подход, который вы должны использовать.

Второй способ сделать это — использовать key . Ключи используются для сопряжения компонентов при повторном рендеринге, чтобы Vue знал, какой старый компонент соответствует какому новому компоненту. Если key изменится, Vue отбросит старый дочерний компонент и вместо него создаст новый. По мере создания нового компонента он запускает created хук. Вероятно, это не лучший подход для вашего сценария, так как неясно, что должен делать дочерний компонент, когда передается spec null .

Третий подход заключается в использовании watch в дочернем компоненте. Это будет следить за изменением значения spec и копировать соответствующие значения в локальные свойства данных компонента. Несмотря на то, что в некоторых случаях использование таких watch уместно, это обычно указывает на основную слабость в дизайне компонента.

Однако в вашем коде есть другие проблемы …

  1. Непонятно, зачем вы копируете значения из реквизита в локальные данные. Вы можете просто использовать опору напрямую. Если вы делаете это просто для того, чтобы дать им более короткие имена, просто используйте вместо этого вычисляемое свойство. Единственная законная причина для их копирования, как это, заключается в том, что значения свойств могут быть изменены внутри дочернего элемента, а реквизит используется только для передачи начального значения. Даже в этом случае вы не будете использовать created хук, вы просто сделаете это внутри функции data . См. Https://vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow .
  2. Вы дублируете все 5 раз для 5 вкладок. Это должно быть реализовано с использованием массива объектов, каждый из которых содержит все необходимые данные для вкладки.
  3. Свойства mesinActive и mesinTab оба представляют mesinTab и те же базовые данные. Вы не должны иметь оба в data . По крайней мере, одно должно быть вычисляемым свойством, хотя лично я, вероятно, просто mesinTab от mesinTab целом. Вместо этого используйте CSS-классы, чтобы применить соответствующий стиль, и просто используйте mesinActive чтобы решить, какие классы применять (как у вас в другом месте). Очевидно, то же самое относится и к другим свойствам xActive / xTab .
  4. Ваши вкладки являются формой единого выбора. Использование 5 логических значений для представления одного выбора не является подходящей структурой данных. Правильный способ сделать это — иметь единственное свойство, которое идентифицирует текущую вкладку. Специфика может варьироваться, это может содержать индекс вкладки, или объект, представляющий данные вкладки, или идентификатор, представляющий вкладку.
  5. Вам не нужно использовать let self = this с функциями стрелок. Это значение сохраняется из окружающей области видимости.

Правильно реализованный код для single-product-spec должен рухнуть практически до нуля. Вы должны быть в состоянии избавиться от около 80% кода. Я ожидаю, что метод openSpaceTab будет однострочным, если вы просто используете соответствующие структуры данных для хранения всех ваших данных.

Обновить:

В соответствии с просьбой, мы переписываем ваш компонент с учетом пунктов 1-4 из раздела «Другие проблемы» моего ответа.

 const ProductSpecTitle = { template: ` {amp}lt;div{amp}gt; {amp}lt;div class="product-spec-title"{amp}gt; Spesifikasi {amp}lt;/div{amp}gt; {amp}lt;div class="produk-laris-wrapper"{amp}gt; {amp}lt;div class="tab-navigation-wrapper tab-navigation-default"{amp}gt; {amp}lt;div v-for="tab of tabs" :key="tab.id" class="tab-navigation tab-default" :class="{ 'active-default': tab.active }" @click="openSpaceTab(tab.id)" {amp}gt; {amp}lt;p class="tab-text tab-text-default"{amp}gt;{{ tab.text }}{amp}lt;/p{amp}gt; {amp}lt;/div{amp}gt; {amp}lt;/div{amp}gt; {amp}lt;div v-for="tab in tabs" class="spec-tab-panel" :class="{ 'spec-tab-panel-active': tab.active }" {amp}gt; {amp}lt;table class="spec-table"{amp}gt; {amp}lt;tbody{amp}gt; {amp}lt;tr v-for="(value, name) in tab.data" :key="name" class="spec-row" {amp}gt; {amp}lt;td{amp}gt; {{ name }} {amp}lt;/td{amp}gt; {amp}lt;td{amp}gt; {{ value }} {amp}lt;/td{amp}gt; {amp}lt;/tr{amp}gt; {amp}lt;/tbody{amp}gt; {amp}lt;/table{amp}gt; {amp}lt;/div{amp}gt; {amp}lt;/div{amp}gt; {amp}lt;/div{amp}gt; `, props: { spec: Object }, data () { return { selectedTab: 'mesin' } }, computed: { tabs () { const tabs = [ { id: 'mesin', text: 'Mesin' }, { id: 'rangka', text: 'Rangka {amp}amp; Kaki' }, { id: 'dimensi', text: 'Dimensi {amp}amp; Berat' }, { id: 'kapasitas', text: 'Kapasitas' }, { id: 'kelistrikan', text: 'Kelistrikan' } ] for (const tab of tabs) { tab.active = tab.id === this.selectedTab tab.data = this.spec[tab.id] } return tabs } }, methods: { openSpaceTab (tab) { this.selectedTab = tab } } } new Vue({ el: '#app', components: { ProductSpecTitle }, data () { return { spec: { mesin: { a: 1, b: 2 }, rangka: { c: 3, d: 4 }, dimensi: { e: 5, f: 6 }, kapasitas: { g: 7, h: 8 }, kelistrikan: { i: 9, j: 10 } } } } }) 
 .tab-navigation-wrapper { display: flex; margin-top: 10px; } .tab-navigation { border: 1px solid #000; cursor: pointer; } .tab-text { margin: 10px; } .active-default { background: #ccf; } .spec-tab-panel { display: none; } .spec-tab-panel-active { display: block; margin-top: 10px; } .spec-table { border-collapse: collapse; } .spec-table td { border: 1px solid #000; padding: 5px; } 
 {amp}lt;script src="https://unpkg.com/vue@2.6.10/dist/vue.js"{amp}gt;{amp}lt;/script{amp}gt; {amp}lt;div id="app"{amp}gt; {amp}lt;product-spec-title :spec="spec"{amp}gt;{amp}lt;/product-spec-title{amp}gt; {amp}lt;/div{amp}gt; 
Понравилась статья? Поделиться с друзьями:
JavaScript & TypeScript
Adblock
detector