はじめに
Nuxt を勉強中です
一応前回の続きですが、前回はほぼ環境構築で終わってしまったので、今回は簡単なアプリを作成しました
[ホスティング先]
https://event-search.netlify.com
[GitHub]
https://github.com/kurosame/event-search
簡単に説明すると、IT 系のイベントサイトからイベント一覧を取得する API を叩いて、取得したものをテーブルに表示しているだけのアプリです
まだあまり使い物にならないですが、イベントサイトを増やしたり、検索機能などを使いやすくしたり、CI で定期的に取得して Slack に通知するなどしたら普通に使えそうなので、もう少しちゃんと作ろうと思います
現在は、以下の縛りがあります
- connpass と ATND のみ対応
- 定員が 30 人以上のイベントに限定
- 本日から来月末までの期間のイベントを取得
開発してて、いくつか気になった点をピックアップして書こうと思います
layouts, pages, components の違いについて
コンポーネントを作る際にディレクトリ構成を眺めると、Vue ファイルを置くディレクトリが 3 つある
pages, components, layouts のこれらの概念を先に理解する必要がある
pages はルーティングで呼ばれるコンポーネントを配置する
components は pages コンポーネントから呼ばれるコンポーネントを配置する
layouts については、この図が分かりやすい
layouts は pages よりも 1 つ上のレイヤーで外観(ヘッダーやフッターなど)が異なるページが必要な時、それぞれの外観をコンポーネントとして作成し、layouts 配下に置いておけば、pages コンポーネントから layout オプションで好きな外観コンポーネントを指定できる
layouts はとても便利な機能だと思う
Nuxt を使ってない時はログイン画面などヘッダーやフッターが必要ない画面はApp.vue
でv-if
してた
以下は簡単な layouts コンポーネントを作成した
<template>
<v-app dark>
<v-container>
<nuxt />
</v-container>
</v-app>
</template>
<nuxt />
は layouts コンポーネントでのみ許される機能で pages コンポーネントを呼び出すことができる
ちゃんと調べてないけど<router-view />
みたいなものだと思う
また、default.vue
という名前で作成しているので、pages コンポーネントで layout オプションを指定していなければ、この layouts コンポーネントが呼ばれる
任意の layouts コンポーネントを使う場合は、default 以外の名前で Vue ファイルを作成し、pages コンポーネントで layout オプションを指定すれば良い
<template>
<v-app>
<v-container>
<nuxt />
</v-container>
</v-app>
</template>
<script>
export default {
layout: 'sample'
}
</script>
plugins の使い方
Nuxt をcreate-nuxt-app
でインストールする際に、UI フレームワークで Vuetify を選択したので、サンプルコードのコンポーネントが Vuetify で作られた
そのコンポーネントを見るとVue.use(Vuetify)
をしていないので、どこかでグローバルレベルで使用できるようにしているはずである
それを行っているのが plugins である
Vue で外部モジュールを使う場合、new Vue(...)
する前にVue.use(...)
する必要がある
plugins ディレクトリ内に JS ファイルを入れておくと、Vue のインスタンスが作られる前に実行されるので、その JS でVue.use(...)
しておけば良い
また、nuxt.config.js
に以下の設定が必要
(create-nuxt-app
で Vuetify をインストールした場合は、既に設定済み)
module.exports = {
plugins: ['@/plugins/vuetify']
}
@
は webpack などでよく使われるエイリアスでデフォルトはprocess.cwd()
が指定されている
axios の使い方
create-nuxt-app
で Axios を選択していれば、axios を拡張したaxios-moduleが利用可能になっている
Axios を選択していなくても、以下で使えるようになると思う
yarn add -D @nuxtjs/axios
module.exports = {
modules: ['@nuxtjs/axios']
}
Vuex での簡単な使い方
export const actions = {
async getApi({ commit }) {
const res = await this.$axios.$get('http://...')
}
}
別ドメインからリソースを取得する場合の CORS 対策について
以下のように別ドメインの API を叩くようなケースがあった場合、CORS ポリシーによりブロックされる
const res = await this.$axios.$get('https://connpass.com/api/v1/event', {
params: {
keyword: 'python'
}
})
今回は API 側にオリジンを許可させることはできない
Nuxt での解決策としては、axios の proxy 機能を使うのが良いらしい
yarn add -D @nuxtjs/proxy
module.exports = {
modules: ['@nuxtjs/axios', '@nuxtjs/proxy'],
proxy: {
'/connpass': {
target: 'https://connpass.com/api/v1/event',
pathRewrite: {
'^/connpass': '/'
}
}
}
}
リクエストをプロキシして API と同一ドメインで叩けるようにしている
静的サイトの場合、proxy が機能しない
nuxt generate
で Netlify などにホスティングした場合、proxy が機能しない
ちゃんと GitHub のREADMEに「Does not work in generated/static mode!」って書いてあった
今回は Netlify Functions を利用して、この問題を回避した
Netlify Functions を使って CORS エラーを回避する
Vuex を型安全にしたい
Nuxt に限ったことではないが、例えば Vue 内のthis.$store
は型定義がStore<any>
なので、中身の State は any 型である
型安全にしたいと思い、「vuex typescript」などでググるといろんなやり方が出てくる
正直スタンダードなやり方は分からなかった
今回作るアプリは小規模ということもあり、一旦 Vue 内にヘルパー関数を実装し、Mixin させることにした
(後でいいやり方が見つかれば、簡単に捨てれるので)
目標としてはStore<state1 | state2>
のような型を付けて、パラメータで渡したモジュール名の State を返してくれるヘルパー関数を作成する
以下のように各 State をまとめた RootState を定義
events と condition というモジュールの State があるとする
import { IEventsState } from '@/store/events'
import { IConditionState } from '@/store/condition'
export interface IState {
events: IEventsState
condition: IConditionState
}
ヘルパー関数を実装した StoreHelper クラスを作成する
<script lang="ts">
import { IState } from '@/store/index'
import { Component, Vue } from 'vue-property-decorator'
@Component
class StoreHelper extends Vue {
getState<K extends keyof IState>(key: K): IState[K] {
return this.$store.state[key]
}
}
export default StoreHelper
</script>
State を参照したい Vue で StoreHelper を Mixin して、getState('モジュール名')
を呼び出す
<script lang="ts">
import StoreHelper from '@/components/mixins/StoreHelper.vue'
import { Component, Mixins } from 'vue-property-decorator'
@Component
class Summary extends Mixins(StoreHelper) {
get items(): [] {
return this.getState('events').events
}
}
export default Summary
</script>
VSCode 等で試すといい感じに補完してくれると思う
SCSS を使う
yarn add -D node-sass sass-loader
<style lang="scss">
...
</style>
ホスティングする
Netlify でやるとかなり簡単
ローカルでyarn generate
(nuxt generate
)が正常に動いていれば、Netlify 側で以下の設定をすれば良い
さいごに
まだ触れてないnuxt/middleware
とかサーバー周りとか PWA とかはまた次回何か作ろうと思います