VueServer.js の紹介

この記事は、Vue.js Advent Calender 2015 1日目の記事です。

みなさん、VueServer.js というのをご存じでしょうか？

この記事では、軽く VueServer.js についてゆるい感じで紹介したいと思います。また、最後に Express に組み込んだデモ的なものを紹介したいと思います。

# VueServer.js とは?
[VueServer.js](https://github.com/ngsru/vue-server) は、その名のとおり、Vue.js で作ったコンポーネントを、サーバサイドでも動作させることができるオープンソースなライブラリです。

Vue.js は React.js の Virutal DOM とは違って、直に DOM を利用してレンダリングしているため、サーバサイドで動作させるには、

- [jsdom](https://github.com/tmpvar/jsdom): DOM エミュレーションライブラリ
- [phantomjs](http://phantomjs.org): ヘッドレスブラウザ
- [Prerender.io](https://prerender.io): Pre-Rendering & キャッシングサービス

などを利用する必要があります。VueServer.js の登場によってサーバサイドで Vue.js をレンダリングするための候補となる可能性があります。

# 誰が作ったの?
VueServer.js はロシアの [NGS.RU(N1.RU)](https://github.com/ngsru) が、自分たちの提供するサービスで、サーバサイドでも Vue.js で作られたコンポーネントをレンダリングして SEO に対応するために開発したようです。このため、Vue.js 公式にサポートするライブラリではなく、どちらかというとサードパーティ的な扱いなライブラリです。後で説明しますが、使用に当たっていくつか制限事項があるので注意が必要です。

# hello world
説明だけでは面白くないので、まずは、実際に VueServer.js をインストールして、hello world 的なコードを動かして、どんなもんか見てみましょう :)

## インストール
以降の説明では、事前に Node.js が必要なので事前にインストールしておいてください。(Node.js のバージョンですが、自分の環境では v5.1.0 で確認していますが、多分そんなに古いバージョンでなければ、動くと思います。)

今回 VueServer.js を試してみる適当な作業ディレクトリで `npm init` 等で `pakcage.json` を用意して、そのディレクトリで、下記の `npm` コマンドで VueServer.js をインストールします。

```
$ npm install vue-server --save-dev
```

## コードの準備
次に、上記インストールで問題なければ、適当に js ファイル作成して下記コードを保存します。
下記コードは、Github の VueServer.js リポジトリ README.md の [Getting Started](https://github.com/ngsru/vue-server#getting-started) のコードを一部改変したものです。

```js
var vueServer = require('vue-server') // 1. VueServer.js を require
var Vue = new vueServer.renderer() // 2. Vue レンダラ を取得

// 3. Vue インスタンスを生成
var vm = new Vue({
  template: '<my-component></my-component>',
  components: {
    myComponent: {
      template: '<div>hello world</div>'
    }
  }
})

// 4. `vueServer.htmlReady` イベント経由でレンダリングされた HTML を取得
vm.$on('vueServer.htmlReady', function (html) {
  console.log(html)
})
```

## コードを動かす
保存した上記コードを `node` コマンドでそのファイル名を指定して実行してみましょう。ここでは、`hello-world.js`というファイルで保存したものを指定して実行してみます。

```
$ node hello-world.js
<div>hello world</div>
```

あなたのターミナルに、上記のように `<div>hello world</div>` が出力されたはずです。

## この hello world コードのポイント
上記 hello world コードが、何をやっているのかは、コメントを読んで頂ければ分かると思うので、ここでは詳しくは説明せずに、VueServer.js の特徴的なコードを説明します。

### Vue.js ライクなインターフェース
この記事をご覧の方は、Vue.js を触っている方がほとんどだと思うのでお分かりかと思いますが、上記コードの`3.`でコメントしたコード、

```js
// 3. Vue インスタンスを生成
var vm = new Vue({
  template: '<my-component></my-component>',
  components: {
    myComponent: {
      template: '<div>hello world</div>'
    }
  }
})
```

こちらのコード、サーバサイドでも、クライアントサイドのブラウザで動作する Vue.js と同じインターフェイスを指定していることに気がついたでしょう。

VueServer.js では、既存の Vue.js のコンポーネントをそのまま利用できます。(ただし、きちんと Vue.js のドキュメントに従ってコンポーネントを作っていればですが)。つまり、`module.exports = { ... }` されたコンポーネントを `require` したものをそのまま指定できるということになります。

また、他に、上記コードの`2.`でコメントしたコード、

```js
var Vue = new vueServer.renderer() // 2. Vue レンダラ を取得
```

こちらですが、Vue レンダラを `Vue` という変数に保持しているのですが、VueServer.js の実装を見ると分かりますが、実際には「クライアントサイドのような動作を振る舞おうとする、Vue インスタンスを生成するファクトリ的な関数」を返すようになっています。このため、このような変数名にすることで、Vue.js ユーザーがこれまでどおりサーバサイドにおいても、クライアントサイドを同じ感覚で利用できるよう、変数名になっているのは納得がいくでしょう。

### レンダリング結果はイベント経由
レンダリング結果の受け取り方ですが、下記コードのように

```js
// 4. `vueServer.htmlReady` イベント経由でレンダリングされた HTML を取得
vm.$on('vueServer.htmlReady', function (html) {
  console.log(html)
})
```

Vue.js の イベントAPI と同じ `$on` で `vueServer.htmlReady` というイベントからコールバック経由でレンダリングされた HTML を受け取ります。

え？何か気持ち悪い？うーん、Vue.js を利用しているユーザーにとっては、このようなインタフェースで受け取るのしょうがないと思います。元々、Vue.js は雑にいうと、基本、各コンポーネントごとにドキュメントフラグメント (DocumentFragment) として管理して、コンポーネントの組み合わせによって DOM に挿入したり削除したりしてレンダリングしていっているので。

## hello world のまとめ
以上が、VueServer.js の特徴的なコードです。ほぼ、Vue.js と同じインタフェースな API 操作で、サーバサイドでも Vue.js をレンダリングできることを、コードを動かしながら理解することができたでしょうか？

VueServer.js を使ったサーバサイドの Vue.js のレンダリングは、ちょっとキモい部分もありますが、この hello world 的なコード構造が実装の「型」になり、後は Vue.js と同じ API を利用する感じなので、利用はそんなに難しくないと思います。

# Vue.js との互換性
最初の部分でもちょっと触れましたが、VueServer.js の開発の背景として、Vue.js で構築されたアプリケーションを静的な HTML としてレンダリングするために設計されています。このため、

- ユーザーのインタラクションによるリアクティブなデータ変更
- Vue.js の hook (created 、ready など) が VueServer.js では異なる (createdBe 、readyBe など)、もしくは無い
- Vue.js のメソッド、ディレクティブなどのいくつかの機能を利用できない (主に DOM に依存しているもの)

といった制限がいくつかあります。このため、VueServer.js を利用する場合は、一度確認した方がよいでしょう。

これら制限の詳細については、今後のバージョンアップ次第で変わる可能性があるのでこの記事には記載しませんが、今後利用を考えている方はこちらの [Restrictions](https://github.com/ngsru/vue-server#restrictions) を参照するとよいでしょう。特に、hook でも何らかしらのレンダリングをしている場合は要注意です。

また、Vue.js のバージョンとの互換性もあるので、[バージョンテーブル](https://github.com/ngsru/vue-server#compatibility-table) の確認もお忘れなく。

# VueServer.js の examples
Github の[こちら](https://github.com/ngsru/vue-server/tree/master/examples) にあります。

今回の hello world 的なものの他、リストを扱うもの、mixin を利用したもの、VueServer.js のプリコンパイル (pre-compile) といった example があります。

# VueServer.js のパフォーマンス
VueServer.js のパフォーマンスについて気になる方がいると思うので、参考リンク載せておきます。

- [Rendering is fit for more:Vue.js vs React](https://www.dropbox.com/s/f5e7zctsca0fv6w/Rendering_is_fit_for_more.pdf?dl=0)
- [Presentation by Andrey #337](https://github.com/vuejs/Discussion/issues/337)

# Express への組み込み
今回のアドベントの記事を書くにあたって、VueServer.js を組み込んだ、ちょっとした Express のデモ的なものを作りました。

- [VueServer.js express demo example](https://github.com/kazupon/vue-server-express-demo-example)

![vuejs-advent-1.gif](https://qiita-image-store.s3.amazonaws.com/0/7988/db7616e1-e1b8-6365-f7e9-505031a781c4.gif)

興味がある方は、触ったり、コード読んでみてください。

# まとめ
Vue.js をサーバサイドで動かしてレンダリングすることが可能なライブラリ、VueServer.js について紹介しました。

VueServer.js を利用することで、Vue.js ライクなインタフェースで容易にクライアントサイドで
 Vue.js でレンダリングされる DOM の内容を、文字列の HTML としてレンダリングすることが可能になります。

ただ、VueServer.js は Vue.js の実装を参考にして、Vue.js と同様に振る舞うよう、独自の実装あったり、DOM に依存していないため、いくつか Vue.js の API やディレクティブといった機能に制限があります。

なので、利用する際は、その点注意しながら、サーバサイドで利用してクライアントと同様に振る舞うようレンダリングする必要があります。

# 最後に(言い訳)
VueServer.js をいろいろと試していくうちに、色々とインスピレーションが湧いてきました。jsdom や phantomjs を利用しなくてもサーバサイドでも動作するので、Vue.js でも、VueServer.js の自体が、Vue.js の動作を hack してオーバーホール的な実装で力技に近いですが、isomorphic/universal のようなこともできそうだと核心しました。

本来は、VueServer.js + vue-router + vueify の組み合わせで、isomorphic/universal の記事を書く予定でしたが、いろいろと yakshaving をしないといけないので、断念。時間があるときにでも、ちょっと後でチャレンジしてみようと思います。