Я создал компонент, который отображает превью статей в блоге. Этот компонент имеет разбиение на страницы, и при выборе новой страницы я обновляю массив превью статей. Список статей берется из JSON api с server1. Ответ содержит информацию для выборки каждой статьи с сервера 2. Затем я запускаю x асинхронных выборок на сервер 2, столько же, сколько элементов в первом ответе. В этих ответах я обновляю элементы в массиве.
Я новичок в vue, но после некоторой борьбы я заставил это работать. Теперь я пытаюсь добавить счетчик в превью статей, пока загружаются отдельные статьи. Моя идея заключалась в том, чтобы следить за обновлением статьи в компоненте предварительного просмотра и показывать счетчик в зависимости от этого. К сожалению, это не работает, и теперь я начинаю сомневаться в своей реализации. Я заметил, что часы в предварительном просмотре не вызываются для каждого компонента предварительного просмотра, но все же каждый предварительный просмотр обновляется и отображается правильно. Я предполагаю, что это из-за системы обмена сообщениями, но мне не удается это исправить.
У меня двоякий вопрос:
Полный код находится на github здесь: Дом и СтатьяПредварительный просмотр. Это самые важные части:
Дом:
<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.info('async');
Vue.set(this.articles, index, article);
}.bind(this, index));
}
})
},
СтатьяПредварительный просмотр:
<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.info('watch');
if (newValue)
this.loading = false;
else
this.loading = true;
}
}
});
</script>



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я думаю, что метод получения подробных данных статьи должен быть инкапсулирован внутри компонента, а состояние загрузки также поддерживается внутри. Точно так же, как приведенный ниже код: (Он не работает должным образом, потому что Mockjs не может правильно выполняться во фрагменте)
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.info(res.data)
this.nofPages = Math.ceil(res.data.nofItems/10);
this.articles = res.data.data
})
}
}) 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;
}<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>