异步更新数组项 - 监视未触发

时间:2018-05-17 14:47:09

标签: javascript asynchronous vue.js vuejs2 vue-component

我创建了一个显示博客文章预览的组件。此组件具有分页功能,在选择新页面时,我刷新了文章预览的数组。文章列表是从server1的JSON api中获取的。响应包含从服务器2获取每篇文章的信息。然后,我向服务器2发送x异步提取,与第一个响应中的项目一样多。在这些响应中,我更新了数组中的项目。

我是vue的新手,但经过一番挣扎,我得到了这个。现在我正在尝试在文章预览中添加一个微调器,同时加载单独的文章。我的想法是在previewcomponent中观看文章更新并根据它显示微调器。不幸的是它不起作用,现在我开始怀疑我的实现。我注意到预览中的手表没有为每个预览组件调用,但仍然会更新每个预览并正确显示。我认为这是因为消息系统,但我无法修复它。

我的问题有两个:

  • 我的实现是否是处理此问题的正确方法?为了让它工作,我很好地需要'擦除'数组,因为否则新文章“覆盖”旧文章,这是可见的。
  • 我该如何处理微调器。为什么没有触发手表,我该如何解决?在下面的代码中,我有一些控制台写入。我看到10次'异步'并且每次都有不同的'看',从不10。

完整的代码在github上:HomeArticlePreview。这些是最相关的部分:

主页:

<template>
    <div class="container article">
        <div class="row" v-for="(article, index) in articles" :key="index">
            <ArticlePreview v-bind:blogEntry="article"></ArticlePreview>
        </div>
        <b-pagination-nav :use-router="true" :link-gen="generateLink" align="center" :number-of-pages="nofPages" v-model="pageIndex" />
    </div>
</template>

data: function ()
{
    return {
        articles: <BlogEntry[]> [],
        nofPages: 1
    }
},

loadContent()
{
    fetch("./api/v1/articles.php?page=" + this.pageIndex)
    .then(response => response.json())
    .then((data) =>
    {
        this.nofPages = Math.ceil(data.nofItems/10);
        this.articles.splice(0);
        this.articles.splice(data.data.length);
        let index :number;
        for(index = 0; index < data.data.length; index++)
        {
            createArticleAsync(data.data[index].name, data.data[index].permlink).then(function(this: any, index: number, article: BlogEntry)
            {
                console.log('async');
                    Vue.set(this.articles, index, article);
            }.bind(this, index));
        }
    })
},

ArticlePreview:

<template>
    <div class="container-fluid">
        <div class="row" v-if="blogEntry">
            <template v-if="blogEntry">
                <div class="imageframe col-md-3">
                    <div class="blog-image">
                        <img :src="blogEntry.previewImage" style="border-radius: 5px;">
                    </div>
                </div>
                <div class="col-md-9">
                    <h5 class="font-weight-bold" style="margin-top:5px;"><router-link :to="{ name: 'Article', params: {author: blogEntry.author, permlink: blogEntry.permlink } }">{{blogEntry.title}}</router-link></h5>
                    <div class="multiline-ellipsis">
                        <p>{{blogEntry.previewBody}}</p>
                    </div>
                    <span class="metadata"><i>by <a :href="AuthorBlogLink">{{blogEntry.author}}</a> on {{blogEntry.created | formatDate}}</i></span>
                </div>
            </template>
            <template v-else>
                <p>Loading</p>
            </template>
        </div>
    </div>
</template>

<script lang="ts">
    import Vue from "vue";
    import VueRouter from 'vue-router';

    import {formatDate} from "../utils/utils";

    export default Vue.extend({
        props: [
            'blogEntry'
        ],
        data: function ()
        {
            return {
                loading: true
            }
        },
        watch:
        {
            blogEntry(newValue)
            {
                console.log('watch');
                if(newValue)
                    this.loading = false;
                else
                    this.loading = true;
            }
        }
    });
</script>

1 个答案:

答案 0 :(得分:1)

我认为获取文章详细数据的方法应该封装在组件内部,加载状态也在内部维护。就像下面的代码一样:(它没有正常工作,因为Mockjs无法执行正确地在片段中)

&#13;
&#13;
Mock.setup({timeout: 2000})
const URL_ARTICLE_LIST = '/api/v1/articles.php'
const URL_ARTICLE_DETAIL = '/api/v1/article_detail.php'

Mock.mock(/\/api\/v1\/articles\.php.*/,function(options){
  return {
    nofItems: 33,
    data: Mock.mock({
      'list|10': [{
        'title': '@title',
        'url': URL_ARTICLE_DETAIL
      }]
    }).list
  }
})
Mock.mock(URL_ARTICLE_DETAIL,function(options){
  return Mock.mock({
  	content: '@paragraph'
  })
})

Vue.component('article-card',{
  template:  `
    <div>
      <template v-if="!loading">
        <div class="article-title">{{articleTitle}}</div>
        <div class="article-content">{{article.content}}</div>
      </template>
      <template v-else>
        <div>loading...</div>
      </template>
    </div>`,
  data () {
    return {
      loading: false,
      article: {}
    }
  },
  props: {
    articleTitle: {
      required: true,
      type: String
    },
    articleUrl: {
      required: true,
      type: String
    }
  },
  watch: {
    articleUrl (url,oldUrl) {
      if(url && url!=oldUrl){
      	this.loadContent()
      }
    }
  },
  methods: {
  	loadContent () {
  		this.loading = true;
      //or your own async functions
      axios.get(this.articleUrl).then(res=>{
        this.article = res.data
        this.loading = false;
      })
  	}
  },
  created () {
  	this.loadContent()
  }
});

var app = new Vue({
  el: '#app',
  data () {
  	return {
	    articles: [],
	    nofPages: 1,
	    page: 1 //you should get page param from vue-router just like this.$route.query.page  		
  	}
  },
  created () {
    //you can also use fetch here
    axios.get(URL_ARTICLE_LIST+'?page='+this.page).then(res=>{
    	console.log(res.data)
      this.nofPages = Math.ceil(res.data.nofItems/10);
      this.articles = res.data.data
    })
  }  
})
&#13;
	ul,li{
		list-style: none;
	}
	.article_list{
		display: flex;
		flex-wrap: wrap;
	}
	.article_list>li{
		width: 300px;
		background: skyblue;
		color: white;
		margin: 10px;
	}
	.article-content{
		text-indent: 2em;
	}
	.pagination-wrapper>li{
		display:inline-block;
		padding: 5px;
		border: 1px solid skyblue;
		margin: 3px;
	}
	.pagination-wrapper>li.active{
		background: skyblue;
		color: #fff;
	}
&#13;
<script src="https://cdn.bootcss.com/Mock.js/1.0.1-beta3/mock-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="app">
  <ul class="article_list">
    <li v-for="article of articles">
      <article-card :article-title="article.title" :article-url="article.url"></article-card>
    </li>
  </ul>
  <ul class="pagination-wrapper">
  	<li v-for="x in nofPages" :class="{active: page==x}">{{x}}</li>
  </ul>
</div>
&#13;
&#13;
&#13;