Vue.js
vue-cli

段階的にフロントエンド開発 with Vue

この投稿は Shinjuku.rb の発表資料です


@tomlla or @tomlla_92

singlion.jpg



これまでの @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は対応しない


個人的な課題感

サーバーサイドレンダリングだけだと...


  1. form系


    • 入力フォームでLive Validationしたいよね?

    • ネストした配列を含むフォームのバリデーションつらくない?

    • ユーザーの入力値, バリデーションエラーをサーバー側で再レンダリングするのつらくない?



  2. 大量/大容量のサブリソース


    • 「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を生成してくれる



選ばれたのは


petbottle_tea.png



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をマウント



  • 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

before.png



After

after.png


まとめ

★ 最近もとめられるUXを満たすためには、

サーバーサイドエコシステムだけだとかなり労力がいる。

★ 先に画面返して、重い処理(DBアクセス)は必要なタイミングで呼び出す方が良いっすよね

結果、webpackはじめフロントエンドエコシステムにどっぷり使うことになるかとは思うんですが



補足: 開発時どうやっているか



起動するプロセスとアクセス先


  • rails + webpack-dev-serverの2つを起動


    • 全部入りdocker-compose.yamlも用意している



  • 画面動作確認は webpack-dev-server側へアクセス

  • webpack-dev-serverの設定でrails側APIに対するリクエストはrailsへプロキシ