今回達成すること
VuexにContentfulの型定義ファイルを作成し、state
の型付けを行います。
型定義ファイルを作成する
Vuexストアの型定義ファイルは、storeディレクトリ直下のtypesディレクトリ内に作成します。
% mkdir store/types && touch $_/index.ts
Vuexストアの型定義
作成した
今後、コードが増えてきたり別のVueファイルの型定義を行う場合は、このstore/typesディレクトリにファイルを追加していきます。
完全にローカルで使用する型は、そのtsファイルに定義します。
Contentfulのインターフェースを定義する
- コンテンツIDなどが格納されている
ContentfulSys
と - メディアイメージの
ContentfulImage
の型を定義します。
store/types/index.ts
export interface ContentfulSys {
id: string
createdAt: string
updatedAt: string
sys: {
id: string
}
contentType: {
sys: {
id: string
}
}
}
export interface ContentfulImage {
fields: {
title: string
file: {
url: string
details: {
image: {
width: number
height: number
}
}
}
}
}
Contentfulモデルの型をfirlds
プロパティに追加します。
筆者はBlogCategory
とBlogTag
を定義します。
ここはご自身のモデルフィールドに合わせてください。
store/types/index.ts
export interface BlogCategory {
sys: ContentfulSys
fields: {
// Not Required
image?: ContentfulImage
title: string
description: string
sort: number
slug: string
isMain: boolean
}
}
export interface BlogTag {
sys: ContentfulSys
fields: {
// Not Required
image?: ContentfulImage
title: string
slug: string
}
}
-
image?
... Contentful上で必須プロパティでない場合は、?
を付けてundefined
を定義する。image? = ContentfulImage | undefined
関連づくモデルのインターフェース
関連するモデルはプロパティ名にインターフェースを指定します。
1対多の関連付けは配列で定義します。
store/types/index.ts
export interface BlogPost {
sys: ContentfulSys
fields: {
// Reference 1:1(Required)
category: BlogCategory
// Required
image: ContentfulImage
title: string
slug: string
body: string
publishDate: string
// References 1:多(Not Required)
tags?: BlogTag[]
}
}
publishDate
... ContentfulのDateはString型で返される。
今回のストア設計とnuxtServerInit()の実行場所
今回Contentfulから取得するデータは、全てstate
に集約します。
これは、サーバーサイドでリクエストを行うnuxtServerInit()
を使用するためです。
nuxtServerInit()
は、
そのため、state
に関連する処理を集約しています。
index.ts state
... Contentfulの全データを保管。mutations
... Contentfulstate
データの代入処理を記述。actions
...nuxtServerInit()
でContentful APIのサーバーサイドリクエスト実行。
APIリクエストを行うActionメソッドについては、モデル単位でファイルを切り分けます。
-
post/index.ts (次回作成)-
actions
... BlogPostモデルのContentful APIリクエスト処理を記述。リクエスト実行は
index.ts のnuxtServerInit()
内で行う。
-
Vueストアの型定義
state
に、ブログコンテンツを保管するデータを用意します。
これまでの
store/index.ts
// typed-vuex setup: https://typed-vuex.roe.dev/getting-started/getting-started-nuxt
import { getAccessorType, getterTree, mutationTree, actionTree } from 'typed-vuex'
import { BlogCategory, BlogPost, BlogTag } from './types'
export const state = () => ({
categories: [] as BlogCategory[],
posts: [] as BlogPost[],
tags: [] as BlogTag[]
})
type RootState = ReturnType<typeof state>
export const getters = getterTree(state, {})
export const mutations = mutationTree(state, {})
export const actions = actionTree({ state, getters, mutations }, {})
export const accessorType = getAccessorType({
state,
getters,
mutations,
actions,
modules: {
/* sub modules */
}
})
Mutationの作成
mutations
には以下の関数を追加します。
store/index.ts
export const mutations = mutationTree(state, {
setCategories (state: RootState, payload: BlogCategory[]) {
state.categories = payload
},
setPosts (state: RootState, payload: BlogPost[]) {
state.posts = payload
},
setTags (state: RootState, payload: BlogTag[]) {
state.tags = payload
}
})
Actionメソッドの作成
actions
内はnuxtServerInit()
メソッドと、Contentfulのデータ取得メソッドを追加します。
nuxtServerInit()
の第二引数にはNuxtContextが取得できるので、Context
型を@nuxt/types
からインポートします。
store/index.ts
// typed-vuex setup: https://typed-vuex.roe.dev/getting-started/getting-started-nuxt
// 一番上に追加 NuxtContextの型をインポート
import { Context } from '@nuxt/types'
...
export const actions = actionTree({ state, getters, mutations }, {
// server only
async nuxtServerInit ({ dispatch }, _nuxtContext: Context) {
await dispatch('getContentfulApi')
},
// Contentful APIリクエストを集約
async getContentfulApi ({ dispatch }) {
await dispatch('post/getEntries')
}
})
最終的なstore/index.ts
最終的に
store/index.ts
// typed-vuex setup: https://typed-vuex.roe.dev/getting-started/getting-started-nuxt
import { Context } from '@nuxt/types'
import { getAccessorType, getterTree, mutationTree, actionTree } from 'typed-vuex'
import { BlogCategory, BlogPost, BlogTag } from './types'
export const state = () => ({
categories: [] as BlogCategory[],
posts: [] as BlogPost[],
tags: [] as BlogTag[]
})
type RootState = ReturnType<typeof state>
export const getters = getterTree(state, {})
export const mutations = mutationTree(state, {
setCategories (state: RootState, payload: BlogCategory[]) {
state.categories = payload
},
setPosts (state: RootState, payload: BlogPost[]) {
state.posts = payload
},
setTags (state: RootState, payload: BlogTag[]) {
state.tags = payload
}
})
export const actions = actionTree({ state, getters, mutations }, {
// server only
async nuxtServerInit ({ dispatch }, _nuxtContext: Context) {
await dispatch('getContentfulApi')
},
// Contentful APIリクエストを集約
async getContentfulApi ({ dispatch }) {
await dispatch('post/getEntries')
}
})
export const accessorType = getAccessorType({
state,
getters,
mutations,
actions,
modules: {
/* sub modules */
}
})
今回の作業は以上です。
% git add -A
% git commit -m "Add type file to store directory" -m "Add contentful data to vuex store"
次回は?
今回はVuexにContentfulの型とstate
を用意しました。
次回は、このstate
にContentful APIで取得したコンテンツを代入し、Vueファイルで表示します。