【メモ】Railsにおけるフロントエンド周辺のディレクトリ構造について
自分用です。
だいぶ前に作ったRailsのプロジェクトで色々古いところもあると思いますが、後学のために整理しておきたかったので振り返りも兼ねて。
全体像としてはまずAPIサーバーとしてRailsがあり、フロント側の描画にはRiot.jsとSassを利用するという構図になっています。
ユーザーはpublicディレクトリ以下に配置された静的なhtmlを受け取り、そのhtml中のRiotで定義されたタグが展開されるという感じ。
で、このRiotのタグとかSassのファイルはどこかしらのディレクトリに纏めてwebpackでビルドするんですが、その時どういうディレクトリ構造だといいかな~というのを考えたアレです。
この時考えたのはこんな感じ。
frontend
├─common : モジュールより粒度の小さい画面ごとに共通の要素を定義したjsやscssを入れる(なんかの配列とか)
├─modules : モジュール単位で分けられたtagとscss
├─api-clients : APIクライアント
└─scenes : 画面単位で分けられたjsとscssがあり、その画面で利用するモジュール等を統括する
helpersとか作ってヘルパーメソッド入れるところを作ってもいいかもしれない。commonと役割が被りそうだけど。
このディレクトリ構造で例えばexampleという画面を表示する場合、それぞれのソースコード配置場所は、
画面のHTML : public/example.html
その画面で使うモジュールをまとめるためのJS : frontend/scenes/example/example.js
その画面で使うcssをまとめるためのscss : frontend/scenes/example/example.scss
という感じになります。
以下、こういうふうに役割分けてましたというソース。
public/example.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/outputs/example.css">
</head>
<body>
<hoge></hoge>
</body>
<script src="/outputs/example.js"></script>
</html>
outputs以下はビルドしたファイルの出力場所です。
scenes/example/example.js
require('../../modules/hoge/hoge.tag');
const ExampleAPI = require('../../api-clients/example-api.js');
const exampleAPI = new ExampleAPI();
riot.mount('hoge', {exampleAPI: exampleAPI});
example画面で利用するAPIクライアントのオブジェクトを作成し、Riotのタグに引数で渡してます。
scenes/example/example.scss
@import "../../common/main";
@import "../../modules/hoge/hoge";
example画面ではmainという共通要素とhoge要素を利用するというイメージ。
api-clients/base-api.js
'use strict';
module.exports = class BaseAPI {
constructor() {
this.host = __API__;
}
get(path) {
return $.ajax({
url: this.host + path
});
}
send(method, path, form) {
return $.ajax({
url: this.host + path,
type: method,
data: form,
contentType: "application/json; charset=UTF-8",
cache: false,
processData: false
});
}
post(path, form) {
return this.send('POST', path, form);
}
patch(path, form) {
return this.send('PATCH', path, form);
}
delete(path, form) {
return this.send('DELETE', path, form);
}
};
APIクライアントのベースクラスを定義してます。雰囲気で書いてるので動かないかも。
api-clients/example-api.js
'use strict';
const BaseAPI = require('./base-api.js');
module.exports = class ExampleAPI extends BaseAPI {
constructor() {
super();
const path = '/api/v1/hoge';
this.path = path;
}
getHoge() {
const path = this.path;
return this.get(path);
}
createHoge() {
const path = this.path;
const form = {};
return this.post(path, form)
}
updateHoge() {
const path = this.path;
const form = {};
return this.patch(path, form)
}
deleteHoge() {
const path = this.path;
const form = {};
return this.delete(path, form);
}
}
モジュール別にAPIにアクセスするメソッドを定義してます。
各メソッドがどういうパラメータを投げるかはベースクラス含めて要調整。
modules/hoge/hoge.tag
<hoge>
<div class="hoge">
hogehoge
</div>
<div class="gethoge" onclick={onClickTest}>
gethoge
</div>
<script type="text/javascript">
const _this = this;
const _api = opts.exampleAPI;
_this.onClickTest = function() {
_api.getHoge()
.done(function (data) {
console.log(data);
}).fail(function () {
console.error('Error...');
});
}
</script>
</hoge>
tagの中からAPIメソッドにアクセスして、取得したデータを元に色々表示内容を変えたり。
こういうアーキテクチャについてはあまり明るくないので、もっと良さげな方法がありそう。勉強していきたいですね。