137
108

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SSR モードの Nuxt.js を Firebase に Deploy する Tutorial

Last updated at Posted at 2018-12-25

書いてある事

大きく3点に分けて説明します。

  • Firebase で SSR する為に必要な最低限の知識
  • Nuxt.js を Express のミドルウェアとして動作させる方法
  • Nuxt.js を Firebase にデプロイする時の注意点

なお、SSR ではなく SPA で十分な人だったり Firebase Functions 使わない人には合わない記事かもしれません。

Firebase で SSR する為に必要な最低限の知識

まずは Firebase functions のプロジェクトを用意します。

% mkdir ~/nuxt-ssr-firebase && cd $_
% firebase init functions

以下の設問が出てくるので各自でFirebase のプロジェクトを選択、もしくは作成してください

? Select a default Firebase project for this directory: 

プロジェクトを選ぶと以下の質問に回答します。とりあえず何の事を聞かれているのかまだ分からない状態の人は以下を真似してして下さい。

# A functions directory will be created in your project with a Node.js
# package pre-configured. Functions can be deployed with firebase deploy.

? What language would you like to use to write Cloud Functions? JavaScript
? Do you want to use ESLint to catch probable bugs and enforce style? (y/N) No
? Do you want to install dependencies with npm now? (Y/n) Yes

現時点ではディレクトリ構成は以下のようになっていると思います。package.jsonnode_moduels~/nuxt-ssr-firebase/functions に配置されています。

% tree -a -L 2 -I 'node_modules' -I '.git'                                            .
├── .firebaserc
├── .gitignore
├── firebase.json
└── functions
    ├── index.js
    ├── node_modules
    ├── package-lock.json
    └── package.json

functions/index.js を修正します。

-// exports.helloWorld = functions.https.onRequest((request, response) => {
-//  response.send("Hello from Firebase!");
-// });
+exports.helloWorld = functions.https.onRequest((request, response) => {
+ response.send("Hello from Firebase!");
+});

firebase serve を使って localhost で firebase functions の動作を確認します。

% firebase serve --only functions:helloWorld
functions: helloWorld: http://localhost:5000/nuxt-ssr-xxxx/us-central1/helloWorld

確認できたら firebase deploy で cloud functions にデプロイして動作確認します。

% firebase deploy --only functions:helloWorld

おそらく次のようなURL でアクセスできると思います。https://us-central1-nuxt-ssr-xxxx.cloudfunctions.net/helloWorld

さて、asyncawait といった構文を使いたいと思いますので functions/package.json に以下の修正を加えておくとよいでしょう。

 {
   "name": "functions",
   "description": "Cloud Functions for Firebase",
+  "engines": {
+    "node": "8"
+  },
   "scripts": {
     "serve": "firebase serve --only functions",
     "shell": "firebase functions:shell",

次に Firebase hosting をセットアップします。

% firebase init hosting
? What do you want to use as your public directory? public
? Configure as a single-page app (rewrite all urls to /index.html)? No

最初の設問で public と答えてある場合は functions/public というディレクトリが作成されていると思います。ちなみに public404.html と index.html は後で消します。

ls public
404.html index.html

次に firebase.json に以下の様な rewrites ルールを追加して下さい。

       "firebase.json",
       "**/.*",
       "**/node_modules/**"
+    ],
+    "rewrites": [
+      {   
+        "source": "**",
+        "function": "helloWorld"
+      }   
     ]
   }
 }

以下のコマンドを実行して動作確認します。

% firebase serve --only functions:helloWorld,hosting
hosting: Local server: http://localhost:5000
functions: helloWorld: http://localhost:5001/nuxt-ssr-xxxxx/us-central1/helloWorld
```

この状態で以下の URL にアクセスして下さい。**public に一致する静的アセットが存在していない場合は Firebase functions に rewrite される**事を確認して下さい。

```
http://localhost:5000/
http://localhost:5000/404.html
http://localhost:5000/hoge
```

ホスティングの優先順位については次の URL で確認して下さい<https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-priorities>

## Nuxt.js を Express のミドルウェアとして動作させる

Nuxt.js アプリケーションをスクラッチから作ります。

```
cd ~/nuxt-ssr-firebase/functions
yarn add nuxt
mkdir -p nuxt-app/pages
touch nuxt-app/pages/index.vue nuxt.config.js
```

create `nuxt.config.js`

```javascript
module.exports = { 
  srcDir: 'nuxt-app',
  buildDir: 'nuxt-dist',
  build: {
    publicPath: '/assets/',
  }
}
```

create `nuxt-app/pages/index.vue`

```vue
<template>
  <h1>Hello world!</h1>
</template>
```

この状態で `yarn run nuxt` を実行して動作確認をして下さい。

## Express のミドルウェアとして動作させる

次に `functions/nuxt-server.js` を作成します。

```javascript
const { Nuxt, Builder } = require('nuxt')
const app = require('express')()

const port = process.env.PORT || 3000

const config = require('./nuxt.config.js')
config.dev = process.env.NODE_ENV === 'development'
const nuxt = new Nuxt(config)

app.get('/api/ping', (req, res) => {
  res.json({ ping: 'pong' }); 
});

app.use(async (req, res) => {
  await nuxt.ready()
  nuxt.render(req, res)
})

// Build only in dev mode with hot-reloading
if (config.dev) {
  new Builder(nuxt).build()
    .then(listen)
    .catch((error) => {
      console.error(error)
      process.exit(1)
    })
}

function listen () {
  console.log('=== listen ===')
  // Listen the server
  app.listen(port, '0.0.0.0')
  console.log('Server listening on `localhost:' + port + '`.')
}

module.exports = app
```

`NODE_ENV=development node nuxt-server.js` を実行した場合は nuxt.config.js で指定した `srcDir` を Hot Reloading します。`node nuxt-server.js` を実行する場合は最初に `yarn nuxt build` を実行してから nuxt.config.js で指定した `buildDir` にファイルを生成して置く必要があります。

次に `functions/index.js` を編集します。

```javascript
 const functions = require('firebase-functions');
+const nuxtServer = require('./nuxt-server');
 
+
+exports.nuxtServer = functions.https.onRequest(nuxtServer)
```

`yarn nuxt build` を実行した後で `firebase serve --only functions:nuxtServer` を実行して動作確認をします。

## Nuxt.js を Firebase にデプロイする時の注意点

ところで以下のファイルは Firebase Hosting から配信すべきファイルですが、今のままだとFirebase Functions が動的に配信を行っています。

```
% tree nuxt-dist/dist/client/
nuxt-dist/dist/client/
├── 3ea0a886292a84605c64.js
├── 70744f471384cdfbb566.js
├── 7d3682722017aec6af25.js
├── LICENSES
└── afc01472d20742d8c1ac.js
```

なのでこちらのファイルは `public/assets` に移動します。以下のコマンドを実行してください。

```
rm -fr ../public/*
cp -R nuxt-dist/dist/client/ ../public/assets
```

次に `firebase.json` を以下の様に修正します。

```diff
     "rewrites": [
       {   
         "source": "**",
-        "function": "helloWorld"
+        "function": "nuxtServer"
       }   
     ]
```

この状態で `firebase serve --only functions:nuxtServer,hosting` を実行して動作確認をして下さい。ちなみに public/assets のファイルを Hosting できている場合は console に以下の様なメッセージが表示されます。

```
127.0.0.1 - - [25/Dec/2018:02:50:39 +0000] "GET /assets/3ea0a886292a84605c64.js HTTP/1.1" 200 143472 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
```

もし functions が動的に返している場合は以下の様に表示されます。

```
[hosting] Rewriting /assets/3ea0a886292a84605c64.js to local function nuxtServer
info: User function triggered, starting execution
info: Execution took 17 ms, user function completed successfully
127.0.0.1 - - [25/Dec/2018:02:51:24 +0000] "GET /assets/3ea0a886292a84605c64.js HTTP/1.1" 200 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
```

こちらのコマンドは `functions/package.json` にまとめて記述した方が良いでしょう。

```diff
-    "logs": "firebase functions:log"
+    "logs": "firebase functions:log",
+    "prebuild": "rm -fr ../public/*",
+    "build": "nuxt build",
+    "postbuild": "cp -R nuxt-dist/dist/client/ ../public/assets"
```

Windows User の方などは cpx, rimraf などを適宜活用頂ければと思います。

最後に `firebase deploy --only functions:nuxtServer,hosting` を実行してデプロイが出来ていることを確認して下さい。

## まとめ

というわけで上記の構成でプロジェクトを作成した場合は以下の様に作業する事ができます。

* 開発時: `NODE_ENV=development node nuxt-server.js`
* デプロイ前の動作確認: `firebase serve --only functions:nuxtServer,hosting`
* デプロイ: `firebase deploy --only functions:nuxtServer,hosting`

実際に業務で開発を行う場合はデプロイ先を `staging`, `production` と分けた方が良いと思いますし、Firebase と Nuxt.js のディレクトリ構成などはまだ検討の余地があると思いますが、その辺りについては後世のエンジニアがきっと素晴らしい記事を書いてくれると思います。


137
108
2

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
137
108

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?