BEAR.Sunday
SPA
vue.js

BEAR.Sunday + Vue.js で始めるシングルページアプリケーション

BEAR.Sunday のマニュアルでは React をフロントエンドフレームワークとしてアプリケーションを構築する例が掲載されています。

ここでは Vue.js の webpack-simple template のバックエンドを BEAR.Sunday で構築したシングルページアプリケーションを作ってみたいと思います。

  • 動作環境

    • PHP 7.1
    • BEAR.Package v1.7.0
    • Vue.js v2.5.11

ソースコードは GitHub で公開しています。

https://github.com/kawanamiyuu/Kawanamiyuu.VuejsSpa

本エントリで紹介しきれていない詳細は完成版のソースコードと各コミットを参照してください :sweat_smile:

webpack-simple template から Vue アプリの雛形を作成する

まずは、webpack-simple template から Vue アプリの雛形を作成します。

$ vue init webpack-simple advent-vue-project 

? Project name advent-vue-project
? Project description A Vue.js project
? Author 
? License MIT
? Use sass? No

   vue-cli · Generated "advent-vue-project".

   To get started:

     cd advent-vue-project
     npm install
     npm run dev

$ tree -a -L 1            
.
├── .babelrc
├── .editorconfig
├── .gitignore
├── README.md
├── index.html
├── package.json
├── src
└── webpack.config.js

$ npm install 
$ npm run dev

> advent-vue-project@1.0.0 dev /Users/Yuu/workspace/advent-calendar/advent-vue-project
> cross-env NODE_ENV=development webpack-dev-server --open --hot

Project is running at http://localhost:8080/
webpack output is served from /dist/
404s will fallback to /index.html

npm run dev を実行するとブラウザで自動的にアプリケーションが起動します。

スクリーンショット 2017-12-17 22.50.22.png

BEAR.Skeleton から BEAR アプリの雛形を作成する

次に BEAR.Skeleton からBEAR アプリの雛形を作成します。

また、アプリケーションのエントリ画面をHTMLとしてレンダリングするため、madapaja/twig-module をインストールします。

$ composer create-project -n bear/skeleton MyAdvent.MyProject
$ tree -a -L 1
.
├── .env
├── .gitignore
├── .php_cs.dist
├── README.md
├── autoload.php
├── bootstrap
├── composer.json
├── composer.lock
├── phpcs.xml
├── phpmd.xml
├── phpunit.xml.dist
├── public
├── src
├── tests
├── var
└── vendor

$ composer require madapaja/twig-module

 TwigModule をアプリケーションにインストールし、Twigテンプレートファイルを作成する
 以下のコミットを参照
 https://github.com/kawanamiyuu/Kawanamiyuu.VuejsSpa/commit/5ba0dc0518b9063bd47988a4b29470750cc41312

$ php -S 127.0.0.1:8080 -t public

スクリーンショット 2017-12-17 22.43.17.png

Vue アプリのクライアントコードを BEAR アプリに移植する

Vue アプリケーションの src ディレクトリを src-ui として BEAR アプリケーションに移植します。

$ tree -a -L 1
.
├── .babelrc <--
├── .env
├── .gitignore
├── .php_cs.dist
├── README.md
├── autoload.php
├── bootstrap
├── composer.json
├── composer.lock
├── node_modules <--
├── package.json <--
├── phpcs.xml
├── phpmd.xml
├── phpunit.xml.dist
├── public
├── src
├── src-ui <--
├── tests
├── var
├── vendor
└── webpack.config.js <--

webpack.config.js を編集して Vue アプリケーションのビルド先を BEAR アプリケーションのエントリポイントである public 配下に変更します。

 module.exports = {
-  entry: './src/main.js',
+  entry: './src-ui/main.js',
   output: {
-    path: path.resolve(__dirname, './dist'),
+    path: path.resolve(__dirname, './public/dist'),
     publicPath: '/dist/',
     filename: 'build.js'
   },

ページコンテンツを Ajax で表示する

Vue アプリのコンテンツ(「Essential Links」と「Ecosystem」のリンクURLとラベル)を Ajax でバックエンドから取得し表示します。

クライアントサイドのルーティングには vue-router を使用し、バックエンドとのAjax通信には axios を使用します。

BEAR アプリの特定の Page リソースで Json レンダリングを行うために AOP でレンダラーを JsonRenderer に差し替えます。

// src/Module/JsonRendererInterceptor.php (新規)

use BEAR\Resource\JsonRenderer;
use BEAR\Resource\ResourceObject;
use Ray\Aop\MethodInterceptor;
use Ray\Aop\MethodInvocation;

class JsonRendererInterceptor implements MethodInterceptor
{
    public function invoke(MethodInvocation $invocation)
    {
        $ro = $invocation->getThis();
        /* @var $ro ResourceObject */
        $ro->setRenderer(new JsonRenderer);

        return $invocation->proceed();
    }
}
// src/Module/AppModule.php (編集)

use Kawanamiyuu\VuejsSpa\Annotation\Api;
use Ray\Di\AbstractModule;

class AppModule extends AbstractModule
{
    protected function configure()
    {
        // ...

        $this->bindInterceptor(
            $this->matcher->annotatedWith(Api::class),
            $this->matcher->startsWith('on'),
            [JsonRendererInterceptor::class]
        );

        // ...
    }
}
// src/Resource/Page/Links.php (新規)

use BEAR\Resource\ResourceObject;
use Kawanamiyuu\VuejsSpa\Annotation\Api;

/**
 * @Api
 */
class Links extends ResourceObject
{
    public function onGet()
    {
        $this['essential_links'] = [
            [
                'label' => 'Core Docs',
                'url' => 'https://vuejs.org'
            ],
            // ...
        ];

        $this['ecosystem_links'] = [
            [
                'label' =>'vue-router',
                'url' => 'http://router.vuejs.org/',
            ],
            // ...
        ];

        return $this;
    }
}

これで初期表示後、クライアントサイドでルーティングし(該当コミット)、かつ非同期でコンテンツを表示する(該当コミット)シングルページアプリケーションが完成しました。

スクリーンショット 2017-12-17 23.27.44.png

↓↓↓(Welcome to Your Vue.js App をクリック)

スクリーンショット 2017-12-17 23.27.59.png