LoginSignup
5
3

More than 3 years have passed since last update.

Nuxt.js + TypeScript + CompositionAPI + PWAの構成をAWS Amplifyにデプロイする

Last updated at Posted at 2019-12-17

はじめに

PWAのアプリを、Nuxt.jsとTypeScriptで構築し、
コンポーネントをCompositionAPIを使った構成を、
AWS Amplifyにデプロイしたいと思います。

実行環境のバージョン

node.js : 12.13.1
create-nuxt-app : 2.12.0

実装

プロジェクトの作成

Nuxt.jsのプロジェクトを作成する。

$ npm install -g create-nuxt-app
$ create-nuxt-app nuxt-pwa-for-composition  # nuxt-pwa-for-compositionはプロジェクト名

対話形式で作成するNuxt.jsの構成を入力します。
この記事では、構成は以下のようにしました。

? Project name _ # デフォルトのままEnter 
? Project description _ # デフォルトのままEnter 
? Author name _ # デフォルトのままEnter 
? Choose the package manager Npm
? Choose UI framework Vuetify.js
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios, Progressive Web App (PWA) Support, DotEnv
? Choose linting tools ESLint, Prettier, StyleLint
? Choose test framework Jest
? Choose rendering mode Single Page App
? Choose development tools (Press <space> to select, <a> to toggle all, <i> to i
nvert selection)

最低でも、Choose Nuxt.js modulesProgressive Web App (PWA) Supportを入れてください。

$ cd nuxt-pwa-for-composition
$ npm run dev
ERROR in No rules found within configuration. Have you provided a "rules" property?

このようなエラーが出ました。
stylelint.config.jsrulesが記述されていないとのことなので追加します。

stylelint.config.js
module.exports = {
  rules: {
    'at-rule-no-unknown': [
      true,
      {
        ignoreAtRules: ['extends', 'ignores']
      }
    ],
    indentation: 2,
    'number-leading-zero': null,
    'unit-whitelist': ['px', 'deg', 'em', 'rem', 's']
  }
}

再び起動

$ npm run dev

今回は問題なく、実行できたかと思います。
ポートが使われていないのであればhttp://localhost:3000でページが表示されるはずです。

TypeScriptの反映

このガイドの内容を中心に構築します。
https://typescript.nuxtjs.org/guide/

必要なライブラリをインストールします。

$ npm install --save-dev @nuxt/typescript-build @nuxtjs/eslint-config-typescript
$ npm install @nuxt/typescript-runtime

次のファイルを更新もしくは追加します。

nuxt.config.js
    buildModules: [
      // Doc: https://github.com/nuxt-community/eslint-module
      '@nuxtjs/eslint-module',
      // Doc: https://github.com/nuxt-community/stylelint-module
      '@nuxtjs/stylelint-module',
-     '@nuxtjs/vuetify'
+     '@nuxtjs/vuetify',
+     '@nuxt/typescript-build'
    ],

  ・・・

    build: {
      /*
       ** You can extend webpack config here
       */
-     extend(config, ctx) {}
+     extend() {}
    }
  }
package.json
  "scripts": {
-   "dev": "nuxt",
+   "dev": "nuxt-ts",
-   "build": "nuxt build",
+   "build": "nuxt-ts build",
-   "start": "nuxt start",
+   "start": "nuxt-ts start",
-   "generate": "nuxt generate",
+   "generate": "nuxt-ts generate",
-   "lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
+   "lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore .",
    "test": "jest"
  },
.eslintrc.js
  parserOptions: {
-   parser: 'babel-eslint'
  },

  ・・・

  extends: [
    '@nuxtjs',
+   '@nuxtjs/eslint-config-typescript',
    'prettier',
    'prettier/vue',
    'plugin:prettier/recommended',
    'plugin:nuxt/recommended'
  ],
tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "lib": [
      "esnext",
      "esnext.asynciterable",
      "dom"
    ],
    "esModuleInterop": true,
    "allowJs": true,
    "sourceMap": true,
    "strict": true,
    "noEmit": true,
    "baseUrl": ".",
    "paths": {
      "~/*": [
        "./*"
      ],
      "@/*": [
        "./*"
      ]
    },
    "types": [
      "@types/node",
      "@nuxt/types",
      "vuetify"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}

Composition APIの適用

コンポーネントには、Vue 3で採用される予定のComposition APIを採用しようと思います。
Composition APIの説明はこの記事では行いません。
ドキュメントはこちら
投稿時点では、Class APIの情報の方が多い気がするので、
Class APIを採用しても良いとは思います。

$ npm install --save @vue/composition-api
plugins/composition-api.js
import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'

Vue.use(VueCompositionApi)

generateされた.vueファイルがいくつかあるので、
そのファイルをComposition APIの記述に変えていきます。
※バージョンアップによりtemplateが変更になるのであれば、
こちらの記述では動作しなくなる可能性があります。

components/Logo.vue(scriptタグのみ抜粋)
<script lang="ts">
import { createComponent } from '@vue/composition-api'

export default createComponent({})
</script>
components/VuetifyLogo.vue(scriptタグのみ抜粋)
<script lang="ts">
import { createComponent } from '@vue/composition-api'

export default createComponent({})
</script>
layouts/default.vue(scriptタグのみ抜粋)
<script lang="ts">
import { createComponent, ref } from '@vue/composition-api'

interface Item {
  icon: string
  title: string
  to: string
}

const useMenus = () => {
  const clipped = ref<boolean>(false)
  const drawer = ref<boolean>(false)
  const fixed = ref<boolean>(false)
  const items = ref<Item[]>([
    {
      icon: 'mdi-apps',
      title: 'Welcome',
      to: '/'
    },
    {
      icon: 'mdi-chart-bubble',
      title: 'Inspire',
      to: '/inspire'
    }
  ])
  const miniVariant = ref<boolean>(false)
  const right = ref<boolean>(false)
  const rightDrawer = ref<boolean>(false)

  return {
    clipped,
    drawer,
    fixed,
    items,
    miniVariant,
    right,
    rightDrawer
  }
}

export default createComponent({
  setup() {
    const menus = useMenus()
    const title = ref<string>('Vuetify.js')

    return {
      ...menus,
      title
    }
  }
})
</script>
layouts/error.vue(scriptタグのみ抜粋)
<script lang="ts">
import { createComponent, ref } from '@vue/composition-api'

export default createComponent({
  layout: 'empty',
  props: {
    error: {
      type: Object,
      default: null
    }
  },
  head() {
    const title =
      (this as any).error.statusCode === 404
        ? (this as any).pageNotFound
        : (this as any).otherError
    return {
      title
    }
  },
  setup() {
    const pageNotFound = ref<string>('404 Not Found')
    const otherError = ref<string>('An error occurred')
    return {
      pageNotFound,
      otherError
    }
  }
})
</script>
pages/index.vue(scriptタグのみ抜粋)
<script lang="ts">
import { createComponent } from '@vue/composition-api'

import Logo from '~/components/Logo.vue'
import VuetifyLogo from '~/components/VuetifyLogo.vue'

export default createComponent({
  components: {
    Logo,
    VuetifyLogo
  }
})
</script>

jsファイルをtsファイルにする

nuxt.config.js->nuxt.config.ts
+ import { Configuration } from '@nuxt/types'
  import colors from 'vuetify/es5/util/colors'


- export default {
+ const nuxtConfig: Configuration = {
    mode: 'spa',

    ・・・

    build: {
      /*
       ** You can extend webpack config here
       */
      extend() {}
    }
  }
+ export default nuxtConfig
plugins/composition-api.js->plugins/composition-api.ts

PWAの修正

アプリアイコンとファビコンを変更する。

  • static/favicon.ico
  • static/icon.png

余談ですが、とあるカンファレンスのWebページがPWA対応になっていましたが、
アプリアイコンは変えていないようで、Nuxtのアイコンが表示されていました。

GitHubにコミット

以上の内容をGitHubにコミットします。
リポジトリはprivateリポジトリでも問題ありません。

AWS Amplifyの準備

AWSコンソールからAWS Amplifyを選択します。
スクリーンショット 2019-12-02 0.24.32.png

「Deploy With the Amplify Console」の「GET STARTED」を選択します。
スクリーンショット 2019-12-08 17.13.15.png

GitHub、BitBucket、GitLab、AWS CodeCommitのGitリポジトリを使うことができるようです。
今回はGitHubにpushしましたので、GitHubを選択します。
スクリーンショット 2019-12-08 17.34.30.png

GitHubから承認を求められるので許可します。
スクリーンショット 2019-12-08 17.38.33.png

リポジトリとブランチを選択します。
スクリーンショット 2019-12-08 17.56.59.png

ビルド設定の構成で、アプリ名を変更してください。
また、frontend.artifacts.baseDirectoryの項目が/になっているようなら、/dist/に変更してください。
スクリーンショット 2019-12-09 15.08.31.png

特に問題内容でしたら、「保存してデプロイ」ボタンを押します。
スクリーンショット 2019-12-08 19.08.47.png

デプロイが完了するまでしばらく時間がかかると思います。
スクリーンショット 2019-12-08 19.09.37.png

「検証」までチェックが入ったら完了です。
URLのリンクをクリックし、ちゃんとデプロイできているかを確認します。
スクリーンショット 2019-12-09 15.15.17.png

スクリーンショット 2019-12-09 15.15.17.png

Vuetify + Nuxt.jsのtemplateが表示されれば成功です。
スクリーンショット 2019-12-09 15.16.07.png

Android Chrome等でデプロイされたURLを開きます。
Screenshot_20191209-153620.png

ホームに追加をタップする。

Screenshot_20191209-154050.png

アプリとして表示できれば成功です。

まとめ

今回は、PWAアプリをNuxt.js + TypeScriptで構築する方法と
作成したアプリをAmplify Consoleでデプロイする方法を紹介しました。
SPAをホスティングサービスは多々ありますが、
Amplify ConsoleをCI/CDの管理を気にせずにデプロイすることができるようになるのが大きな利点だと思います。
ただ、今回のサンプルのようにただのホスティングサービスとしてしか使わないと、Amplifyをいかせてはいないとは思います。
また、AWSの資源(カスタムドメイン設定など)を活用しやすいのも利点としてあげれるかと思います。
デプロイ先の一つの候補としてAmplifyを考えてみてはいかがでしょうか。

今回の完成形のリポジトリはこちら

5
3
0

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
5
3