Vue.js 2.0 рендеринг одного и того же компонента для одного элемента, но не для другого

Я создаю веб-сайт вопросов и ответов, очень похожий на stackoverflow, где вы можете vote question или answer. Для упрощения я оставил код для голосования.

Это компонент vote, который используется как в компонентах question, так и в компонентах answer.

<template>
  <div class="vote">
    <h4 @click="voteUp">
      <i :class="{'fa fa-chevron-circle-up' : votedUp>-1, 'fa fa-chevron-up': votedUp==-1 }"></i>
    </h4>
  </div>
</template>

<script>
  export default {
    props: ['votes','item'],
    computed:{
      votedUp() {
        return _.findIndex(this.votes, {user_id: this.$root.authuser.id});
      }
    },
    methods:{
      voteUp() {
        axios.post(this.$root.baseUrl+'/vote_item', {item_id:this.item.id,item_model:this.item.model,vote:'up'})
             .then(response => {
                _.isEmpty(response.data) ? this.votes.splice(this.votedUp,1) : this.votes.push(response.data);
              })
      }
    }
  }
</script>

Это компонент question, который использует компонент vote:

<template>
  <div class="question">
    <h1>{{ question.title }}</h1>
    <div class="row">
      <div class="col-xs-1">
        <vote :votes="question.votes" :item="item"></vote>
      </div>
      <div class="col-xs-11 pb-15">
        <p>{{ question.body }}</p>
        <comment-list :comments="question.comments" :item="item" ></comment-list>
      </div>
    </div>
    <answer v-for="answer in question.answers" :answer="answer"></answer>
  </div>
</template>

<script>
  export default {
    props: ['question'],
    data(){
      return {
        item:{ id:this.question.id, model:'Question' }
      };
    }
  }
</script>

Это компонент answer, который использует компонент vote:

<template>
  <div class="answer row">
    <div class="col-xs-1 mt-20">
      <vote :votes="answer.votes" :item="item"></vote>
    </div>
    <div class="col-xs-11">
      <h3>{{ answer.title }}</h3>
      <p>{{ answer.body }}</p>
      <comment-list :comments="answer.comments" :item="item" ></comment-list>
    </div>
  </div>
</template>

<script>
  export default {
    props: ['answer'],
    data(){
      return {
        item:{ id:this.answer.id, model:'Answer' }
      };
    }
  }
</script>

ПРОБЛЕМА

Голосование работает нормально и изменяет состояние как question.votes, так и answer.votes, но отображает только HTML-код answers. Мне нужно обновить, чтобы увидеть голосование за question. Кроме того, в консоли инструментов разработчика Vue answer.votes обновляется автоматически, в то время как мне нужно нажать кнопку обновления vue, чтобы увидеть, как question.votes принимает во внимание новое голосование (но все еще без рендеринга HTML).

ВАЖНОЕ ПРИМЕЧАНИЕ

Как видите, вы также можете использовать comment для question и answer, и это прекрасно работает для обоих, потому что я использовал другой подход к $emit событию. Возможно, это решение моего ответа, но я действительно хочу знать, почему функциональность в vote работает на answer, а не на question. Спасибо!


person BassMHL    schedule 22.02.2017    source источник
comment
Не используете Vue в режиме разработки? Вы не должны изменять/мутировать props. Как и в компоненте vote - this.votes.push.   -  person Jonatas Walker    schedule 22.02.2017
comment
Да, я использую Vue в режиме разработки. Как вы предлагаете мне сделать это вместо этого?   -  person BassMHL    schedule 22.02.2017
comment
Тогда у вас должен быть warn из Vue в консоли.   -  person Jonatas Walker    schedule 22.02.2017


Ответы (1)


Ваш компонент vote не должен изменять props. Измените локальную копию:

export default {
  props: ['votes','item'],
  data() {
    return { votesCopy: [] };
  },
  mounted() {
    this.votesCopy = this.votes.slice();
  },
  computed:{
    votedUp() {
      return _.findIndex(this.votesCopy, {user_id: this.$root.authuser.id});
    }
  },
  methods:{
    voteUp() {
      axios.post(this.$root.baseUrl+'/vote_item', {item_id:this.item.id,item_model:this.item.model,vote:'up'})
           .then(response => {
              _.isEmpty(response.data)
                ? this.votesCopy.splice(this.votedUp,1)
                : this.votesCopy.push(response.data);
            })
    }
  }
}
person Jonatas Walker    schedule 22.02.2017
comment
Это работает, спасибо. Но я до сих пор не понимаю, почему компонент не должен мутировать props. Не могли бы вы объяснить? Зачем мне копировать и загружать память, когда эффективнее изменить реквизит? - person BassMHL; 22.02.2017
comment
Я предполагаю, что это обсуждение объясняет лучше, чем я могу. - person Jonatas Walker; 22.02.2017
comment
Кажется, что правильный способ синхронизировать все — это генерировать события для изменения состояния родителя. Ваше решение работает и более удобно для кода (вся логика голосования остается в компоненте голосования), но создает несинхронизированное состояние между родительским и дочерним состояниями. Спасибо, что поделился. - person BassMHL; 23.02.2017
comment
Последний вопрос: почему вы нарезали голоса в Mounted()? Я пробовал с data(){ return {votesCopy: this.votes; } } и это работает - person BassMHL; 23.02.2017
comment
Ну ладно тогда. Это мой способ создать копию массива. - person Jonatas Walker; 23.02.2017
comment
@Jonatas Walker, кажется, вы освоили vue.js. Мне нужна твоя помощь. Посмотрите на это: stackoverflow.com/questions/42454027/ - person samuel toh; 25.02.2017