Почему компонент vue.js не показывает полученные данные с первой попытки?

Я успешно получаю данные из внешнего файла js (длинный процесс Json, экспортирует функцию обещания), но данные не были добавлены в компонент с первой попытки. Мне нужно ввести другой компонент, а затем при возврате отображаются данные.

Я пробовал использовать метод beforeCreate(), но это не помогло, condole.log(data) показывает информацию, а компонент нет.

<template>
   <div class="roomsBar">
      <div class="roomsArea">
          <li v-for="building in allBuildings" :key="building">
              <a v-on:click="clicked(building)">{{building}}</a>
      </li>
      </div>
   </div>
</template>

<script>
import {menuData as menuData} from '../controllers/navigation.js';

export default {
    name: 'Buildings',
    data() {
        return {
        allBuildings: window.menu
    }
    },
    methods: {
    clicked: function(item){
        this.$router.push({ path: '/navigation/'+item })
    }
    },
    beforeCreate() {
    window.menu = [];
    menuData().then((data) => {   // Recover the data from 'Navigation.js'
        console.log(data);
            for (const element of data) { // Filter data and only add building to the menu
        window.menu.push(element.building);
    }
    console.log(window.menu);
    });
    }
}
</script>


<style scoped>
    .roomsBar {
        width: 100%;
        height: 100%;
        background-color: #fff;
    }
    li {
        list-style-type: none;
    }
    a {
    cursor: pointer;
    color: #42b983;
    }
</style>

Мне нужно, чтобы данные отображались с первой попытки.

Кстати, я новичок в vue.


person Mauricio Andrés    schedule 14.02.2019    source источник


Ответы (2)


Причина, по которой это не работает, заключается в том, что Vue не может отследить изменение window.menu и, следовательно, не может выполнить повторный рендеринг как таковой.

Все ваше использование window для этого в лучшем случае сомнительно. Рассмотрим следующее:

let menuData = function() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      return resolve([{
          name: 'test'
        },
        {
          name: 'test 2'
        }
      ]);
    }, 100);
  });
}

let component = Vue.component('test', {
  name: 'Buildings',
  template: '<ul><li v-for="item in allBuildings">{{item.name}}</li></ul>',
  data: () => ({

    allBuildings: []
  }),
  beforeCreate() {
    menuData().then((data) => {
      this.$set(this.$data, 'allBuildings', data);
    });
  }
});

let app = new Vue({
  el: "#app"
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <test></test>
</div>

Этим небольшим переписыванием мы достигли нескольких целей:

  • вы больше не зависите от массива, хранящегося вне этого элемента (т.е. window), предпочитая использовать свойство самого вашего объекта
  • Vue теперь четко знает, когда изменяется массив, так как мы вызываем $set
  • связь между ними теперь явная

Дополнительная информация о $set/Vue.set доступна в документации Vue и является основным способом для хранения и обновления реактивных данных.


Что касается цикла for и фильтрации, если вы никогда не собираетесь обновлять данные этого компонента, совершенно нормально оставить push для массива, например:

menuData().then((data) => {
  for (const element of data) {
    this.$data.allBuildings.push(element);
  }
});

Vue.js напрямую поддерживает большинство методов изменения массива прямо на сам объект данных; если вы окажетесь в случае, когда вам нужно отфильтровать массив, использовать их или построить массив отдельно, тогда $setting это также сработает.

person Sébastien Renauld    schedule 14.02.2019
comment
Я думаю, что понимаю, что вы написали, но vue выдает ошибку Property or method "allBuildings" is not defined on the instance but referenced during render. и Avoid adding reactive properties to a Vue instance or its root $data at runtime - declare it upfront in the data option. Кроме того, причина, по которой я подаю в суд на for, заключается в том, что мне нужно отфильтровать данные, так как же это сделать с помощью $set? - person Mauricio Andrés; 15.02.2019
comment
@MauricioAndrés извинения. Я записал этот ответ с мобильного, а затем сразу пошел спать - вернемся к нему сейчас, и я обращусь к части for для вас :-) - person Sébastien Renauld; 15.02.2019
comment
Там. Отредактировано, код теперь представляет собой фрагмент (чего я не мог сделать с телефона), и он работает - person Sébastien Renauld; 15.02.2019
comment
Круто, спасибо, теперь мой компонент работает как положено, огромное спасибо. - person Mauricio Andrés; 15.02.2019

Попробуй это. Вы должны инициализировать allBuildings с пустым массивом, а не с несуществующим объектом, который вызывает ошибку.

export default {
  name: 'Buildings',
  data() {
    return {
      allBuildings: []
    }
  },
  methods: {
    clicked: function(item){
      this.$router.push({ path: '/navigation/'+item })
    }
  },
  beforeCreate() {
    window.menu = [];
    menuData().then((data) => {
      for (const element of data) {
        window.menu.push(element.building);
      }

      this.allBuildings = window.menu;
    });
  }
}
person Yosuke Sato    schedule 15.02.2019