C# Blazorで作ったサイトをVue.jsで作り直してみた

タイトルの通りのことをやってみました。

結論から言うと、Blazorをやっているとvueの学習コストが下がるので「dotnetしかやったことないよ！」という人にはVueはお勧めできるかと思います。

Blazorで作った話はこちら↓

* [BlazorでSkclusive-UIを使った話](https://qiita.com/sYamaz/items/d0b12043f5b25a36d8e6)
* ページ：https://syamaz.github.io/website/

今回作ったものはこちら↓

* ページ：https://syamaz.github.io/website-vue/

## 環境

* Vue3
* Vue-router@4
* Vuetify 3.0.0-alpha
* Vite
* gh-pages

## vueコンポーネントとrazorコンポーネント

コンポーネントにはスタイル、テンプレート（UI）、スクリプトが含まれるという点でvueコンポーネントとrazorコンポーネントはほぼ同じ役割を持たせることができます

以下、Materialデザインの「カード」を列挙するページについてのコンポーネントです。

##### vue

https://github.com/sYamaz/website-vue/blob/main/src/components/Works.vue

<details><summary>コード</summary><div>

```vuejs
<template>
  <v-container>
  <h1>Works</h1>
  <v-container v-for="app in apps" v-bind:key="app">
    <v-card>
      <v-card-title>{{app.name}}</v-card-title>
      <v-card-media>
        <v-img :src="app.img"></v-img>
      </v-card-media>
      <v-card-text>{{ app.text }}</v-card-text>

      
      <v-card-subtitle>Platform {{ app.platform.join(", ") }}</v-card-subtitle>
      <v-card-subtitle>Status {{app.status}}</v-card-subtitle>
      <v-card-text></v-card-text>
      <v-divider></v-divider>
      <v-card-actions>
        <v-btn v-if="app.url != ''" :to="app.url"><span class="text-info">Read more</span></v-btn>
        <v-btn v-if="app.outerurl != ''" :href="app.outerurl"><span class="text-info">Read more</span></v-btn>
      </v-card-actions>
    </v-card>
  </v-container>
  </v-container>
</template>

<script setup>
import routineTreeImg from "../assets/RoutineTree.png"
const apps = [
  {
    name: "RoutineTree",
    img: routineTreeImg,
    text: "Task management application that helps you accomplish your daily routine tasks.",
    platform: ["iOS"],
    status: "In Review",
    url: "",
    outerurl: "https://syamaz.github.io/RoutineTree/"
  },
  {
    name: "My homepage",
    img: "",
    text: "This website.",
    platform: ["web"],
    status: "Released",
    url: "/",
    outerurl: ""
  }
]
</script>

<style>
</style>
```

</div></details>


##### Blazor

https://github.com/sYamaz/website/blob/main/website/Pages/Works/WorksPage.razor

<details><summary>コード</summary><div>

```razor
@page "/works"

@inject NavigationManager navman
@using website.Components
@using website.Pages.Works.Parts
@using website.Pages.Works.Datas
<style>
    .styled-linkbutton {
        @*縦並び*@
        display: block;
        text-transform:none;
    }

        .styled-linkbutton:is(:hover) {
            text-decoration: underline;
        }
</style>

<PageTitle>Works - sYamaz</PageTitle>


<Typography Variant="TypographyVariant.H5">
    Works
</Typography>


@foreach (var item in Datas)
{
    <Box Padding="2" Margin="2">
        <WorkCardView WorkData="@item" />
    </Box>
}



@code {



    private IEnumerable<AnyWorkData> Datas
    {
        get
        {


            yield return new AnyWorkData
            {
                Title = "RoutineTree",
                Description = "Task management application that helps you accomplish your daily routine tasks.",
                ReadMoreURL = "https://syamaz.github.io/RoutineTree/",
                ImagePath = "images/RoutineTree.png",
                Status = WorkStatus.inReview,
                SupportPlatform = SupportPlatform.iOS
            };

            yield return new AnyWorkData
            {
                Title = "Some web app/service",
                Description = "My practice project using Vue and AWS",
                ReadMoreURL = "",
                ImagePath = "images/Noimage.png",
                Status = WorkStatus.underDevelop,
                SupportPlatform = SupportPlatform.web
            };

            // --- end ---

            yield return new AnyWorkData
            {
                Title = "My homepage",
                Description = "This website.",
                ReadMoreURL = "",
                ImagePath = "images/Noimage.png",
                Status = WorkStatus.release,
                SupportPlatform = SupportPlatform.web
            };
        }
    }


}

```

</div></details>


## ルーティング

Blazorの場合、デフォルトでルーティング機能が付いてます。

`@page "/{url}"`をコンポーネント内で宣言することで他ページから遷移することができます。

vueでvue-routerを使う場合はコンポーネントで宣言というよりは一括で宣言することになります。（ドキュメントを十分に読み込めてないだけで、別の方法があるかもしれません）

##### vue

https://github.com/sYamaz/website-vue/blob/main/src/router/index.js

<details><summary>コード</summary><div>

```js
import { createRouter, createWebHistory } from 'vue-router'
// ページのコンポーネントをインポート
import AboutVue from '../components/About.vue'
import WorksVue from '../components/Works.vue'
import PostsVue from '../components/Posts.vue'


const routes = [
  {
    path: '/',
    name: 'About',
    component: AboutVue
  },
  {
    path: '/works',
    name: 'Works',
    component: WorksVue
  },
  {
    path: '/posts',
    name: 'Posts',
    component: PostsVue
  },
]


const baseURL = import.meta.env.BASE_URL;
console.log("base : " + baseURL)

const router = createRouter({
  history: createWebHistory(baseURL),
  routes
})

export default router
```

</div></details>

## GitHub Pages

基本的に楽をしたいため、`gh-pages`npmパッケージを使用します。

vite.config.jsにデバッグ時と公開時でBaseURLをスイッチする定義を追加

```js
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vuetify from '@vuetify/vite-plugin'

import path from 'path'

// https://vitejs.dev/config/
export default defineConfig({
  // 略
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  },
  // この一行を追加　'/{Githubリポジトリ名}'とか'/'とか
  base: process.env.NODE_ENV === 'production' ? '{公開時}' : '{デバッグ時}',

  // 略
})

```

このbaseプロパティをvue-routerで使用します

https://ja.vitejs.dev/guide/env-and-mode.html

>import.meta.env.BASE_URL: {string} アプリが配信されているベース URL。これは base 設定オプション によって決まります。



https://github.com/sYamaz/website-vue/blob/main/src/router/index.js

```js
//vue-router@4

const baseURL = import.meta.env.BASE_URL;

const router = createRouter({
  history: createWebHistory(baseURL),
  routes
})

export default router
```

# まとめ

dotnet開発者が→Webに手を広げていく際の一つの道が、「WinForm/WPF/UWP」→「Blazor」→「vue」なのかもしれません