📖

.NET Core Blazor + MicroCMSでブログサイト作成⑥

2021/09/04に公開

なんやかんやで第6回目です。
6回目...6階...

6階触らせてー!!

https://www.youtube.com/watch?v=Kqcu63UNIv4

今回は以下の3点を行います。

  • データフェッチの並列化
  • 取得データ内容の削減
  • ちょこっとスタイリング

データフェッチの並列化

OnInitializedAsync内を書き換えるだけです。

Blog.razor
// 抜粋
@code {
    private SiteData sitedata;
    private Tags tags;
    private Blogs blogs;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            /*** 直列ver. ***/
            //blogs = await Http.GetFromJsonAsync<Blogs>("MicroCMS/Blogs");
            //sitedata = await Http.GetFromJsonAsync<SiteData>("MicroCMS/SiteData");
            //tags = await Http.GetFromJsonAsync<Tags>("MicroCMS/Tags");

            /*** 並列ver ***/
            // サーバーからTaskを受け取る
            Task<SiteData> taskSiteData = Http.GetFromJsonAsync<SiteData>("MicroCMS/SiteData");
            Task<Tags> taskTags = Http.GetFromJsonAsync<Tags>("MicroCMS/Tags");
            Task<Blogs> taskBlogs = Http.GetFromJsonAsync<Blogs>("MicroCMS/Blogs");

            // 並列処理
            await Task.WhenAll(taskSiteData, taskTags, taskBlogs);

            // 結果取り出し
            sitedata = taskSiteData.Result;
            tags = taskTags.Result;
            blogs = taskBlogs.Result;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }
}

クライアントからGetFromJsonAsyncを呼び、対応するサーバー側のコントローラーはTask<T>オブジェクトを返します。
このメソッドをawaitすることで、Taskの完了を待ちます。(直列ver)
これを待機せず一旦Taskのまま受け取り、いくつかまとめて同時にスタートさせ、すべてのTaskの完了を待ち合わせます。(並列ver)
全Taskの待ち合わせはawait Task.WhenAllが担います。
待機完了時点でTaskは結果を持っているので、最後にResultを取り出して終了です。

実行すると直列verのときと同じように表示されます。

時間計測

実際にブラウザからデータ取得して、タイムラインを見てみます。

何回かやってみたところ、600~800msかかりました。
3つのうちどれかが、他の2つを待たせているような感じがします。

一方、並列化していない状態ではどうでしょうか。

900~1100msでした。
またSiteDataだけやけに長いようです。
単項目のオブジェクトなので一番レスポンスが早くなる気がするのですが、予想に反してこのような結果になりました。
もしかしたらですが、最初のリクエストだけ認証が行われているのかなぁと思います。
そこで取得の順番を変えてみました。

やはり先頭だけ時間がかかるようです。
並列の場合はどうなってるんかなぁ。

取得データ内容の削減

ブログ一覧表示に必要なものは「タイトル」「タグ」「公開日」だけです。

MicroCMSのAPIはクエリパラメータでほしい項目だけ選ぶことができます。
ここまできて申し訳ないのですが、いろいろ修正します。(突貫工事の悪いところ)

設定周り

appsetting.json
// 抜粋
  "MicroCMS": {
    // ...
    // 修正
    "Endpoints": {
      "SiteData": "sitedata",
      "TagList": "tag",
      "BlogList": "blog",
      "BlogContents": "blog/#id"
    },
    // 追加
    "QueryParam": {
      "SiteData": "?fields=title",
      "TagList": "?fields=id,name",
      "BlogList": "?fields=title,publishedAt,tag.id,tag.name"
    }
  }
MicroCMSSettingmodel.cs
namespace BlazorBlog.Server
{
    public class MicroCMSSettingModel
    {
        public string BaseUrl { get; set; } 

        public string ApiKey { get; set; }

        public MicroCMSEndpointsSettingModel Endpoints { get; set; }

        public MicroCMSQueryParamSettingModel QueryParam { get; set; }
    }

    public class MicroCMSEndpointsSettingModel
    {
        public string SiteData { get; set; }

        public string TagList { get; set; }

        public string BlogList { get; set; }

        public string BlogContents { get; set; }
    }

    public class MicroCMSQueryParamSettingModel
    {
        public string SiteData { get; set; }

        public string TagList { get; set; }

        public string BlogList { get; set; }

        public string BlogListByTag { get; set; }
    }
}

取得データモデル

必要な項目にしぼります。またクラス名が若干変わっています。

SiteDataModel.cs
// クラス名変更 SiteData -> SiteDataModel
using System.Text.Json.Serialization;

namespace BlazorBlog.Shared.MicroCMS
{
    public class SiteDataModel
    {
        [JsonPropertyName("title")]
        public string BlogTitle { get; set; }
    }
}
TagModel.cs
// クラス名変更 Tags -> TagModel
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace BlazorBlog.Shared.MicroCMS
{
    public class TagModel
    {
        [JsonPropertyName("contents")]
        public List<TagContents> TagList { get; set; }

        public int TotalCount { get; set; }
        
        public int Offset { get; set; }

        public int Limit { get; set; }
    }

    public class TagContents
    {
        public string Id { get; set; }

        public string Name { get; set; }
    }
}
BlogModel.cs
// クラス名変更 Blogs -> BlogModel
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace BlazorBlog.Shared.MicroCMS
{
    public class BlogModel
    {
        [JsonPropertyName("contents")]
        public List<BlogContents> BlogList { get; set; }

        public int TotalCount { get; set; }

        public int Offset { get; set; }

        public int Limit { get; set; }
    }

    public class BlogContents
    {
        public string Id { get; set; }

        public DateTime PublishedAt { get; set; }

        public string Title { get; set; }

        public TagContents Tag { get; set; }

        public string Body { get; set; }
    }
}

コントローラー

MicroCMSController.cs
// 抜粋
// 適宜クラス名も変わっています
namespace BlazorBlog.Server.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class MicroCMSController : ControllerBase
    {
    // ...
        /// <summary>
        /// サイトデータを取得します。
        /// </summary>
        /// <returns>サイトデータ</returns>
        [Route("SiteData")]
        public async Task<SiteDataModel> GetSiteData()
        {
	    // クエリパラメータ追加
            string url = apiSettings.BaseUrl + apiSettings.Endpoints.SiteData + apiSettings.QueryParam.SiteData;
            string apikey = apiSettings.ApiKey;

            SiteDataModel result = await Fetch<SiteDataModel>(url, apikey);

            return result;
        }

        /// <summary>
        /// タグ一覧を取得します。
        /// </summary>
        /// <returns>タグ一覧</returns>
        [Route("TagList")]
        public async Task<TagModel> GetTagList()
        {
	    // クエリパラメータ追加
            string url = apiSettings.BaseUrl + apiSettings.Endpoints.TagList + apiSettings.QueryParam.TagList;
            string apikey = apiSettings.ApiKey;

            TagModel result = await Fetch<TagModel>(url, apikey);

            return result;
        }

        /// <summary>
        /// 全てのブログ記事一覧を取得します
        /// </summary>
        /// <returns>ブログ記事一覧</returns>
        [Route("BlogList")]
        public async Task<BlogModel> GetBlogList()
        {
	    // クエリパラメータ追加
            string url = apiSettings.BaseUrl + apiSettings.Endpoints.BlogList + apiSettings.QueryParam.BlogList;
            string apikey = apiSettings.ApiKey;

            BlogModel result = await Fetch<BlogModel>(url, apikey);

            return result;
        }
    // ...
    }
}

問題なく画面表示できて、コンソールにエラーが出ないことを確認してください。
追加したアクションはまだ完成してないので、思うようには動きません。

データ量比較

変更前

変更後

結構節約できました。
PCだとあまり関係ないですが、スマホで見るときはデータ量少ないほうがありがたいですからね。

ちょこっとスタイリング

BlogCard.razor
// 抜粋
<div class="row my-2 d-flex justify-content-center">
    <!-- border系クラス削除、cardbox追加 -->
    <div class="col-12 col-md-8 cardbox">
        <div class="m-3">
            <h4>@title</h4>
            <div>Tag:@tag</div>
            <div>Publish:@publishedAt.ToLocalTime().ToString("yyyy/MM/dd")</div>
        </div>
    </div>
</div>
app.css
// 追加
.cardbox {
    padding: 0.5em 1em;
    margin: 1em 0;
    background: #f4f4f4;
    border-left: solid 6px #5bb7ae;
    box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.33);
}

こんな感じになります。しれっとCounterページ消しました。
あとデザインはここからパクりました。
https://saruwakakun.com/html-css/reference/box

CSSを手書きすることってあまりないなぁ。

次回

  • 記事をタグで絞り込む機能を実装(イベント処理)
  • ブログ記事を表示する
    • ページ作成
    • シンタックスハイライト

ここまで来て言うのもアレですが、誤字脱字あったらすみません(ヽ´ω`)

Discussion