5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Vue.jsのSFC(単一ファイルコンポーネント)+PHPでWebアプリケーションを構築する - バックエンド環境編

Last updated at Posted at 2020-04-05

前回の記事はこちら

本記事は、『フロントエンド環境編』の続きです。

バックエンド側の環境構築

タイトルにある通り、バックエンドはPHP言語を利用します。
また、著者はWindows10で環境構築をしています。

ソースのルートディレクトリについて

前回に引き続き、C:\hello-vuejsを前提に話を進めます。

インストールライブラリのまとめ

バックエンド側で必要なものは以下の通りです。

ソフトウェア バージョン 説明
Apache 2.4.* Webサーバー
PHP 7.4.* バックエンド言語
Slim Framework 3.1.* PHPフレームワーク
Slim PHP-View ^2.2 PHPフレームワーク
Monolog ^2.0 PHPロガー

Xamppのインストール

ApacheとPHPを使いたいので、XAMPPをインストールしました。本記事は、XAMPPベースで説明を進めます。
また、XAMPPの7.4系(しっかり確認していませんが、PHPの7.x系なら全般的に動作可能な気がします)をチョイスしました。

XAMPPのページ

HINT
XAMPPのインストールを進めると、コンポーネント選択画面が表示されます。
本記事で必要なものは、以下の通りです(最小限の指定)。

XAMPPインストールウィザード - Select Components
xampp_select_components.png

注意
著者の環境(OSはWindows10)では、インストール完了後に、XAMPPコントロールパネルを起動する際、"Error:Cannot create file "[XAMPPインストールパス]\xampp-control.ini". Access denied.と表示されました。
エラーを解消するには、XAMPPインストールパスのxampp-control.exeを管理者権限で起動(右クリックして管理者権限で起動)します。以後は通常の操作で起動できるようになりました。

Apacheの設定

ポート番号の変更

Apacheを起動すると、httpは80番、httpsは443番として、デフォルトのポート番号が割り当てられています。
デフォルトのままだと、他のアプリとポート番号が衝突する可能性があるので変更することにします。
本記事では、httpを5555番、httpsを5556番に変更しました。

[XAMPPインストールパス]\apache\conf\httpd.conf
# Listenの設定
Listen 80

Listen 5555

# ServerNameの設定
ServerName localhost:80

ServerName localhost:5555
[XAMPPインストールパス]\apache\conf\extra\httpd-ssl.conf
# Listenの設定
Listen 443

Listen 5556

# VirtualHostの設定
<VirtualHost _default_:443>

<VirtualHost _default_:5556>

# ServerNameの設定
ServerName www.example.com:443

ServerName www.example.com:5556

公開ディレクトリの設定

hello-vuejspublicディレクトリをApacheの公開ディレクトリとして指定します。
公開ディレクトリの指定には幾つかありますが、ここでは、alias_moduleを利用します。

[XAMPPインストールパス]\apache\conf\httpd.conf
...
<IfModule alias_module>
    ...

    Alias /hello-vuejs "C:\hello-vuejs\public"
    <Directory "C:\hello-vuejs\public">
        AllowOverride All
        Require all granted
    </Directory>

</IfModule>
...

HINT
alias_moduleを使用すると、URLが ”http://[ドメイン]/hello-vuejs” のように1段階層が深くなります。
virtual_hostを使用するというのも良い選択肢かと思います。

PHPの設定

PHPの挙動を設定するファイルは、[XAMPPインストールパス]\php\php.iniです。
今回は、デフォルト設定のままで問題ないので特に編集しません。

Apacheの起動

XAMPPのコントローラパネルからApacheを起動しましょう。下図のような画面になればOKです。

hello-vuejs2_xampp_control_panel.png

Composerで必要なパッケージを導入する

PHPの各種ライブラリを入手するために、Composerをインストールするところから始めます。
以下のページから最新版をダウンロードしてインストールしましょう。

Composerのページ

HINT
ComposerはPHPのパッケージ管理システムです。こちらを使って、PHPの各種ライブラリを導入します。

Composerの初期化

コマンドプロンプトを開いて、C:\hello-vuejscdしましょう。
そして、最初に以下を実行してください。
色々聞かれますが、大体の質問にEnterを押して、最後にyesを入力して行けばとりあえずOKです。

詳細が知りたい人は、ググってみてください。

コマンドプロンプト
composer init

...

# パッケージ名は、<vendor>/<name>形式の必要があるので、"ison/hello-vuejs"と入力
Package name (<vendor>/<name>) [***/***]: ison/hello-vuejs

... その他の質問は適当にEnterとyesを入力する

Composerのパッケージインストール

次に、以下を実行して行きます。

コマンドプロンプト
composer require slim/slim:3.*
composer require slim/php-view
composer require monolog/monolog

HINT
composer requireは、npmで言うところのnpm installと同じものです。
著者の環境では、コマンドを打ち込んでからしばらく反応が無く3分以上待たされてしまいました。気長に待ちましょう。

Autoloadの設定

次に、composer.jsonの"autoload"部分を追記します。

composer.json
{
    ...
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "config": {
        "optimize-autoloader": true
    }
}

ファイルの編集が終わったら、以下を実行します。

コマンドプロンプト
composer dumpautoload

autoloadは、PHPでモジュールファイルをロードする仕組みの事です。
簡単に説明すると、上の設定はsrcディレクトリに配置されるPHPのクラスファイルは、Appという名前空間で始まるという事を意味します。

また、最適化のために、"optimize-autoloader": trueというオプションも有効にしました。
詳細は、以下のページを確認してください。
オートローダーの最適化

HINT
クラスの追加や変更があった場合に、オートローダーの最適化を再適用するコマンドは以下の通りです。

コマンドプロンプト
composer dumpautoload           ... 最適化あり
composer dumpautoload --no-dev  ... 最適化ありで開発環境以外にリリース

バージョン管理システムからチェックアウトした直後などは、単にcomposer install [--no-dev]するだけでOKです。

composer.jsonに"optimize-autoloader": trueを記述しない場合は、composer dumpautoload -o [--no-dev]composer install --optimize-autoloader [--no-dev]とすればOKです。


全て実行し終えると、以下のようなファイルが出来上がっているはずです。

ファイル構成
c:\hello-vuejs
    + vendor
    + composer.json
    + composer.lock

vendorには、インストールしたライブラリファイルが格納されています。こちらは、バージョン管理が不要なディレクトリです。

composer.jsonには、composer initした際の各種設定と、何をインストールしたかの情報が記載されています。
composer.lockには、インストール時の厳密なライブラリのバージョン番号のスナップショットが記載されています。
こちらの2ファイルはコミットが必要です。

他の開発者は、こちらのcomposer.jsonまたはcomposer.lockから、composer installを実行して必要なライブラリをインストールすることになります。
したがって、本節で実施した、composer initとcomposer require [ライブラリ名]は、一度限りの実行になります。

他の開発者は、バージョン管理ツールからチェックアウトしたソースに対して、composer installを実行するだけで必要なライブラリを揃えることができます。

index.php、.htaccess、bootstrap.phpの作成

Webリクエストを受信した際にPHPを動作させるために必要な起動用のファイルを作成します。

ファイル構成
c:\hello-vuejs
    ...
    + public
        + index.php
        + .htaccess
    + src
        + bootstrap.php

Webリクエストを受信すると、以下の順序で処理が行われます。

  1. .htaccessの実行(リクエストされたファイルが存在しない場合に、mod_rewriteの書き換えルールが働きindex.phpが実行される)
  2. index.phpの実行(index.phpの先頭の処理でbootstrap.phpが読み込まれる。index.phpはURLの内容からコントローラークラスと実行すべきメソッドを特定して実行する)

.htaccessの内容は以下の通りです。
mod_rewriteというURL書き換えモジュールの適用と、PHP設定の初期化です。

.htacccess
<IfModule mod_rewrite.c>
  RewriteEngine On

  # Some hosts may require you to use the `RewriteBase` directive.
  # Determine the RewriteBase automatically and set it as environment variable.
  # If you are using Apache aliases to do mass virtual hosting or installed the
  # project in a subdirectory, the base path will be prepended to allow proper
  # resolution of the index.php file and to redirect to the correct URI. It will
  # work in environments without path prefix as well, providing a safe, one-size
  # fits all solution. But as you do not need it in this case, you can comment
  # the following 2 lines to eliminate the overhead.
  RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$
  RewriteRule ^(.*) - [E=BASE:%1]

  # If the above doesn't work you might need to set the `RewriteBase` directive manually, it should be the
  # absolute physical path to the directory that contains this htaccess file.
  RewriteBase /hello-vuejs

  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteRule ^ index.php [QSA,L]
</IfModule>

php_value "display_errors" "On"
php_value "error_reporting" "E_ALL"

php_value "max_execution_time" "0"

php_value "memory_limit" "1024M"
php_value "post_max_size" "1024M"
php_value "upload_max_filesize" "5G"
php_value "max_input_vars" "10000"
php_value "date.timezone" "Asia/Tokyo"

index.phpの内容は以下の通りです。
前述で作成したbootstrap.phpのロードと、HTTPリクエストのルーティング設定を行います。

例えば、http://[ドメイン]/hello-vuejs/helloを実行するとsrc\Func\Hello\Controller\HelloController.phpactionIndexが実行されます。

index.php
<?php

use Slim\Exception\NotFoundException;
use Slim\Http\Request;
use Slim\Http\Response;

$app = require __DIR__ . '/../src/bootstrap.php';

$app->any('/[{func1}[/{func2}[/{func3}]]]', function (Request $request, Response $response, array $args) use($app) {

    $func1 = $args['func1'] ?? null;
    $func1 = ucfirst($func1);
    $func2 = $args['func2'] ?? null;
    $func2 = ucfirst($func2);
    $func3 = $args['func3'] ?? null;
    $func3 = ucfirst($func3);

    $class = '';
    $method = '';
    $pattern = null;

    if ($func1 && $func2 && $func3) {
        // 例)/login/auth/exec → Login\Controller\AuthController#actionExec
        $pattern = 1;
        $class = '\\App\\Func\\' . $func1 . '\\Controller\\' . $func2 . 'Controller';
        $method = 'action' . $func3;
    } else if ($func1 && $func2) {
        // 例)/login/exec → Login\Controller\ExecController#actionIndex
        $pattern = 2;
        $class = '\\App\\Func\\' . $func1 . '\\Controller\\' . $func2 . 'Controller';
        $method = 'actionIndex';

        if (!class_exists($class)) {
            // 例)/login/exec → Login\Controller\LoginController#actionExec
            $class = '\\App\\Func\\' . $func1 . '\\Controller\\' . $func1 . 'Controller';
            $method = 'action' . $func2;
        }
    } else if ($func1) {
        // 例)/login → Login\Controller\LoginController#actionIndex
        $pattern = 3;
        $class = '\\App\\Func\\' . $func1 . '\\Controller\\' . $func1 . 'Controller';
        $method = 'actionIndex';
    }

    if (!class_exists($class)) {
        throw new NotFoundException($request, $response);
    }

    $controller = new $class($app);
    if (!method_exists($controller, $method)) {
        throw new NotFoundException($request, $response);
    }

    try {
        return $controller->$method();
    } catch (Throwable $exc) {
        $GLOBALS['unexpectedException'] = $exc;
        throw $exc;
    }
});

// Run app
$app->run();

bootstrap.phpの内容は以下の通りです。
PHPのフレームワークであるSlim関連のインスタンスを生成しています。

bootstrap.php
<?php

use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
use Monolog\Processor\UidProcessor;
use Slim\App;
use Slim\Views\PhpRenderer;

const PUBLIC_PATH = __DIR__ . '/../public/';
const SRC_PATH = __DIR__ . '/';

$app = null;

/*
 * Load the autoloader
 */
$autoloader = require __DIR__ . '/../vendor/autoload.php';

// Create Slim App instance
$app = new App([
    'settings' => [
        'displayErrorDetails' => true,
    ],
]);

/*
 * Set up dependencies
 */
$container = $app->getContainer();

// View Renderer
$container['renderer'] = function ($c) {
    $templatePath = SRC_PATH;
    return new PhpRenderer($templatePath);
};

// monolog
$container['logger'] = function ($c) {
    $settings = $c->get('settings')['logger'];
    $logger = new Logger('app');
    $logger->pushProcessor(new UidProcessor());

    $handler = new RotatingFileHandler(__DIR__ . '/../log/app.log', 0, \Monolog\Logger::DEBUG, true, 0664);
    $handler->setFilenameFormat('{date}-{filename}', 'Y/m/d');
    $logger->pushHandler($handler);

    return $logger;
};

return $app;

Controllerクラスの作成とHelloコンポーネントの修正

前述で作成したindex.phpから実行されるコントローラクラスを作成します。
Helloコンポーネントも合わせて修正します。

ファイル構成
c:\hello-vuejs
    ...
    + src\Func
        + Base\Controller
            + BaseController.php     ... Hello・Goodbyeなどの各コントローラの基底クラス
        + Goodbye\Controller
            + GoodbyeController.php  ... Goodbyeのコントローラ
        + Hello
            + Controller
                + HelloController.php    ... Helloのコントローラ
            + Front\View                 ... Helloコンポーネント(htmlとjsのみ修正)
                + Hello.html
                + Hello.js

BaseController.phpの内容は以下の通りです。

特に重要なのは、renderメソッドです。
このメソッドで、src\View\Layout\Base.php内のHTMLテンプレートをレスポンスに出力しています。

BaseController.php
<?php

namespace App\Func\Base\Controller;

use App\Common\DB\DBObserver;
use App\Common\Exception\ServiceException;
use App\Common\Log\AppLogger;
use App\Common\Message\MessageManager;
use App\Common\ResponseCache\ResponseCache;
use App\Common\Session\SessionData;
use App\Common\Util\DBUtil;
use App\Constant\CommonConstant;
use App\Func\Login\Service\LoginService;
use SebastianBergmann\RecursionContext\Exception;
use Slim\App;
use Slim\Container;
use Slim\Exception\NotFoundException;
use Slim\Http\Response;
use Slim\Http\Stream;
use const SRC_PATH;

/**
 * 基本となるコントローラークラス。
 * コントローラクラスは、本クラスを継承すること。
 */
class BaseController {

    /**
     * @var App Appオブジェクト
     */
    protected $app;

    /**
     *
     * @var Container Containerオブジェクト
     */
    protected $container;

    /**
     * @var AppLogger ロガー
     */
    protected $logger;

    /**
     * @var string レイアウト名
     */
    protected $layoutName;

    /**
     * @var array リクエストパラメータ
     */
    protected $requestParams;

    /**
     * コンストラクタ。
     * @param App $app アプリケーションオブジェクト
     */
    public function __construct(App $app) {

        $this->app = $app;
        $this->container = $app->getContainer();
        $this->logger = $this->container['logger'];

        $this->layoutName = 'Base';
        $this->requestParams = null;

    }

    /**
     * リクエストパラメータを取得する。
     * @return array リクエストパラメータ
     */
    protected function getRequestParams(): array {

        if ($this->requestParams !== null) {
            return $this->requestParams;
        }

        $request = $this->container->request;

        $getParams = $request->getQueryParams() ?? [];
        $postPutParams = $request->getParsedBody() ?? [];

        $allParams = array_merge($getParams, $postPutParams);
        $this->requestParams = $allParams;

        return $allParams;
    }

    /**
     * 描画メソッド。
     * @param string $contentsViewPath コンテンツビューのパス … /srcディレクトリからのパスを指定する(例:/Func/Login/Front/View/Login)
     * @param array $contentsData データ
     * @param string $layoutName レイアウトページ名 … /src/View/Layout下のレイアウトページを指定する(例:Base.phpの場合は、"Base"と指定する)
     * @param array $importWidget インポートするWidget
     * @return type
     */
    protected function render(string $contentsViewPath, array $contentsData, string $layoutName = null) {

        if (!file_exists(SRC_PATH . $contentsViewPath . '.vue')) {
            // ファイルが存在しないため、例外を発行する(基本的には発生しないエラー)
            throw new \Exception("コンテンツビューファイル未存在エラー contentsViewPath={$contentsViewPath}");
        }

        $contentsViewName = basename($contentsViewPath);

        // レイアウトページの設定
        if ($layoutName === null) {
            $layoutName = $this->layoutName;
        }
        $layoutPhp = '/View/Layout/' . $layoutName . '.php';

        // コンテナから関連するオブジェクトを取得
        $container = $this->container;
        $request = $container->request;
        $response = $container->response;
        $renderer = $container->renderer;
        $baseUrl = $request->getUri()->getBasePath();

        // リクエストパラメーターを取得する
        $requestParams = $this->getRequestParams();

        // レイアウトページのデータオブジェクト
        $layoutDataObj = [
            '__baseUrl' => $baseUrl,
            '__requestParams' => $requestParams,
            '__contentsViewPath' => $contentsViewPath,
            '__contentsViewName' => $contentsViewName,
            '__contentsData' => $contentsData,
        ];

        return $renderer->render($response
                        , $layoutPhp
                        , $layoutDataObj);
    }

    /**
     * Json出力メソッド。
     * @param mixed $data The data
     * @param int $status The HTTP status code.
     * @param int $encodingOptions Json encoding options
     * @return type
     */
    protected function renderJson($data, int $status = null, int $encodingOptions = 0) {

        $response = $this->container->response;

        return $response->withJson($data, $status, $encodingOptions);
    }

}

HelloController.phpで重要なのは、actionIndexメソッドです。
内部では、BaseController#renderメソッドを呼び出しています。引数にHelloコンポーネントのパスを渡すことで、最終的にVue.jsのHelloコンポーネントを表示するように指示しています。

また、Helloコンポーネント上のボタン押下時のajax通信に対応するために、actionTalkメソッドを設けました。こちらは、BaseController#renderJsonメソッドでtalkMessageデータをレスポンスとして返却しています。

HelloController.php
<?php

namespace App\Func\Hello\Controller;

use App\Func\Base\Controller\BaseController;
use Slim\App;

/**
 * Helloコントローラー。
 */
class HelloController extends BaseController {

    /**
     * コンストラクタ。
     * @param App $app アプリケーションオブジェクト
     */
    public function __construct(App $app) {
        parent::__construct($app);
    }

    /**
     * 表示処理。
     */
    public function actionIndex() {

        return $this->render('/Func/Hello/Front/View/Hello', []);
    }

    /**
     * 話す処理。
     */
    public function actionTalk() {

        $params = $this->getRequestParams();

        $data = ['talkMessage'=>"Hi, I came from space."];

        return $this->renderJson($data);
    }
}

Helloコンポーネントのhtmlとjsファイルも修正します。
修正内容は、ボタンの追加・ボタン押下時の処理・返却メッセージの表示領域の追加です。

Hello.html
<div class="hello-component" v-cloak ref="view">

    <div>
        This is Hello Component.<br>
        {{message}}
    </div>

    <div>
        <button type="button" @click="talk">Hello?</button>
    </div>

    <div>
        <p>{{talkMessage}}</p>
    </div>

</div>
Hello.js

export default {
    data() {
        return {
            message: 'Hello Vue.js',
            talkMessage: ''
        };
    },
    methods: {
        talk() {

            $.ajax({
                url: AppContext.baseUrl + '/hello/talk'
            }).then((data)=> {
                this.talkMessage = data.talkMessage;
            });

        }
    }
}

GoodbyeController.phpは、HelloController.phpとほとんど同じ内容です(ただしactionTalkメソッドは設けていません)。

GoodbyeController.php
<?php

namespace App\Func\Goodbye\Controller;

use App\Func\Base\Controller\BaseController;
use Slim\App;

/**
 * Goodbyeコントローラー。
 */
class GoodbyeController extends BaseController {

    /**
     * コンストラクタ。
     * @param App $app アプリケーションオブジェクト
     */
    public function __construct(App $app) {
        parent::__construct($app);
    }

    /**
     * 表示処理。
     */
    public function actionIndex() {

        return $this->render('/Func/Goodbye/Front/View/Goodbye', []);
    }

}

レイアウトHTML(Base.php)の作成

Base.phpは、前回の記事で作成したindex.htmlに代わるHTMLのテンプレートです。
ついでに、C:\hello-vuejs\public\index.htmlを削除しましょう。

ファイル構成
c:\hello-vuejs
    ...
    + public
        + index.html ... !!! 削除する
    + src\View\Layout
        + Base.php

前回の記事で、index.htmlではPOSTパラメータにアクセスすることができないと言いました。
ですが、Base.phpで動的にHTMLを生成して、リクエストパラメータ(GETやPOST)やサーバー側で任意に設定した情報を、JavaScriptコードとして生成することで、JavaScript側でPOSTパラメータやその他サーバーで設定した情報に(AppContextというグローバル変数経由で)アクセスできるようになります。

Base.php
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta http-equiv="Pragma" content="no-cache">
        <meta http-equiv="Cache-Control" content="no-cache">
        <meta http-equiv="Expires" content="Mon, 26 Jul 1997 05:00:00 GMT">
        <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale = 1.0, maximum-scale = 1.0, user-scalable = no">

        <title>Hello Vue.js</title>

    </head>

    <body class="layout-base">

        <div id="app" v-cloak>
        </div>

        <?php /* グローバル関連の定義 */ ?>
        <script type="text/javascript">

            // アプリケーションコンテキストの定義(アプリケーション全体で使用したいデータ等を定義、イミュータブルなオブジェクトにする)
            var AppContext = Object.freeze({
                name: 'Hello Vue.js',
                baseUrl: '<?= $__baseUrl ?>',
                requestParams: <?= json_encode($__requestParams) ?>,
                contentsComponentPath: '<?= $__contentsViewPath ?>',
                contentsComponentId: '<?= $__contentsViewName ?>Component',
                contentsData: <?= json_encode($__contentsData) ?>
            });

        </script>

        <?php /* バンドルファイルの読み込み */ ?>
        <?php /* ※本ファイルの読み込み後に、main.jsで定義したグローバル変数などが読み込みできるようになる */ ?>
        <script src="<?= $__baseUrl . '/dist/bundle.js?' . filemtime(PUBLIC_PATH . 'dist/bundle.js') ?>" ></script>

        <?php /* Vue.js生成 */ ?>
        <script type="text/javascript">
            // Vue.js生成
            window.AppFuncs.createVuejs(AppContext.contentsComponentPath, AppContext.contentsComponentId);
        </script>

    </body>

</html>

main.jsの修正

各種ファイルの新規作成に伴い、main.jsの修正が必要になります。

ファイル構成
c:\hello-vuejs
    ...
    + src
        + main.js

その前に、jQueryをインストールしたいので、cd c:\hello-vuejsして以下を実行しましょう。
jQueryは、ajaxメソッドを利用するために導入しました。axiosでもOKです。

コマンドプロンプト
npm install -D jquery

main.jsを以下の内容で置き換えます。

大きく変更された箇所は、jQueryのインポート、Vue.jsインスタンスの生成メソッドをAppFuncsに移動したこと。また、最後の行でwindowオブジェクトにjQueryとAppfuncsを設定しています。

windowオブジェクトはグローバルオブジェクトなので、このようにしないと、bundle.js(main.jsが含まれている最終成果物ファイル)の外から、これらのオブジェクトを参照することができないのです。

main.js
// -----------------------------------------------------------------------------
// ライブラリのインポート
// -----------------------------------------------------------------------------
// JQuery
import $ from 'jquery';
// VueJs
import Vue from 'vue';

/**
 * アプリケーションの関数定義
 */
const AppFuncs = {

    /**
     * Vue.jsインスタンスを生成する。
     * @param {String} componentPath コンポーネントパス
     * @param {String} componentId コンポーネントID
     * @returns {Vue} Vue.jsインスタンス
     */
    createVuejs(componentPath, componentId) {

        // 動的インポートを実施
        //
        // componentPathを以下のルールで変換
        // 例)ログインの場合
        //    /Func/Login/Front/View/Login
        //     ↓
        //   ./Func/Login/Front/View/Login.vue
        const componentLoadPromise = import("./" + componentPath.replace(/^\//, "") + ".vue");

        componentLoadPromise.then(function (value) {
            // 動的インポート完了

            // VueのComponentのグローバル登録は、Vueインスタンス生成前に実施する必要あり
            Vue.component(componentId, value.default /* import関数の戻り値に default があるので、そちらを使用する(defaultエクスポート定義を読み込むという意味になる) */);
            // Vueインスタンスの生成
            const vm = new Vue({
                el: '#app',
                render: h => h(componentId)
            });

            return vm;
        });

    }

};

// -----------------------------------------------------------------------------
// グローバル変数の定義
// -----------------------------------------------------------------------------
window.$ = $; // jQueryオブジェクト
window.AppFuncs = AppFuncs;

HINT
ES6で定義されたデータや関数にES6未満のJSコードからアクセスする場合、ES6側で対象データをwindowオブジェクト(グローバルな領域に)に設定することで、アクセスが可能になります。

webpack.dev.jsの修正

前回はNode.jsをWebサーバーとして利用していましたが、PHPを使用するために今回からApacheをWebサーバーとして使用することになります。ただ、Apacheを使用するとHMR(Hot Module Replacement)が機能しなくなります。そのためプロキシ設定を行います。

プロキシ設定は、webpack.dev.jsで行います。

ファイル構成
c:\hello-vuejs
    ...
    + webpack.dev.js

修正内容は以下のdevServer部分です。devServer内の最終行にproxyを丸ごと貼り付けてください。
ついでに、devServer内のopenPageを変更(index.htmlを削除したので)しましょう。

HMR(Hot Module Replacement)を利用するためには、dist/bundle.jsファイルは今まで通りNode.jsサーバーに処理させたいので、dist以下のファイルはNode.jsで、それ以外の全てのリクエストはApache Webサーバーにリクエストさせるといった事をproxy部分に記述しています。

webpack.dev.js
...
module.exports = merge(common, {
    ...
    devServer: {
        ...
        // ブラウザの表示ページ
        openPage: prefixUri.substring(1) + '/hello',
        ...
        // プロキシ設定
        proxy: [
            {
                context: [
                    // すべてのリクエストを対象とする
                    '**',
                    // dist以下は対象外とする
                    '!' + prefixUri + '/dist/**'
                ],
                // Apache Webサーバーに転送する
                target: 'http://localhost:5555'
            }
        ]
    }
});

動かしてみる

処理イメージは下図の通りです。

hello-vuejs2_index_php_flow.png

C:\hello-vuejsで以下のコマンドを実行してください。すると、ブラウザが起動してページが表示されるはずです。

コマンドプロンプト
npm run start

hello-vuejs2_screen_hello.png

Helloコンポーネント上の『Hello?』ボタンを押してPHPと通信できるか試してみましょう。
すると、下図のように、『Hi, I came from space.』というメッセージが表示されましたね。

hello-vuejs2_screen_hello_after_pressed_button.png

HMRが有効かどうかも試しましょう。Hello.jsを開いて、dataのmessageを編集してみると…。

Hello.js
...
    data() {
        return {
            message: 'ニイハオ Vue.js',
            talkMessage: ''
        };
    },
...

hello-vuejs2_screen_hello_after_edit.png

Goodbyeコンポーネントの画面も表示しましょう。
http://localhost:8080/hello-vuejs/goodbye

hello-vuejs2_screen_goodbye.png

リリースビルドの実行

リリースビルドの実行については、フロントエンドで実行したnpm run buildと何ら変わりありません。
本番環境にリリースするファイルを作成するには、npm run buildしてbundle.jsを作成しましょう。

それと、デバッグ時はNode.jsのWebサーバーを経由してApacheにアクセスしていましたが、リリース時はApacheに直接接続するので、URLは以下のように(Apacheに直接接続)します。

http://localhost:5555/hello-vuejs/hello
http://localhost:5555/hello-vuejs/goodbye

最終的なファイル構成

ファイルの最終的な構成は以下の通りです。
フロントエンドとバックエンドのソースが混合された構成になっています。

ファイル構成
C:\hello-vuejs
    + node_modules               ... npmでインストールしたライブラリ(バージョン管理対象外とするディレクトリ)
    + public                     ... Webサーバーの公開ディレクトリ
        + .htaccess              ... Apache設定ファイル
        + index.php              ... フロントコントローラ
    + src
        + Func                   ... 機能別ディレクトリ
            + Base
                + Controller
                    + BaseController.php
            + Hello
                + Controller
                    + HelloController.php ... Helloコントローラー
                + Front\View              ... Helloコンポーネント
                    + Hello.vue
                    + Hello.html
                    + Hello.css
                    + Hello.js
            + Goodbye
                + Controller
                    + GoodbyeController.php ... Goodbyeコントローラー
                + Front\View                ... Goodbyeコンポーネント
                    + Goodbye.vue
                    + Goodbye.html
                    + Goodbye.css
                    + Goodbye.js
        + View
            + Layout
                + Base.php       ... HTMLテンプレート
        + bootstrap.php          ... PHP起動用共通処理
        + main.js                ... webpackエントリポイントファイル
    + vendor                     ... Composerでインストールしたライブラリ(バージョン管理対象外とするディレクトリ)
    + composer.json              ... Composerに関する設定ファイル
    + composer.lock              ... composer requireのスナップショットファイル
    + package.json               ... npmに関する設定ファイル
    + package-lock.json          ... npm installのスナップショットファイル
    + webpack.common.js          ... webpackの共通設定ファイル
    + webpack.dev.js             ... webpackの開発用設定ファイル
    + webpack.prod.js            ... webpackのリリース用設定ファイル

バックエンドのまとめ

以上で、本記事が目標としていたWebアプリケーションのひな型が完成しました。
SPA(シングルページアプリケーション)でもない、従来のページ遷移方式で、さらにはVue.jsのSFCを用いることもできました。

webpackのビルドも、設定さえ正しくできれば開発効率を妨げる要因はなく、むしろ開発効率が向上することが実感できました。
JavaScriptのES6も慣れてくると非常に書きやすいです。本記事を応用すればTypeScriptを導入することもできます。

ただ、webpackの流儀に従うのであれば、依存するライブラリが増えた場合には、main.jsにimportを追記する必要が有ります。
画像ファイルもbundle.jsにマージしたいこともあるでしょうし、Webフォントも一緒に取り扱うという事であればwebpackの設定と深く付き合っていく必要があります。これらは、公式リファレンスを見たり、色々な記事を見て勉強していくしかありませんね。

今回作成したソースはGitHubにアップしておきました。以下のページにアクセスして確認してみてください。
hello-vuejsソース(PHP統合版)

:grinning:

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?