前回は、Laravelを最適化した上でCloud Run
にデプロイする方法について説明しました。
これにより、バックエンドは高速に動作するようになったハズです。
次はフロント側(React)について考えていきます。
バンドルしたjsファイルをCloud Storageにアップしてパフォーマンスを改善する
'Laravel-mix'はとても優れたライブラリだと思っています。通常であれば煩わしいTypescriptやReactのコンパイルなどの設定をラップして、最適化までしてくれるからです。しかし、デフォルトでは、Laravel-mix
でコンパイルしたassetファイルはプロジェクト内に保管されます。
minify
されているとはいえ、Webサイトにアクセスすする度にバンドルされたapp.js
やapp.css
をCloud Run
からダウンロードすることになり、最終的にはフロントの規模が大きくなった時に思ったようなパフォーマンスを出すことはできなくなります。
この部分を解消するため、Laravel-mix
で生成したassetファイルは是非とも、CDNにおきたいところです。
ではこのために、どのようにすれば良いか説明していきます。
laravel-mixを使ってReactを有効にする
Cloud Runに関する説明とは逸れてしまいますが、一応、説明しておきます。
laravel/uiをインストールし、必要な設定ファイルを追加・更新する
まずはローカルで動作確認をしていきましょう。必要に応じて、ボリュームをマウントしたり、VSCodeのリモートコンテナを使ったりすると、どのようにファイルが更新されるかわかりやすいと思います。
(例)ホストのディレクトリをマウントして実行する
docker run --volume $PWD/app:/var/www/html -p 8080:80 -d cloud-run-demo
では、前回の記事で作成したDockerコンテナ内で下記のコマンドを実行していきます。
/var/www/html# composer require laravel/ui
/var/www/html# php artisan ui react
/var/www/html# npm install
これで基本的にReact
はインストールされるのですが、せっかくなのでTypescript
も導入します!
型の定義をいちいちしなければならないなど、ちょっとしんどいですが変数に何が入っているか、何を入れれば良いかが明確にわかるので弊社では重宝しています!
まずは同じくDockerコンテナ内で、下記のコマンドを実行します。
/var/www/html# npm add -D typescript @types/node @types/react @types/react-dom
appディレクトリの配下に、tsconfig.json
を作成します。弊社の場合は下記のように書いています。
{
"compilerOptions": {
"jsx": "react",
"outDir": "./built/",
"sourceMap": true,
"strict": true,
"noImplicitReturns": true,
"noImplicitAny": true,
"module": "es2015",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"moduleResolution": "node",
"target": "es5",
"allowSyntheticDefaultImports": true,
"lib": [
"es2016",
"dom"
]
},
"include": [
"resources/assets/ts/**/*"
]
}
仕上げにwebpack.mix.js
を下記の通りに書き換えてあげましょう。
const mix = require("laravel-mix");
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix.ts("resources/assets/ts/app.tsx", "public/js")
.sass("resources/assets/sass/app.scss", "public/css")
.options({
processCssUrls: false
})
.webpackConfig({
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
exclude: /node_modules/
},
{
test: /\.js?$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/react"]
}
}
},
{
test: /\.s[ac]ss$/i,
use: [
{
loader: "resolve-url-loader",
options: {}
},
{
loader: "sass-loader",
options: {
sourceMap: true
}
}
]
}
]
},
resolve: {
extensions: ["*", ".js", ".jsx", ".ts", ".tsx"],
alias: {
react: path.resolve("./node_modules/react")
}
}
});
ここまでで設定ファイルの変更は完了です。次からは実際に表示をさせてみましょう!
サンプル画面を表示する。
弊社の場合、ディレクトリ構造が複雑になりそうだったので、resources
配下にassets
というディレクトリを作ってその下にファイルを作成しています。手順は以下の通りです。
- デフォルトでは
resources/js
配下にあったファイルをresources/assets/ts
に移動する。 - 同じくデフォルトでは
resources/sass
配下にあったファイルをresources/assets/sass
に移動する。 - Example.jsの名前をExample.tsxに変更する
- app.jsの名前をapp.tsxに変更する
対応はこの程度です。今回、ファイル内容の変更は行っていません。ディレクトリ の見た目は下記のようになるはずです。
最後に、bladeの記述を変更します。
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<link rel="stylesheet" href="{{mix('css/app.css') }}">
</head>
<body>
<div class="flex-center position-ref full-height">
<div id="example"></div>
</div>
<script src="{{ mix('js/app.js')}} "></script>
</body>
</html>
ポイントは
<link rel="stylesheet" href="{{mix('css/app.css') }}">
であったり、
<script src="{{ mix('js/app.js')}} "></script>
でバンドルしたcss
やjs
を読み込むようにしていることです。
では、早速コンパイルして表示してみましょう!!コマンドは下記の通りです。
npm install && npm run dev
うまくいかない場合は、下記のコマンドでキャッシュしたviewファイルをクリアしてください。
/var/www/html# php artisan view:clear
エラーなくコンパイされていればapp/public/js
やapp/public/css
配下にファイルが作られているはずです。
長くなってしまいましたが、Laravel-mixを使ってReactをインストールすることができました!
Cloud Strageにアップするためのwebpackの書き方
次はいよいよ本題、生成したjs
やcss
をCloud Strageにアップする手順を紹介します。
webpack-google-cloud-storage-pluginをインストールする
Dockerコンテナ内で下記のコマンドを実行すればOKです。
npm install --save webpack-google-cloud-storage-plugin
webpack.mix.jsを修正する
webpack-google-cloud-storage-plugin
に関する処理を追加します。mix.inProduction()
の判定がありますが、これは
npm run prod
の時だけ、このプラグインを使いたいための判定です。(ローカルでのテストの時にはもちろん実行したくないですからね。)
const mix = require("laravel-mix");
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
//ここを追加!
const WebpackGoogleCloudStoragePlugin = require("webpack-google-cloud-storage-plugin");
const plugins = [];
if (mix.inProduction()) {
mix.version();
plugins.push(
new WebpackGoogleCloudStoragePlugin({
directory: "public",
include: ["app.js","app.css"],
exclude: ["images"],
storageOptions: {
keyFilename: process.env.MIX_CLOUD_STORAGE_CREDENTIAL_FILE
},
uploadOptions: {
bucketName: process.env.MIX_PUBLIC_BUCKET_NAME,
destinationNameFn: file => path.join("", file.path),
gzip: true,
makePublic: true,
resumable: true,
concurrency: 5
}
})
);
}
//追加ここまで
mix.ts("resources/assets/ts/app.tsx", "public/js")
.sass("resources/assets/sass/app.scss", "public/css")
.options({
processCssUrls: false
})
.webpackConfig({
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
exclude: /node_modules/
},
{
test: /\.js?$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/react"]
}
}
},
{
test: /\.s[ac]ss$/i,
use: [
{
loader: "resolve-url-loader",
options: {}
},
{
loader: "sass-loader",
options: {
sourceMap: true
}
}
]
}
]
},
resolve: {
extensions: ["*", ".js", ".jsx", ".ts", ".tsx"],
alias: {
react: path.resolve("./node_modules/react")
}
},
plugins: plugins//ここも追加しています。
});
Cloud Storageにバケットを作る
もちろん、これがないと始まりませんね。GCPでバケットを作ったら、その名前を控えておきましょう。
また、サービスアカウントキーも必要となりますので、こちらのページを参考にJSONファイルをダウンロードしてapp
直下に保存してください。
アップするための情報を.env
に追加する
.env
ファイルに下記の記述を追記します。
# Bundleされたファイルの配置バケット
MIX_PUBLIC_BUCKET_NAME="joolen-cloud-run-example"
# デプロイする時に利用するCloud StorageのCredentialファイル名
MIX_CLOUD_STORAGE_CREDENTIAL_FILE="your-service-account.json"
ローカルでnpm run prod!
ここまで対応したら、
npm run prod
を実行しましょう。エラーなく処理が完了したらCloud Storage
には下記のようなディレクトリやファイルができるはずです。
公式ページを参考にしてapp.js
とapp.css
を公開しましょう。
これで、laravel-mix
で作成したjs
やcss
の準備もできるようになりました
ちなみに、公式ページに記載がありますが、
一般公開で閲覧可能なオブジェクトはデフォルトで Cloud Storage ネットワークにキャッシュされるので、特に設定しなくても、Cloud Storage は基本的にコンテンツ配信ネットワーク(CDN)のように機能します。
とのことです。これはありがたいですね。
Cloud RunにデプロイするためのDockerfileを修正する。
ここまで準備が整ったら、Dokcerfile
の修正は簡単です。下記の1行を追記しましょう。
RUN npm install && npm run prod
最終的なDockerfileは下記の通りになります。
FROM php:7.4-apache
WORKDIR /var/www/html
# 必要最低限のPHP拡張とNode.jsのインストール
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -
RUN apt-get update \
&& apt-get install -y libzip-dev libpq-dev mariadb-client unzip git libfreetype6-dev libjpeg62-turbo-dev libpng-dev \
&& docker-php-ext-configure gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/ \
&& docker-php-ext-install zip pdo_mysql mysqli gd exif opcache \
&& a2enmod rewrite \
&& apt-get -y install vim \
&& apt-get install -y nodejs \
&& git clone https://github.com/phpredis/phpredis.git /usr/src/php/ext/redis \
&& docker-php-ext-install redis
EXPOSE 80
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME /composer
ENV PATH $PATH:/composer/vendor/bin
ENV APACHE_LOG_DIR /var/log/apache2
# composerをインストール
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
COPY ./apache/*.conf /etc/apache2/sites-enabled/
COPY ./php/php.ini /usr/local/etc/php/php.ini
COPY ./app /var/www/html
# composer install を高速化するためのライブラリをグローバルインストール
RUN composer global require hirak/prestissimo
RUN composer install --optimize-autoloader --no-dev
RUN php artisan key:generate \
&& php artisan config:cache \
&& php artisan view:cache
# ReactのモジュールをコンパイルしてCloud Storageにアップロード
RUN npm install && npm run prod
RUN chmod 777 -R /var/www/html/storage/framework/
あとは前回と同様に
gcloud builds submit --config cloudbuild.json --timeout="30m" --ignore-file ".gcloudignore"
を実行してコンテナイメージを作成してコンテナレジストリにpush
できます。
Cloud Runの環境変数を修正してjs
やcss
はCloud Storageを参照するように変更する
Laravelのコンフィグファイルを修正する。
app/config/app.php
に下記の1行を追加します。(場所はどこでも大丈夫です)
これにより、mix関数が生成するベースURLを変更することができます。
'mix_url' => env('MIX_ASSET_URL', null),
.envの環境変数にAsset_URLを追記する
下記のようにMIX_ASSET_URL
追加します。URLには、バケットの公開URLのpublic
までです。(スラッシュを付けない)
# CDNに配置されたBundleファイルのURL
MIX_ASSET_URL="https://storage.googleapis.com/{your_bucket_name}/public"
view:cache
をしないのであれば、下記のようにCloud Run
の環境変数に設定をすることもできます。
また、ローカルでReactをビルドしたときの名残でpublic配下にmix-manifest.json
があると思います。これも消してしましましょう!これがあると、参照すべきファイルを間違えてしまう可能性があります。
これで、app.js
やapp.css
はCloud Storageにあるファイルを参照するようになります。
様々なライブラリを使うようになったりして、バンドルしたファイルが大きくなればなるほど、非常に効果のある対応です。
こちらも、弊社プロジェクトでは数百ms単位での効果があり、体感できるほどのパフォーマンス向上につながりました
今回も長文にお付き合い頂きまして、ありがとうございました!
サーバレスなLaravel & Reactで快適開発体験を体験してみてくださいね。
次回はこれらの仕組みの実現に必要な設定やコストについて触れていきたいと思います。