以前読んだ、Software Design 2020 年 9 月号 で Vue.js の特集がありました。
この企画の記事の中で、ライブラリを使わないデータストアについての、紹介があります。
小規模アプリなら十分使えそうなものだとその時感じていました。
このライブラリを使わないデータストア相当のものを、Vue 3 Composition API で書けないかと試したので、メモです。
要件
- 外部のライブラリを使わない
- なるべく簡単に使える
- よくある共通の値を参照しているカウンタを作る
実装
準備
開発環境の準備は、viteで用意する。
1 2 3 4 5 6
| npm init @vitejs/app app --template vue-ts
cd app
npm install npm run dev
|
これで Vue.js のよく見る初期画面を確認できる。
データストアを作る
次のようにデータストアを作ってみました。
データストアを作るというよりも、データの作成と更新をコンポーネントから切り出した感じですね。
app/src/store/store.ts1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { ref, computed, ComputedRef } from "vue";
export interface store { count: ComputedRef<number>; up: () => void; down: () => void; }
const _count = ref(0);
const up = () => { _count.value++; };
const down = () => { _count.value--; };
const count = computed(() => _count.value);
export default { count, up, down };
|
自作データストアを使う
続けて、自作したデータストアを使ってみます。
app/src/components/counter.vue1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <template> <div class="counter"> Count: {{ count }} <button @click="up">count ++</button> <button @click="down">count --</button> </div> </template>
<script lang="ts"> import { defineComponent } from "vue"; import store from "../store/store";
export default defineComponent({ setup: () => { const { count, up, down } = store; return { count, up, down }; }, }); </script>
<style scoped> .counter { margin: 10px; } </style>
|
この counter.vue
の呼び出し側は次のようになります。
app/src/App.vue1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <template> <div> <Counter /> <Counter /> </div> </template>
<script lang="ts"> import { defineComponent } from "vue"; import Counter from "./components/counter.vue";
export default defineComponent({ name: "App", components: { Counter, }, }); </script>
<style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
|
動作確認
npm run dev
で起動すると次のようになります。
カウンターのコンポーネント 2 つの間で値を共有して取り扱うことができました。
データストアを provide(2021-05-23 追記)
今回より簡単なただ 1 つの変数を provide する記事を前に書いていたことを、この記事公開してから気が付きました。
今回作ったデータストアも provide で公開して、inject で使用してみます。
ストアの修正
呼び出しに使用するキーを記述します。
app/src/store/store.ts1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import { ref, computed, ComputedRef, InjectionKey } from "vue";
export interface store { count: ComputedRef<number>; up: () => void; down: () => void; }
export const storeKey: InjectionKey<store> = Symbol("store");
const _count = ref(0);
const up = () => { _count.value++; }; const down = () => { _count.value--; };
const count = computed(() => _count.value);
export default { count, up, down };
|
provide で登録する
app/main.ts1 2 3 4 5 6 7 8
| import { createApp } from "vue"; import App from "./App.vue"; import store, { storeKey } from "./store/store";
const app = createApp(App);
app.provide(storeKey, store); app.mount("#app");
|
inject で呼び出す
provide で登録したデータを、inject で呼び出してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <template> <div class="counter"> Count: {{ count }} <button @click="up">count ++</button> <button @click="down">count --</button> </div> </template>
<script lang="ts"> import { defineComponent, inject } from "vue"; import { store, storeKey } from "../store/store";
export default defineComponent({ setup: () => { const { count, up, down } = inject(storeKey) as store; return { count, up, down }; }, }); </script>
<style scoped> .counter { margin: 10px; } </style>
|
画面を確認すると、先に作ったものと同様に動きます。
データ部分だけの切り出しを行ってみた今回。
リアクティブなデータで結果を共有し、API のレスポンスをキャッシュできるようにしたりすると、より面白そうです。
ではでは。