Javascript UI

ビューのレンダリングをTwigなどのPHPのテンプレートエンジン等が行う代わりに、サーバーサイドのJavaScriptが行います。 PHP側は認証/認可/初期状態/APIの提供を行い、JSがUIをレンダリングします。

既存のプロジェクトの構造で、アノテートされたリソースのみに適用されるので導入が容易です。

前提条件

Note: V8JsがインストールされていないとNode.jsでJSが実行されます。

用語

  • CSR クライアントサイドレンダリング (Webブラウザで描画)
  • SSR サーバーサイドレンダリング (サーバーサイドのV8またはNode.jsが描画)

JavaScript

インストール

プロジェクトにkoriym/ssr-moduleをインストールします。

// composer create-project bear/skeleton MyVendor.MyProject; cd MyVendor.MyProject // 新規の場合
composer require bear/ssr-module

UIスケルトンアプリkoriym/js-ui-skeletonをインストールします。

composer require koriym/js-ui-skeleton 1.x-dev
cp -r vendor/koriym/js-ui-skeleton/ui .
cp -r vendor/koriym/js-ui-skeleton/package.json .
yarn install

UIアプリケーションの実行

まずはデモアプリケーションを動かして見ましょう。 現れたWebページからレンダリング方法を選択してJSアプリケーションを実行します。

yarn run ui

このアプリケーションの入力はui/dev/config/の設定ファイルで設定します。

<?php
$app = 'index';                   // =index.bundle.js
$state = [                        // アプリケーションステート
    'hello' =>['name' => 'World']
];
$metas = [                        // SSRでのみ必要な値
    'title' =>'page-title'
];

return [$app, $state, $metas];

設定ファイルをコピーして、入力値を変えてみましょう。

cp ui/dev/config/index.php ui/dev/config/myapp.php

ブラウザをリロードして新しい設定を試します。 このようにJavascriptや本体のPHPアプリケーションを変更しないでUIのデータを変更して動作を確認することができます。

このセクションで編集したPHPの設定ファイルはあくまでyarn run uiで実行する時のみに使用されます。 PHP側が必要とするのはバンドルされて出力されたJSのみです。

UIアプリケーションの作成

PHPから渡された引数を使ってレンダリングした文字列を返すrender関数を作成します。

const render = (state, metas) => (
  __AWESOME_UI__ // SSR対応のライブラリやJSのテンプレートエンジンを使って文字列を返す
)

stateはドキュメントルートに必要な値、metasはそれ以外の値、例えば<head>で使う値などです。renderという関数名は固定です。

ここでは名前を受け取って挨拶を返す関数を作成します。

const render = state => (
  `Hello ${state.name}`
)

ui/src/page/index/hello/server.jsとして保存して、webpackのエントリーポイントをui/entry.jsに登録します。

module.exports = {
  hello: 'src/page/hello/server',
};

これでhello.bundle.jsというバンドルされたファイルが出力されるようになりました。

このhelloアプリケーションをテスト実行するためのファイルをui/dev/config/myapp.phpに作成します。

<?php
$app = 'hello';
$state = [
    ['name' => 'World']
];
$metas = [];

return [$app, $state, $metas];

以上です!ヴラウザをリロードして試してください。

render関数の中の処理をReactやVue.jsなどのUIフレームワークを使ってリッチなUIを作成できます。 通常のアプリケーションでは依存を最小限にするためにserver.jsエントリーファイルは以下のようにrenderモジュールを読み込むようにします。

import render from './render';
global.render = render;

ここまでPHP側の作業はありません。SSRのアプリケーション開発はPHP開発と独立して行うことができます。

PHP

モジュールインストール

AppModuleにSsrModuleモジュールをインストールします。

<?php
use BEAR\SsrModule\SsrModule;

class AppModule extends AbstractAppModule
{
    protected function configure()
    {
        // ...
        $build = dirname(__DIR__, 2) . '/var/www/build';
        $this->install(new SsrModule($build));
    }
}

$buildフォルダはJSのファイルがあるディレクトリです。(ui/ui.config.jsで指定するwebpackの出力先)

@Ssrアノテーション

リソースをSSRするメソッドに@Ssrとアノテートします。appにJSアプリケーション名が必要です。

<?php

namespace MyVendor\MyRedux\Resource\Page;

use BEAR\Resource\ResourceObject;
use BEAR\SsrModule\Annotation\Ssr;

class Index extends ResourceObject
{
    /**
     * @Ssr(app="index_ssr")
     */
    public function onGet($name = 'BEAR.Sunday')
    {
        $this->body = [
            'hello' => ['name' => $name]
        ];

        return $this;
    }
}

$this->bodyrender関数に1つ目の引数として渡されます。 CSRとSSRの値を区別して渡したい場合はstatemetasでbodyのキーを指定します。

/**
 * @Ssr(
 *   app="index_ssr",
 *   state={"name", "age"},
 *   metas={"title"}
 * )
 */
public function onGet()
{
    $this->body = [
        'name' => 'World',
        'age' => 4.6E8;
        'title' => 'Age of the World'
    ];

    return $this;
}

実際statemetasをどのようにして渡してSSRを実現するかはui/src/page/index/serverのサンプルアプリケーションをご覧ください。影響を受けるのはアノテートしたメソッドだけで、APIやHTMLのレンダリングの設定はそのままです。

PHPアプリケーションの実行設定

ui/ui.config.jsを編集して、publicにweb公開ディレクトリをbuildにwebpackのbuild先を指定します。 buildはSsrModuleのインストールで指定したディレクトリと同じです。

const path = require('path');

module.exports = {
  public: path.join(__dirname, '../var/www'),
  build: path.join(__dirname, '../var/www/build')
};

PHPアプリケーションの実行

yarn run dev

ライブアップデートで実行します。 PHPファイルの変更があれば自動でリロードされ、Reactのコンポーネントに変更があればリロードなしでコンポーネントをアップデートします。ライブアップデートなしで実行する場合にはyarn run startを実行します。

linttestなどの他のコマンドはコマンドをご覧ください。

パフォーマンス

V8のスナップショットをApc保存する機能を使ってパフォーマンスの大幅な向上が可能です。 ProdModuleApcSsrModuleをインストールしてください。 ReactJsやアプリケーションのスナップショットがAPCuに保存され再利用されます。V8jsが必要です。

$this->install(new ApcSsrModule);

Apc以外のキャッシュを利用するにはApcSsrModuleのコードを参考にモジュールを作成してください。 PSR16対応のキャッシュが利用可能です。

さらなる高速化のためにはV8をコンパイルする時点でJSコード(ReactJsなど)のスナップショットを取り込みます。 詳しくは以下をご覧ください。

デバック

  • Chromeプラグイン React developer toolsRedux devToolsが利用できます。
  • 500エラーが帰ってくる場合はvar/logcurl でアクセスしてレスポンス詳細を見てみましょう

リファレンス

その他ビューライブラリ