これはクソアプリ2アドベントカレンダー11日目の記事です。
今回、9月に正式リリースされたVue3を使ってとてつもなくシンプルなクソアプリを作ったので、その制作について話をします。
作ったアプリ
このアプリのクソポイント
- UIがクソ
- ろくすっぽスタイルがあたってません(そもそも要素が少ないというのもある)
- 機能がクソ
- 機能は、入力された名前にランダム文字列を追加してAPIに投げ、画像を取得して表示するだけです
このアプリの制作趣意
今回は、Vite Appを使い、Vue3とTypeScriptで、以下を学ぶことを目標としていました。
- Composition API
- Provide/Inject
制作過程
環境構築
まず、Vite AppでVue3環境を構築します。
TypeScriptを使いたいので、--template
フラグを利用してTypeScriptのテンプレートを利用します。
$ npm init vite-app <project-name> -- --template vue-ts
# or
$ yarn create vite-app <project-name> --template vue-ts
この時点でのディレクトリ構造はこちら
.
├── index.html
├── package.json
├── public
│ └── favicon.ico
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ ├── index.css
│ ├── main.ts
│ └── vue-app-env.d.ts
├── tsconfig.json
└── yarn.lock
ここからこねこねして、最終的に以下のディレクトリ構造になりました
.
├── README.md
├── index.html
├── package.json
├── src
│ ├── App.vue
│ ├── assets
│ │ └── style
│ ├── components
│ │ ├── AnswerBtn.vue
│ │ ├── InputName.vue
│ │ ├── ResetBtn.vue
│ │ ├── SwichingView.vue
│ │ └── UserImage.vue
│ ├── main.ts
│ ├── store
│ │ └── useState
│ │ ├── model.ts
│ │ └── provider.ts
│ └── vue-app-env.d.ts
├── tsconfig.json
└── yarn.lock
親コンポーネントにProvideを設置
前述の通り、今回のアプリの状態管理にはProvide/Injectを採用してます。これは親コンポーネントの状態を子コンポーネントに状態を受け渡すことができる仕組みで、Propsと違って子コンポーネントであればどこまでも受け渡すことができるので、Propsのようなバケツリレーをすることがありません。
正直、今回の構成だとPropsで問題ないんですが、これを試すのが目的なので、使っています。
まず親コンポーネントで、受け渡す値をuseState
という関数にまとめて別ファイルで変数に格納します。
export const useState = () => {
const state = reactive({
name: 'whyk',
newname: '',
answered: false,
clicked: false,
})
const updateName = (newname: string) => {
const randString = Math.random().toString(32).substring(2)
state.name = `${newname}_${randString}`
}
const updateAnswered = () => {
state.answered = !state.answered
}
const updateClicked = () => {
state.clicked = true
}
const resetState = () => {
state.name = 'whyk'
state.newname = ''
state.answered = false
state.clicked = false
}
return {
state,
updateName,
updateAnswered,
updateClicked,
resetState,
}
}
また、この関数を識別するキーとして、シンボル化した値を変数に格納します。
型については、InjectionKey
はProvide/Injectのキーとして利用される型定義、型変数にはStateStore
という、また別の場所で作成された上記関数の返り値を型として利用できるようにしたものになります。
export const StateKey: InjectionKey<StateStore> = Symbol('StateStore')
これらを親コンポーネントであるsrc/App.vue
で読み込みます。
import { defineComponent, provide } from 'vue'
import { useState, StateKey } from './store/useState/provider'
export default defineComponent({
setup() {
provide(StateKey, useState())
return {}
},
})
これで、子コンポーネントへの状態の受け渡しの準備が整いました。
子コンポーネントにInjectを設置
次に、子コンポーネントを作成し、親コンポーネントから状態を受け取ります。
import { defineComponent, inject } from 'vue'
import type { StateStore } from '../store/useState/model'
import { StateKey } from '../store/useState/provider'
export default defineComponent({
setup() {
const { state } = inject(StateKey) as StateStore
return {
state,
}
},
})
Vueから提供されているinject
にProvide/Injectのキーを渡し、型アサーションで型をuseState
の返り値で上書きします。
これで親コンポーネントから状態が受け取れるようになりました。
そのあと
Provide/Injectを使う関係で、すべての要素を子コンポーネントにする必要があります。
そのため、最終的な親コンポーネントは以下のようになっています。
<template>
<user-image />
<h1>Whats Your Name?</h1>
<switching-view>
<template #question>
<input-name />
<answer-btn />
</template>
<template #answer>
<p>Hahaha!! Very suitable!!!!</p>
<reset-btn />
</template>
</switching-view>
</template>
<script lang="ts">
import { defineComponent, provide } from 'vue'
import SwitchingView from './components/SwichingView.vue'
import UserImage from './components/UserImage.vue'
import InputName from './components/InputName.vue'
import AnswerBtn from './components/AnswerBtn.vue'
import ResetBtn from './components/ResetBtn.vue'
import { useState, StateKey } from './store/useState/provider'
export default defineComponent({
components: {
SwitchingView,
UserImage,
InputName,
AnswerBtn,
ResetBtn,
},
setup() {
provide(StateKey, useState())
return {}
},
})
</script>
最後に
Advent Calendar初参加でしたが、本来出すはずのクソアプリが完成せず、急きょ別のクソアプリで記事を書きました。
ここに出すはずだったクソアプリを作るプランも並行して考えているので、また違うフレームワークで作ってみたいところです。