Cloud Functions for FirebaseでVue.jsのSSRを試してみる

Firebaseがcloudfunctions.netと連携したことにより、Javascriptを使って動的にHTMLを構築して返すことが出来るようになりました。

さらに流行りのVue.jsによるサーバサイドレンダリングが出来れば最高です。試してみます。

※以下はfirebase上にprojectを作成済みであるとして記述しています。

init&deploy

まずは動的にHTMLを返すまでの手順を確認します。

$ firebase init functions
$ tree -L 2
.
├── firebase.json
└── functions
    ├── index.js
    ├── node_modules
    └── package.json

functions/index.jsを開いてコメントアウトされている行を有効にします。

var functions = require('firebase-functions');

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

そしてfirebase deployコマンドでデプロイします。

$ firebase deploy
// 省略
Function URL (helloWorld): https://us-central1-(project).cloudfunctions.net/helloWorld

出力されたURLにWebブラウザでアクセスすると"Hello from Firebase!"と表示されました。

Vue.jsの導入

サーバサイドレンダリングはクライアントサイドレンダリングとの共通化を考えなければシンプルです。

まずは必要なnpmをダウンロードします。

$ cd functions
$ npm install vue vue-server-renderer --save

またfunctions/index.jsを開いて編集します。


var Vue = require('vue');
var renderer = require('vue-server-renderer').createRenderer();
var functions = require('firebase-functions');

exports.helloWorld = functions.https.onRequest((request, response) => {
  renderer.renderToString(
    new Vue({
      template: ''
        + '<!DOCTYPE html>'
        + '<html>'
        + '<head>'
        + '  <title>Vue SSR Sample</title>'
        + '</head>'
        + '<body>'
        + '  <p>Server Side Rendered</p>'
        + '</body>'
        + '</html>'
    }), 
    (error, html) => {
      if (error) { return response.status(500).send('Server Error') }
      response.send( html )
    }   
  )
})

これでfirebase deployすると"Vue SSR Sample"というタイトルのページが返るようになりました。

realtime databaseの読み書き

サーバサイドレンダリングに出来てクライアントサイドレンダリングに出来ないこととして大きなことは、metaタグの出力を動的に変更することです。

realtime databaseにあるデータを出力するようにコードを修正します。

まずはdatabaseにデータを入れます。以下は初期化から行っています。


$ firebase init database
$ echo '{"description": "Vue SSR Sample with Firebase Realtime Database"}' | firebase database:set /helloworld --confirm
$ firebase database:get /
{"helloworld":{"description":"Vue SSR Sample with Firebase Realtime Database"}}

そしてmeta descriptionをdatabaseの内容に書き換えるようコードを修正します。


var Vue = require('vue');
var renderer = require('vue-server-renderer').createRenderer();
var functions = require('firebase-functions');
var admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

var data = {description: ""};
var vm = new Vue({
  template: ''
    + '<!DOCTYPE html>'
    + '<html>'
    + '<head>'
    + '  <title>Vue SSR Sample</title>'
    + '  <meta name="description" v-bind:content="description" />'
    + '</head>'
    + '<body>'
    + '  <p>Server Side Rendered</p>'
    + '</body>'
    + '</html>',
  data: data
});

exports.helloWorld = functions.https.onRequest((request, response) => {
  admin.database().ref("/helloworld/description").once("value")
    .then(snapshot => {
      data.description = snapshot.val();
      renderer.renderToString(
        vm, 
        (error, html) => {
          if (error) { return response.status(500).send('Server Error') }
          response.send( html )
        }   
      )   
    })  
    .catch(error => {
      console.log(error);
    })  
  ;
})

webブラウザでアクセスしてhtmlソースコードを見てみるとmeta descriptionが期待通りに「Vue SSR Sample with Firebase Realtime Database」になっていました。

今後

クライアントサイドレンダリングとサーバサイドレンダリングの共通化を試してみたいですね。

また別の話ですけど生成されるURLがシンプルではないので改善されることを期待してます。