🐕‍🦺

Nuxt.jsとTypeScriptで開発環境を構築する

2021/06/14に公開

Nuxt.jsとTypeScriptを用いた開発が増えてきたので、自分なりの環境構築や開発の方法をまとめてみました。

前提

yarn -v
1.22.5
node -v
v14.16.1

最終的なpackage.jsonのdependenciesは

  "dependencies": {
    "@nuxtjs/axios": "^5.13.1",
    "core-js": "^3.9.1",
    "nuxt": "^2.15.3",
    "nuxt-buefy": "^0.4.4",
    "nuxt-typed-vuex": "^0.2.0"
  },

です。

プロジェクトの作成

まず、以下のコマンドでNuxt.jsのプロジェクトを作成します。

yarn create nuxt-app <project-name>

オプションでいくつか質問されます。
今回は以下のように回答しました。

? Project name: app
? Programming language: TypeScript
? Package manager: Yarn
? UI framework: Buefy
? Nuxt.js modules: Axios - Promise based HTTP client
? Linting tools: ESLint, Prettier, Lint staged files, StyleLint
? Testing framework: Jest
? Rendering mode: Single Page App
? Deployment target: Static (Static/Jamstack hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Continuous integration: None
? Version control system: Git

ブラウザでの確認

以下のコマンドを用いることで、開発中の画面(http://localhost:3000/)をブラウザで確認できます。

yarn dev

ポイント

Can't create . because there's already a non-empty directory . existing in path.

プロジェクト作成時のコマンドの引数であるproject-nameは省略できますが、ディレクトリが空でない場合エラーが出るので注意が必要です。

「このページは動作していません」と表示される場合

server: {
  host: '0.0.0.0',
}

Nuxt.jsのデフォルトではホストマシンからのみアクセス可能です。
外部からのアクセスを許容する場合は、上記のような設定が必要になります。
詳しくは公式ドキュメントのホストとポート番号を編集する
を参照してください。

TypeScriptをVue.jsで使用

Nuxt.jsでTypeScriptを使用する場合、セットアップはほぼ初期設定のみ(Programming languageの質問でTypeScriptを選択する)で大丈夫ですが、実際に開発を進める際にファイルの種類毎にお作法のようなものがあります。
ここでは、実際に使用する場合の参考コードを紹介します。
動作確認用のコードを含んでいるので、実際に使用する場合は適宜修正して利用してください。

コンポーネント

scriptタグのlang属性を指定し、Vue.extendを使用することでTypeScriptを使用できるようになります。

pages/index.vue
<script lang="ts">
import Vue from 'vue'
import Card from '~/components/Card.vue'

export default Vue.extend({
  components: {
    Card,
  },
  mounted() {
    //動作確認用、意味のないコード
    const foo: string = ''
    console.log(foo)
  },
})
</script>

Options API

ちなみに、

export default Vue.extend({

としている箇所があります。
これはOptions APIの場合のTypeScript化になります。
Composition APIを使う場合などは公式ドキュメントご参照ください。

公式ドキュメントによると、Options API、Composition API、Class APIの例があるのですが、Class APIがVue3系より廃止になるらしい?のと、私の好みでOptions APIを用いた例にしています。

vue-shim.d.ts

IDEを用いているとimport使用箇所で

Cannot find module '~/components/Card' or its corresponding type declarations.

とでていると思います。
その場合は、types/vue-shim.d.tsを作成し

types/vue-shim.d.ts
declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}

importの該当箇所で.vueとすればエラーは消えます。

pages/index.vue
import Card from '~/components/Card.vue'

ただし、私の場合は後者の.vueにするだけで大丈夫でしたので、より正確な対応方法があれば追記したいと思います。どなたか詳しい方は教えてください

参考

https://stackoverflow.com/questions/64213461/vuejs-typescript-cannot-find-module-components-navigation-or-its-correspon

ミドルウェア

middleware/index.ts等、任意のファイルを作成します。

middleware/index.ts
import { Middleware, Context } from '@nuxt/types'

const test: Middleware = ({ isHMR }: Context) => {
  //動作確認用、意味のないコード
  console.log(isHMR)
}

export default test

そして、pages/index.vueに以下を追加します。
もし、全ページに設定したい場合はpages/index.vueではなく、nuxt.config.jsに設定してください。

pages/index.vue
  middleware: 'index',

ブラウザでconsoleにisHMRの値を出力できたら成功です。

プラグイン

plugins/test.ts等、任意のファイルを作成します。

plugins/test.ts
import Vue from 'vue'

Vue.prototype.$test = (message: string) => console.log(message)

作成したプラグインに対する型定義ファイル(types/index.d.ts)を作成します。

types/index.d.ts
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import Vue from 'vue'

declare module 'vue/types/vue' {
  interface Vue {
    $test(message: string): void
  }
}

nuxt.config.jsで読み込む設定をします。

nuxt.config.js
  plugins: ['~/plugins/test.ts'],

実際にコンポーネントでプラグインを使用します。

components/index.vue
mounted() {
  this.$test('This is TypeScript test!!')
},

ブラウザでconsoleに文字を出力できたら成功です。

ストア

ここではOptions APIとの相性を考慮し、nuxt-typed-vuexを用いることにします。

yarn add nuxt-typed-vuex

nuxt.config.jsのbuildModulesにnuxt-typed-vuexを追加します。

nuxt.config.js
  buildModules: [
    'nuxt-typed-vuex',
  ],

store/index.tsを作成します。

store/index.ts
import { getAccessorType } from 'typed-vuex'
import * as submodule from '~/store/submodule'

export const state = () => {
  return {}
}
export const getters = {}
export const mutations = {}
export const actions = {}
export const accessorType = getAccessorType({
  state,
  getters,
  mutations,
  actions,
  modules: {
    submodule,
  },
})

store/submodule.tsを作成します。

store/submodule.ts
import { getterTree, mutationTree, actionTree } from 'typed-vuex'

export const state = () => ({
  text: '' as string,
})

export const getters = getterTree(state, {
  getText: (state) => state.text,
})

export const mutations = mutationTree(state, {
  setText(state, newValue: string) {
    state.text = newValue
  },
})

export const actions = actionTree(
  { state, getters, mutations },
  {
    initialise({ commit }) {
      commit('setText', 'typed-vuex test!!')
    },
  }
)

型定義ファイルであるtypes/index.d.tsに追記し、以下のようにします。

types/index.ts
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import Vue from 'vue'
import { accessorType } from '~/store'

declare module 'vue/types/vue' {
  interface Vue {
    $test(message: string): void
    $accessor: typeof accessorType
  }
}

declare module '@nuxt/types' {
  interface NuxtAppOptions {
    $accessor: typeof accessorType
  }
}

これでコンポーネントからthis.$accessorでアクセスできます。

store/submodule.ts
  //actions
  this.$accessor.submodule.initialise()
  //getters
  this.$accessor.submodule.getText

devtoolsにうまくstoreが表示されない場合

以下の対応で表示されるかもしれません。

1.Vue Devtoolsの歯車アイコンの設定タブを押下
2.New Vuex backendをEnableに変更

参考:fix: devtools does not show auth module #108

さいごに

自身の備忘録を兼ねて作成しました。
参考になれば幸いです。

参考

文中にあげたリンク以外にも、以下のドキュメントを参考にさせていただきました。
https://jp.vuejs.org/
https://typed-vuex.roe.dev/
https://typescript.nuxtjs.org/ja
https://qiita.com/shindex/items/a90217b9e4c03c5b5215
https://blog.unsweets.net/entries/get-types-of-store-using-nuxt-typed-veux/

Discussion