見出し画像

【メモ】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メソッドにアクセスして、取得したデータを元に色々表示内容を変えたり。

こういうアーキテクチャについてはあまり明るくないので、もっと良さげな方法がありそう。勉強していきたいですね。

この記事が気に入ったらサポートをしてみませんか?