Порядок рендеринга Vue - PullRequest
0 голосов
/ 20 сентября 2018

Я работаю над созданием компонента вкладки с vue и заметил следующее поведение.

Если я создаю свой компонент следующим образом:

<tabs-component>
  <tab-component :title="1">
    <h1>1</h1>
    <p>Lorem ipsum dolor sit amet.</p>
  </tab-component>
  <tab-component :title="2">
    <h1>2</h1>
    <p>Lorem ipsum dolor sit amet.</p>
  </tab-component>
</tabs-component>

Хуки жизни запускаются в следующем порядке:

  • Создан компонент вкладок
  • Создан компонент вкладок
  • Установлен компонент вкладок
  • Установлен компонент вкладок

Что кажетсяОднако, чтобы быть правильным, если вместо того, чтобы объявлять слоты вручную, я использую директиву v-for, предполагая, что имею дело с некоторыми данными из службы, подобной этой:

<tabs-component>
  <tab-component v-for="(type, index) in beerTypes" :key="type.slug" :title="type.slug">
    <h1>{{ index }}</h1>
    <p>Lorem ipsum dolor sit amet.</p>
  </tab-component>
</tabs-component>

Хуки жизни запускаются в этом порядке:

  • Создан компонент вкладок
  • Установлен компонент вкладок
  • Создан компонент вкладок
  • Установлен компонент вкладок

Директива v-for, кажется, портит порядок запуска ловушек.И это вызывает у меня проблемы, так как я запускаю метод selectTab на смонтированном спасательном хуке, чтобы выбрать выбранную вкладку по умолчанию, но когда запущенные триггеры не смонтированы.

Кто-нибудь знает, как я могуправильно слушать события в правильном порядке или дождаться готовности дочерних компонентов, чтобы я мог выбрать панель по умолчанию?

Вот остаток кода для справки:

App.vue

<template>
  <main>
    <h1>Discover beer styles</h1>
    <tabs-component>
      <tab-component v-for="(type, index) in beerTypes" :key="type.slug" :title="type.slug">
        <h1>{{ index }}</h1>
        <p>Lorem ipsum dolor sit amet.</p>
      </tab-component>
    </tabs-component>
  </main>
</template>

<script>
  import axios from 'axios';
  import TabsComponent from './components/tabs/Tabs.vue';
  import TabComponent from './components/tabs/Tab.vue';

  export default {
    components: {
      'tabs-component': TabsComponent,
      'tab-component': TabComponent,
    },

    data() {
      return {
        beerTypes: null,
        selectedType: null
      }
    },

    filters: {
      capitalize: function(value) {
        if (!value) return '';

        return `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
      }
    },

    mounted() {
      axios.get('/data/data.json')
        .then((response) => {
          this.beerTypes = response.data;
          this.selectedType = this.beerTypes[0].slug;
        })
        .catch((error) => console.log(error));
    },

    methods: {
      selectActiveType: function(selected) {
        this.selectedType = selected;
      }
    }
  }
</script>

Tabs.vue

<template>
  <div class="tabs-component">
    <div>
      <ul>
        <li v-for="(tab, index) in tabs" :class="{'is-active': tab.isActive}" :key="index">
          <a href="#" @click="selectTab(index)">{{ tab.title }}</a>
        </li>
      </ul>
    </div>
    <div>
      <slot/>
    </div>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        tabs: [],
        activeTabIndex: 0
      }
    },

    created() {
      this.tabs = this.$children;
    },

    mounted() {
      this.selectTab(this.activeTabIndex);
    },

    methods: {
      selectTab(index) {
        this.tabs.forEach(function(tab) {
          tab.isActive = (this.tabs.indexOf(tab) === index);
        }.bind(this));
      }
    }
  };
</script>

Tab.vue

<template>
  <section v-show="isActive">
    <slot/>
  </section>
</template>

Я ценю, если кто-то может найти время, чтобы помочь мне.

Спасибо!

1 Ответ

0 голосов
/ 20 сентября 2018

Я думаю, что используется Vue: предоставить / ввести будет одним из решений здесь.

Для вашего случая, если будет добавлен новый <tab-component> (вы заявили, что beerTypes будут обновлены из вашегобэкэнд-сервис), <tabs-component> не узнает, пока не получит новейшие this$children внутри хука = updated().

Если использовать предоставить / ввести , мы можем позволить <tab-component> зарегистрировать его происхождениек одному свойству данных <tabs-components>.Тогда <tabs-component> не нужно будет заботиться о добавлении новой вкладки, и он сможет напрямую обращаться к экземпляру <tab-component>.

Также вам нужно реализовать один _unRegister, когда экземпляр уничтожается, удалитеэто из свойства данных <tabs-component>, ниже демо использует hook = beforeDestory для его реализации.

Ниже приведено одно dmeo:

Vue.productionTip = false
Vue.component('tabs-component', {
  template: `  <div class="tabs-component">
    <div>
      <h3>Selected Tab: {{activeTabIndex}}</h3>
      <ul>
        <li v-for="(tab, index) in tabs" :class="{'is-active': tab.isActive}" :key="index">
          <a href="#" @click="selectTab(index)">{{ tab.title }}</a>
        </li>
      </ul>
    </div>
    <div>
      <slot/>
    </div>
  </div>`,
  provide () {
    return {
      _tabs: this
    }
    },
    data() {
      return {
        tabs: [],
        activeTabIndex: 0
      }
    },
    mounted() {

    },

    methods: {
      selectTab(selectedIndex) {
        this.activeTabIndex = selectedIndex
        this.tabs.forEach((tab, tabIndex) => {
          tab.isActive = selectedIndex === tabIndex
        })
      },
      _registerTab(tab) {
        this.tabs.push(tab)
        this.selectTab(this.activeTabIndex)
      },
      _unRegisterTab(unRegTab) {
        this.tabs = this.tabs.filter(tab=>{
          return unRegTab !== tab
        })
        this.selectTab(0)
      }
    }
})

Vue.component('tab-component', {
  template: `  <section v-show="isActive" :title="title">
    <slot/>
  </section>`,
	inject: {
    _tabs: {
      default () {
        console.error('tab-component must be child of tab-components')
      }
		}
  },
  props: ['title'],
  mounted () {
    this._tabs._registerTab(this)
  },
  beforeDestroy(){
    this._tabs._unRegisterTab(this)
  },
  data () {
    return {
      isActive: false
    }
  }
})

new Vue({
  el: '#app',
  data () {
    return {
      beerTypes: []
    }
  },
  methods: {
    pushData () {
      setTimeout(()=>{
        this.beerTypes.push({'slug': 'test' + this.beerTypes.length})
      }, 1000)
    },
    popData () {
      this.beerTypes.pop()
    }
  }
})
.is-active {
  background-color: green;
}
<script src="https://unpkg.com/vue@2.5.17/dist/vue.min.js"></script>
<div id="app">
  <button @click="pushData()">Add Tab</button>
  <button @click="popData()">Pop one</button>
    <tabs-component>
      <tab-component v-for="(type, index) in beerTypes" :key="type.slug" :title="type.slug">
        <h1>{{ index }}: {{type.slug}}</h1>
        <p>Lorem ipsum dolor sit amet.</p>
      </tab-component>
    </tabs-component>
</div>

И обратите внимание на это примечание в руководстве Vue для предоставить / ввести :

Примечание: привязки подачи и впрыска НЕ ​​являются реактивными.Это намеренно.Однако, если вы передадите наблюдаемый объект, свойства этого объекта останутся реактивными.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...