Nuxt.js #2 Advent Calendar 2018の4日目の投稿となります。

Nuxt.jsにTypeScriptを適用したプロジェクトとして下記をよく利用させてもらっています。そちらがまだPWA化していなかったので、対応してみました。

jeehyukwon/nuxt-serverless: Nuxt.js Serverless SSR Starter on AWS (Lambda + API Gateway + S3) with Serverless Framework
https://github.com/jeehyukwon/nuxt-serverless

Progressive Web App(PWA)とは

Googleが力を入れるプログレッシブウェブアプリ(PWA)とはなにか? | SEO研究所サクラサクラボ
https://www.sakurasaku-labo.jp/blogs/pwa

PWAとはなにか?という問いに、非常に簡単に答えるとすると「アプリのようなウェブサイト」になります。

前提

  • AWSアカウントがある
  • serverlessがインストール・設定済み
  • node.js、npm(、yarn)がインストール済み

手順

環境構築

今回はPWAに対応して動作確認することを目的としますので、上記リポジトリをベースにS3を利用しないようにしたリポジトリを用います。

kai-kou/nuxt-serverless at feature/no-use-s3
https://github.com/kai-kou/nuxt-serverless/tree/feature/no-use-s3

ベースにするリポジトリに関しては下記に利用方法をまとめています。

Nuxt.js(v2.2.0)+TypeScriptなアプリをAWS Lambda+αにデプロイしてみた(S3なし版) – Qiita
https://cloudpack.media/44910

今回のソースはGitHubにもアップしていますので、ご参考ください。
https://github.com/kai-kou/nuxt-serverless/tree/feature/add-pwa

> mkdir 任意のディレクトリ
> cd 任意のディレクトリ
> git clone https://github.com/kai-kou/nuxt-serverless.git
> cd nuxt-serverless
> git checkout feature/no-use-s3
> npm install

PWAライブラリの導入

Nuxt.jsの場合、@nuxtjs/pwa というモジュールが提供されていますので、それを利用します。

nuxt-community/pwa-module: ⚡ Supercharge Nuxt with a heavily tested, updated and stable PWA solution
https://github.com/nuxt-community/pwa-module

> npm install --save @nuxtjs/pwa

利用方法については下記を参考にさせていただきました。

Nuxt.js で作った静的サイトを PWA 化する – Qiita
https://qiita.com/QUANON/items/880eaa43c1d2f55f5b4d

PWAをNuxt.jsで簡単に体験する – SCOUTER開発者ブログ
https://techblog.scouter.co.jp/entry/2017/12/07/080416

両方の記事ともにTypeScriptを利用していないため、プロジェクトに合わせて設定する必要がありました。

handler.jsapplication/manifest+json を追加します。

handler.js

const awsServerlessExpress = require('aws-serverless-express')
const app = require('./server')

const binaryMimeTypes = [
  'application/javascript',
  'application/json',
  'application/manifest+json',
  'application/octet-stream',
  'application/xml',
  'font/eot',
  'font/opentype',
  'font/otf',
  'image/jpeg',
  'image/png',
  'image/svg+xml',
  'text/comma-separated-values',
  'text/css',
  'text/html',
  'text/javascript',
  'text/plain',
  'text/text',
  'text/xml'
]
const server = awsServerlessExpress.createServer(app, null, binaryMimeTypes)

module.exports.render = (event, context) => awsServerlessExpress.proxy(server, event, context)

nuxt.config.jsmodules@nuxtjs/pwa を追加、pwaのオプションmanifestworkbox を追加します

nuxt.config.js

 modules: [
    '@nuxtjs/pwa',
  ],
  manifest: {
    name: 'Nuxt.js+TypeScriptアプリをPWA化する',
    lang: 'ja',
    start_url: '/dev/',
    icons: [
      {
        src: "static/icon.png",
        sizes: "512x512",
        type: "image/png",
      }
    ]
  },
  workbox: {
    swDest: 'static/sw.js',
  },
}

server.js/sw.js のルーティング設定を追加します。
PWAで利用するのにstatic/icon.png を指定していますので、適当に用意してください。

server.js

const express = require('express')
const {Nuxt} = require('nuxt')
const path = require('path')
const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware')

const app = express()

app.use(awsServerlessExpressMiddleware.eventContext())

app.use('/_nuxt', express.static(path.join(__dirname, '.nuxt', 'dist', 'client')))

app.use('/static', express.static(path.join(__dirname, 'static')))

app.use('/sw.js', express.static(path.join(__dirname, 'static', 'sw.js')))

let config = require('./nuxt.config.js')
const nuxt = new Nuxt(config)

app.use((req, res) => {
  req.url = `${config.router.base}${req.url}`.replace('//', '/')
  nuxt.render(req, res)
})

module.exports = app

デプロイして動作確認

Expressでパス指定しており、ローカルでの検証ができませんので、デプロイして確認してみます。

デプロイ前にserverless.yml の設定serviceregion など、適宜変更してください。

> npm run build
> serverless deploy

ブラウザで確認するとsw.jsが読み込まれていることが確認できました。

スマホ(Android)では、Google Chromeの「ホーム画面に追加」機能で、アプリっぽく利用できることが確認できました。

やったぜ。

ポイント

PWAのmanifest設定を変更する

TypeScriptを利用しているので、nuxt.config.js で諸々パスを変更する必要がありました。

manifest.start_url は初期設定だと.となり、かつpublicPath_nuxt となるため、/dev とします。カスタムドメインを利用する場合には、/ となります。

nuxt.config.js_一部抜粋

manifest: {
    start_url: '/dev',
  },

アイコンについてもstatic/icon.png が参照されるように指定します。

nuxt.config.js_一部抜粋

 manifest: {
    icons: [
      {
        src: "static/icon.png",
        sizes: "512x512",
        type: "image/png",
      }
    ]
  },

sw.js の出力先が初期設定だと、src/static/sw.js となるため、static/sw.js としています。

nuxt.config.js_一部抜粋

 workbox: {
    swDest: 'static/sw.js',
  },

また、sw.jsのパスがstatic 以下のままですと、PWAのスコープ(対象範囲)がstatic 以下となってしまい、使い勝手が悪くなるため、/sw.js でアクセス可能になるようにします。

server.js

app.use('/sw.js', express.static(path.join(__dirname, 'static', 'sw.js')))

スコープやmanifestworkbox の設定については下記が参考になりました。

Service Worker、はじめの一歩 – Service Workerとは | CodeGrid
https://app.codegrid.net/entry/2016-service-worker-1

Workbox Build | Workbox | Google Developers
https://developers.google.com/web/tools/workbox/modules/workbox-build

Workbox Module – PWA Module
https://pwa.nuxtjs.org/modules/workbox

Webアプリケーションマニフェスト| MDN
https://developer.mozilla.org/en-US/docs/Web/Manifest

タイムアウトエラーが発生したらyarn を利用する

npm でライブラリをインストールしていると、デプロイできても、URLへアクセスするとタイムアウトエラーになることがありました。その場合には、yarn でライブラリをインストールし直すとうまく動きました。
その際にpackage-lock.json が含まれたままだと、Serverlessでデプロイ時にnode_modules 以下すべてが含まれてパッケージングされてしまうので、注意しましょう。

package-lock.jsonがある状態でyarn installするとServerlessのデプロイで痛い目にあう
TODO: あとでURLを貼る

> rm -rf node_modules/
> rm package-lock.json
> npm install -g yarn # yarnがインストールされていなかったら
> yarn
> yarn build
> serverless deploy

参考

Googleが力を入れるプログレッシブウェブアプリ(PWA)とはなにか? | SEO研究所サクラサクラボ
https://www.sakurasaku-labo.jp/blogs/pwa

Service Worker、はじめの一歩 – Service Workerとは | CodeGrid
https://app.codegrid.net/entry/2016-service-worker-1

Workbox Build | Workbox | Google Developers
https://developers.google.com/web/tools/workbox/modules/workbox-build

Workbox Module – PWA Module
https://pwa.nuxtjs.org/modules/workbox

Nuxt.js で作った静的サイトを PWA 化する – Qiita
https://qiita.com/QUANON/items/880eaa43c1d2f55f5b4d

PWAをNuxt.jsで簡単に体験する – SCOUTER開発者ブログ
https://techblog.scouter.co.jp/entry/2017/12/07/080416

Webアプリケーションマニフェスト| MDN
https://developer.mozilla.org/en-US/docs/Web/Manifest

package-lock.jsonがある状態でyarn installするとServerlessのデプロイで痛い目にあう
TODO: あとでURLを貼る

元記事はこちら

Nuxt.js+TypeScriptなアプリをProgressive Web App(PWA)化する