JavaScript UI

ビューのレンダリングをTwigなどのPHPのテンプレートエンジンが行う代わりに、サーバーサイドのJavaScriptが実行します。PHP側は認証・認可・初期状態・APIの提供を行い、JavaScriptがUIをレンダリングします。既存のプロジェクトの構造で、アノテーションが付与されたリソースのみに適用されるため、導入が容易です。

前提条件

注:V8Jsがインストールされていない場合、Node.jsでJavaScriptが実行されます。

用語

  • 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ページからレンダリング方法を選択して、JavaScriptアプリケーションを実行します:

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側が必要とするのは、バンドルされて出力されたJavaScriptファイルのみです。

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フォルダはJavaScriptファイルがあるディレクトリです(ui/ui.config.jsで指定するwebpackの出力先)。

@Ssrアノテーション

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

<?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のビルド先を指定します。buildSsrModuleのインストール時に指定したディレクトリと同じにします:

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のスナップショットをAPCuに保存する機能を使って、パフォーマンスの大幅な向上が可能です。ProdModuleApcSsrModuleをインストールしてください。ReactやアプリケーションのスナップショットがAPCuに保存され再利用されます。V8Jsが必要です:

$this->install(new ApcSsrModule);

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

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

デバッグ

  • ChromeプラグインReact Developer ToolsRedux DevToolsが利用できます。
  • 500エラーが返ってくる場合は、var/logcurlでアクセスしてレスポンスの詳細を確認してみましょう。

リファレンス

その他ビューライブラリ

以前のReact JSページはReactJsをご覧ください。