この投稿は Shinjuku.rb の発表資料です
これまでの @tomlla のあらすじ
2012〜
ネットワーク機器開発 c, sh, php, python
target: Linux 2.6系 x86,arm
2014〜
java8 + PlayFramework1 社内システムの開発
(GulpでビルドしてVue+coffeeをやってた)
2016〜
rails(coffee+jquery)
不動産系ベンチャー
2017 〜
java + spring(vue, vue+typescript)
広告出稿系システム
2018.04〜
rails ← いまのジョブはこれ♪
ちなみに 今の案件こんな感じです
- k8s on GCP
- CloudSQL
- ruby 2.5.1, rails 5.2.1 (最近上げた)
- circleci
- vuejs(with vue-cli-service)
- 1万ユーザーくらいの国内向けSaaS
- IEは対応しない
個人的な課題感
サーバーサイドレンダリングだけだと...
- form系
- 入力フォームでLive Validationしたいよね?
- ネストした配列を含むフォームのバリデーションつらくない?
- ユーザーの入力値, バリデーションエラーをサーバー側で再レンダリングするのつらくない?
- 大量/大容量のサブリソース
- 「500ms以下で返せた!やったー?」
- ↑TTFB後のサブリソース読み込みのこと考えてる?
1.form系問題への取り組み
js object → DOMへの反映
バイディングまじでありがたい 😍
<script>
new Vue({
data: {
host: "....",
allowIPs: [],
},
computed: {
validationErrors() { ... },
}
});
</script>
<template>
<input :value="host" />
<div v-if="hasValidationError" class="error" >
{{ validationErrors["host"] }}
</div>
<ul>
<li v-for="(ip, i) in allowIPs" :key="i" >
<input :value="ip" />
<div v-if="hasValidationError" class="errors">
{{ validationErrors["allowIPs"][i] }}
</div>
</li>
</ul>
</template>
サーバーへのpush
- axiosをつかっています
- CSRFトークンはrailsのviewsでhtmlに埋め込んでおいたのを使う
axios.defaults.headers.common["X-CSRF-Token"] = document.querySelector(...);
axios.post('/foo/bar', {
host: this.host,
allow_ips: this.allowIPs
}
.then(response => {...})
.catch(err=> {...});
- ActiveRecordやActiveModelのValidationErrorも整形してレスポンスとして返す
メリット:
ユーザーの入力はブラウザに残るので
バリデーションエラーの際、サーバー側で画面再構築しなくてよい😉
まずここまでが第一段階でした
この時点での状況
- npmやwebpack使わない
- vueを使いたいページで CDNからvueを読み込み(scriptタグ)
- 適当なところにidをつけてvue componentをマウント
お手軽!
そしてbinding最高!
- 自分でDOMごりごり構築しなくてよくなった
- api で投げるときもDOMではなくjs objectの値をつかえばok!
この段階のDXへの不満
- unit testできない
- VueのSFCが使えない
- scoped cssがつかえない
- ページごとに使うVue/Axiosのバージョンがばらばらになりやすい..
- pug/stylus使いたい
加えて...
サブリソース読み込み開始時間をもっとはやくしたい
2. 大量/大容量サブリソース読み込みへの対処
課題をもういちど
あるページのサーバーサイドのレスポンスが500ms以内だとして...
- そのあとに, CSS, js, img, web-font,などの読み込みが始まる
- FontAwesomeとか普通に読み込むとかなり大きい
そもそも実際には...
-
雑につくって放置してデータが増えるとサーバーサイドレスポンスが500msを超える.
(雑に機能追加して気づかぬうちにN+1など) -
そのあとにサブリソース読み込みが行われる
↓ということで
一旦htmlを返したあとに、apiから必要コンテンツを取得する方式へ
具体的にどういう構成にしたか
この段階で使いはじめたフロントエンドエコシステム
- vue cli v3 および vue-cli-service
- vue-router
- webpack
- webpack-dev-server
- webpack-manifest-plugin
- html-webpack-plugin
※ vue cli v3のvue-cli-serviceを利用したいためwebpackerは利用せず
サーバーサイド(rails)
- 既存のページはそのまま
- 新規 layoutを作成して 改善対象ページだけ新規layoutを使う
- 外枠のみrailsで返す
- メインコンテンツはapiで渡す
- またはgonで渡す
※認証動作はrailsで行い、認可された後のページを今回の改善対象としています
サーバーサイド(rails)
重要だったポイント:
- webpackでビルドすると複数のファイルが出来上がる
- ※設定によっていろいろ
- 今回の設定でビルドされたファイル名にはhashがついている
- rails側のlayout内で必要なファイルのビルド後のURLを出力する必要がある。
どうするか
案1: webpack-manifest-plugin
もとのファイル名 -> ビルド後のファイル名(フルパス)のmappingをjson出力してくれる
案2: html-webpack-plugn
必要なjs,cssなどを読み込むlinkタグ,scriptタグが含まれたhtmlを生成してくれる
選ばれたのは
html-webpack-plugn
- rails起動時にhtml-webpack-pluginの出力するhtmlを読み込む。
-
<head>
に配置するlink/scriptタグと、 -
<body>
の最下部に配置するlink/scriptタグがある。
-
- webpack.rbというクラスを用意しクラスインスタンス変数内に保存
-
script
タグlink
タグなどをlayoutファイル内の適切な位置に出力
ただし, やっていることがかなりざつなので
できればwebpack manifest pluginを使う方式にしたい
😅😅😅
ブラウザ側の動き
- vue-routerでパスに対して特定のVue Componentをマウント
- dynamic import をつかって必要なjsのみ読み込む
- componentマウント時にメインコンテンツをrailsサーバーから取得
-
旧layout <=> 新layout
のページ遷移では通常のサーバーサイドレンダリング -
新layout <=> 新layout
のページ遷移ではcompnentの差し替えだけ行う(SPA的動き)
デプロイ時
$ npm run build (or yarn build)
$ RAILS_ENV=production rails assets:precompile
$ RAILS_ENV=production rails s
ずるいけどBefore/After
Before
After
まとめ
★ 最近もとめられるUXを満たすためには、
サーバーサイドエコシステムだけだとかなり労力がいる。
★ 先に画面返して、重い処理(DBアクセス)は必要なタイミングで呼び出す方が良いっすよね
結果、webpackはじめフロントエンドエコシステムにどっぷり使うことになるかとは思うんですが
補足: 開発時どうやっているか
起動するプロセスとアクセス先
- rails + webpack-dev-serverの2つを起動
- 全部入りdocker-compose.yamlも用意している
- 画面動作確認は webpack-dev-server側へアクセス
- webpack-dev-serverの設定でrails側APIに対するリクエストはrailsへプロキシ