LoginSignup
2

More than 3 years have passed since last update.

Nuxt で IT イベント検索アプリを作成した

Posted at

はじめに

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.vuev-ifしてた

以下は簡単な layouts コンポーネントを作成した

layouts/default.vue
<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 オプションを指定すれば良い

layouts/sample.vue
<template>
  <v-app>
    <v-container>
      <nuxt />
    </v-container>
  </v-app>
</template>
pages/index.vue
<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 をインストールした場合は、既に設定済み)

nuxt.config.js
module.exports = {
  plugins: ['@/plugins/vuetify']
}

@は webpack などでよく使われるエイリアスでデフォルトはprocess.cwd()が指定されている

axios の使い方

create-nuxt-appで Axios を選択していれば、axios を拡張したaxios-moduleが利用可能になっている
Axios を選択していなくても、以下で使えるようになると思う

yarn add -D @nuxtjs/axios
nuxt.config.js
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
nuxt.config.js
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 があるとする

store/index.ts
import { IEventsState } from '@/store/events'
import { IConditionState } from '@/store/condition'

export interface IState {
  events: IEventsState
  condition: IConditionState
}

ヘルパー関数を実装した StoreHelper クラスを作成する

components/mixins/StoreHelper.vue
<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
Sample.vue
<style lang="scss">
...
</style>

ホスティングする

Netlify でやるとかなり簡単
ローカルでyarn generate(nuxt generate)が正常に動いていれば、Netlify 側で以下の設定をすれば良い

 2019-06-14 17.49.08.png

さいごに

まだ触れてないnuxt/middlewareとかサーバー周りとか PWA とかはまた次回何か作ろうと思います

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2