こんにちは!株式会社フォトラクションでWebエンジニアをしています南風原(はえばる)です。 Photoruction Advent Calendar 2021の9日目の記事です。
導入
現在、担当している案件のフロントエンドがほぼリプレイス並みの開発というタイミングもあり、技術スタックを新しく見直しました。
開発環境はViteを用いてVue.js + Vue Composition API + TypeScriptで機能開発を行っています。
今回は環境構築までの流れを書いていこうと思います。
その前に、現状のPhotorucutionのフロントエンド事情を軽く説明します。
プロダクトの約7割はBackbone.js、jQueryで実装されており、画面の表示はHandlebars.jsというテンプレートエンジンとLaravelのbladeファイルを用いて実装されています。
3年くらい前から新機能の開発はVue.jsで開発していく流れにはなっていますが、まだまだレガシーコード満載な状況です笑
今後さらにフロントエンドの開発環境が良いものとなるように一緒に改善に取り組んでいけるエンジニアを絶賛募集中です!
Viteとは
ViteはVue.jsの開発者であるEvan You氏が開発しているノーバンドルで高速に動作するビルドツールになります。
「ヴィート」と読むそうで、フランス語で「高速」の意味らしいです。(このツールを知った当初は、バイトって読んでました笑)
「高速」というだけあって、プロジェクトの作成も一瞬。ビルド時間もあっというまで開発体験がめちゃくちゃいいです。
Vue.js以外にも、ReactやPreactのビルドもサポートしているそうです。
環境構築編
[前提] 既存プロジェクトの大体のディレクトリ構成になります。
root/
├ app/
├ bootstrap/
├ config/
├ database/
├ public/
│ └ js/
│ └ dst/
├ resources/
│ └ assets/
│ └ js/
│ └ app/
│ └ lang/
│ └ views/
├ package.json
1. Viteを使って新しくプロジェクトを作成
既存プロジェクトで使用しているパッケージとは分離するために、新しいディレクトリを作成しViteをインストールします。(メッセージの指示に従って環境のセットアップをしていく)
$ mkdir resources/assets/js/app/[newProject]
$ cd resources/assets/js/app/[newProject]
$ npm init vite@latest
2. Viteで作成した新プロジェクトに移動しビルド実行
$ cd [newProject]/
$ npm install
$ npm run dev
3. Viteプロジェクトのビルド時に生成されたファイルの出力先を変更
vite.config.js
内にビルド時のファイル出力先を既存プロジェクトのファイルパスになるようにします。
build: {
outDir: '../../../../../public/js/dst/[newProject]'
},
4. scssファイルのインポート設定
vite.config.js
内にコンポーネント共通で参照するscssファイルのパスを追加します。
css: {
preprocessorOptions: {
scss: {
additionalData:
`@import "./src/assets/scss/variables.scss";
@import "./src/assets/scss/common.scss";`,
}
}
}
5. 既存プロジェクト(Laravel)にViteプロジェクトを読み込むヘルパー関数を作成
<?php
use GuzzleHttp\\Client;
use Illuminate\\Support\\HtmlString;
function vite_assets($path, $fileName): HtmlString
{
$devServerIsRunning = false;
if (config('app.env') === 'local') {
try {
$client = new Client();
$res = $client->get("<https://localhost:3000/{$fileName}>");
$devServerIsRunning = true;
} catch ( \\Exception $e ) {
}
}
// ローカルサーバに接続できた場合
if ($devServerIsRunning) {
return new HtmlString(<<<HTML
<script type="module" src="<https://localhost:3000/@vite/client>"></script>
<script type="module" src="<https://localhost:3000/{$fileName}>"></script>
HTML);
}
// その他の環境ではコンパイルしたjsを返す
$manifest = json_decode(file_get_contents(public_path("{$path}/manifest.json")), true);
return new HtmlString(<<<HTML
<script type="module" src="/{$path}/{$manifest[$fileName]['file']}"></script>
<link rel="stylesheet" href="/{$path}/{$manifest[$fileName]['css'][0]}">
HTML);
}
※ローカルサーバー(https://localhost:3000/)で開発する際はlocalhostへのアクセス許可が必要になります。
LaravelのbladeファイルにVueコンポーネントをマウントする方法
既存機能の一部分に新しく作成したVueコンポーネントを表示したいパターンが有るかと思います。
その場合の方法を紹介します。
import FeatureContent from '@/pages/FeatureContent.vue'
// 任意の要素(DOM)の変化を監視する
const observer = new MutationObserver(records => {
records.forEach(mutation => {
const target = (mutation.target as any).getAttribute('id')
switch (target) {
case 'feature_content': {
const app = setFeatureContentApp()
vue = app
break
}
}
})
})
const featureApp = document.getElementById('feature_app')
if (featureApp) {
observer.observe(featureApp, {
attributes: true,
subtree: true,
attributeFilter: ['class'],
})
}
// Vueコンポーネントを任意のDOMにマウントする
const setFeatureContentApp = () => {
const featureContentElm = document.getElementById('feature_content')
if (!featureContentElm) return
return new Vue({
render: (h) => h(FeatureContent),
}).$mount('#feature_content')
bladeファイル
<div id="feature_app">
<div id="feature_menu">
<!-- 既存View -->
</div>
<div id="feature_content" /> ← 新Vueコンポーネントがマウントされる
</div>
まとめ
Backbone.jsで作成された既存機能をVue.jsにリプレイスしていく場合、全てを一から作り直すというのは時間と開発コストがかかります。
上記のような方法で、既存機能への影響を最小限に抑えながら少しずつリプレイスをしていくことが可能だということを今回担当している案件を通して学べました。
これから先、UI/UX含めフロントエンドをどんどんアップデートしていき、ユーザへより一層価値の高いサービスを提供できるようなプロダクトを作っていきたいものです。
ぜひ、一緒にフロントエンド開発を盛り上げていただける方をお待ちしております!
株式会社フォトラクションでは一緒に働く仲間を募集していますhttps://corporate.photoruction.com/careers/