【Rails】webpackerの構成を✅
クリスマスイブだからいいよね、年末だからいいよね、が積み重なってデブにならないか危惧している23期中野昴こと「クリスマスプレゼントをまだ買えていない」です。
今回は、jsがデバイスごとに挙動が違うというissuehttps://github.com/subaru-hello/Zeroken/issues/107の解決を試みた話になります。
まず「 javascript iphone 動かない」や「javascript 本番環境 キャッシュ」などでググったところ、webpackerに原因があるのでは?という記事が多く掲載されていました。
webpackerを理解するために取り急ぎwebpacker.ymlを学習していきたいと思います。
webpacker.ymlとは
メインディレクトリconfig配下にあるwebpacker gemを管理するymlファイル(ファイルの書き方ルールのひとつ)。
Webpackと呼ばれる、「それぞれが依存関係にあるjs,scss,imageなどのファイルををまとめて静的アセットにバンドルするモジュールバンドラー」をRuby on Railsで使うために作られたのがwebpacker。webpackerの構造を管理するのがwebpacker.yml
下記のような構造で書かれている
app/javascript:
# Only Webpack entry files here
└── application.js
└── application.css
└── src:
│ └── my_component.js
└── stylesheets:
│ └── my_styles.css
└── images:
└── logo.svg
production,development,test環境のそれぞれをconfig/webpacker.ymlひとつが下記のように管理している。
config
│ ...
├── webpack
│ ├── development.js
│ ├── environment.js
│ ├── production.js
│ └── test.js
└── webpacker.yml
そのため、ローカルで動いたjavascriptやcssが本番では機能しない、、といった不具合の原因はここにある可能性が高い。そう感じている。
webpacker.ymlの構造を紐解く
source_path: app/javascript
webpackによってバンドルされるソースファイルをまとめる場所。
ソースエントリーパスが配置されている。このjavascriptという記述はfrontendという名前に変えたりする事ができる。images,cssといった静的アセットを使わない場合にfrontendと名前を変更する事が多い。
source_entry_path: packs
application.js、いわゆるエントリーポイントをここに書く。
public_root_path: public
source_pathのファイルがバンドルされた結果が出力される場所。
public_output_path: packs
manifest.jsonが出力される場所。
cache_path: tmp/cache/webpacker
再コンパイルの処理速度を高めるために、キャッシュを保存しておく場所。
sorth_path内の.jsに変更が加えられた際に差分を更新する必要がある。この、差分のみ更新すればいいのか、それとも全て更新しなくてはならないのか、で処理速度が変わるわけで、「差分のみを更新する」ことを実現してくれるのが、cache_path
webpack_compile_output
compileされた結果を出力する場所
基本的にtrueにしておくのがベター。俺は絶対ミスしないという自信があるのであればfalseにしてもいい?
cassh_manifest
The
cache_manifest
setting simply tells Rails whether we want to maintain the parsed manifest in Ruby memory.
頻繁にソースコードを更新する場合、cash_manifestをfalseにしておくことがベターとされている。
extract_css
extract_css: false
is helpful for development; most applications will want to setextract_css: true
in production.
webpackerによってバンドルされたcssファイルをアウトプットする方法が二つあり、assets pipelineから取り除きたいcssファイルをapplication.cssとして切り出しておく方法と、ブラウザーがロードされたタイミングでjavascriptが動的にページへcssを挟み込む方法がある。
開発環境ではfalse,本番環境ではtrueにしておく事が好ましい。
compile
webpackがassetファイルをコンパイルするとき、
本番環境環境においてprecompileされて欲しい、もしくは開発環境でwebpack-dev-serverを実行して欲しい場合はfalse,
railsにshell comand実行してもらいたいときはtrueにしておく。
一言
webpacker.ymlを見れば「iphoneとmacでの動作が違う」問題が解消されるかと思ったんだけど分からずじまいだな、、
ps
技術面談をしてアドバイスをいただき、smoothscroll-polyfillをアプリに入れたら本issueは解決いたしました!明日はpolyfillについて記事を書きます。
https://rossta.net/blog/how-to-use-webpacker-yml.html
https://railsguides.jp/webpacker.html
https://railsguides.jp/webpacker.html#development環境でwebpackerを実行する
【javascript】デバック方法を✅
ゆるPF雑談会の心理的安全性の高さが心地よいのでまた開催して欲しいと考えている23期酒ケジュール作成中です。
ジョジョ立ち、ナポリピッザ、デスボイス、ウィスキー、母音。とりあえずteachable machineの有用性の高さに驚いています。
今回は、javascriptのデバック方法をチェックしていきたいと思います。
デバックとは
バグの箇所を突き止めるために行う行為。rubyでいうところのbinding.pry
Vueはライフサイクルフックを採用している関係上、どのタイミングでデータが読み込まれているのかによって期待する挙動を返さない事がある。
例えば以下のようなインスタンスを作成している場合、createdではundefinedになる一方で、mountedは<div id=”app”></div>を返す。
new Vue({
el: '#app',
data: {
},
created : function(){
console.log('created')
console.log(this.$el)
},
mounted : function(){
console.log('mounted')
console.log(this.$el)
}
})
createdの段階ではまだDOMが構築されていないため、this.$elにアクセスする事ができないから。
このように、エラーの場所を突き止める際に使う。
デバック方法
ブラウザ上のコンソールでデバックを行う方法と、ローカルで開発中のコードにdebuggerと追記してデバックをする方法の二通り存在する。
debuggerを使用する場合
調べたい関数の中にdebuggerを書き込む。
javascriptで繰り返し処理を行っている時。
実際に値を取り出すことができているのかな?と気になった時に使う。
真ん中にある再生ボタン的なものを押すと、for文が実行され、iが一つ増える。
変数に配列が入っていることが確認できる。
console.log
引数に入れた文字や変数を入れるとコンソールに表示される。
DBからデータをちゃんと取ってこれてるのか確認したい時に、下記のようにしてconsole.logの引数に入れる。
検証ツールのコンソール画面を見ると、this.authUser.dataに値が入っている事がわかる。
その他にもconsole系は用意されている
console.info
軽く指摘してくれる
メッセージタイプのログを出力でき、FirefoxとSafariの場合にはアイコンが付く。
ログレベルはChromeが「info」に、Firefoxが「情報」に、Safariが「ログ」にそれぞれ出力される。
console.warn
警告してくれる。
実際に使ってみる
コードに差し込む
実際に出力された結果
一言
エラーを突き止めるときに、「ここかな?」とピンポイントでdebuggerやconsole.logを差し込んで原因を究明できたら格好いいっすね〜〜
【javascript】スコープを✅
エラーを直してはエラーがでて、直して、の繰り返しなのでそろそろエラーのない画面を求めている23期中野昴 です。それにしても眠気がすごい。
今回は、javascriptのスコープに関して学習していきたいと思います。
スコープとは
スコープとは変数の名前や関数などの参照できる範囲を決めるもの。 スコープの中で定義された変数はスコープの内側でのみ参照でき、スコープの外側からは参照できない。
スコープの役割
変数名の競合を避ける
もしスコープがなければ、プログラム全体で使われるすべての変数に、一意な名前を付けて衝突を避けなければならなくなる。一方でJavaScriptにはスコープがあるため、その必要がない。
メモリの消費を避ける
JavaScriptには、使われなくなったメモリ領域を自動的に解放するガベージコレクションという仕組みがあるそう。これによって、無駄なメモリの消費を回避しる。
もしスコープがなければ、すべての変数がグローバルに属することになる。
そして、グローバルに属する変数はプログラムから参照され続けるため、ガベージコレクションされない。
つまり、ページを閉じるまでの間ずっと、不要なメモリ領域を確保し続けるという「メモリリーク」が起きてしまう。
実際にはスコープがあるため、関数の実行が終われば、そのスコープに属する変数は不要とみなされ、ガベージコレクションの対象となる。
スコープの種類
スコープは、グローバルスコープとローカルスコープの2種類が存在している。
さらに、ローカルスコープは、関数スコープとブロックスコープに分類できる。
├── グローバルスコープ
└── ローカルスコープ
├──関数スコープ
└──ブロックスコープ
グローバルスコープ
プログラムのトップレベルで宣言された変数はグローバル変数になり、プログラム全体のどこからでもアクセス可能なグローバルスコープを持つようになる。
var scope = 'global';
# トップレベルからのアクセス
console.log(scope); // -> global
# 関数内からのアクセス
(function () {
console.log(scope); // -> global
})();
ローカルスコープ
関数スコープ
関数内で宣言された変数は関数外からアクセスする事ができない。
function sake() {
const age = 20;
# sake関数のスコープ内から`age`は参照できる
console.log(age); // => 20
}
sake();
// fn関数のスコープ外から`age`は参照できないためエラー
console.log(age); // => ReferenceError: age is not defined
{}で囲まれた関数内の変数は関数外で呼び出すとエラーになる。
ブロックスコープ
{}で囲まれたスコープのこと。
ブロックないで定義した変数へ、スコープ外からアクセスする事ができない。
# ブロック内で定義した変数はスコープ内でのみ参照できる
{
const age = 20;
console.log(age); # => 20
}
# スコープの外から`x`を参照できないためエラー
console.log(age); // => ReferenceError: age is not defined
ループ文でブロックスコープを作成することもできる。
const array = [1, 2, 3, 4, 5];
# ループごとに新しいブロックスコープを作成する
for (let element of array) {
# forのブロックスコープの中でのみ`element`を参照できる
console.log(element);
}
# ループの外からはブロックスコープ内の変数は参照できない
console.log(element);
# => ReferenceError: element is not defined
宣言文とスコープ
関数の仮引数と
var
は関数スコープだけを生成し、let
とconst
は関数スコープとブロックスコープの両方を生成する。
letやconstを使わずに宣言文を作成すると自動的にグローバルスコープになってしまう。グローバルスコープを作りすぎるとグローバル汚染が起きてしまう
function fn() {
# 関数スコープの中で宣言文を付けずに変数宣言
scope = 'local';
}
fn();
# グローバルスコープとなり、関数スコープの外からアクセスできてしまう
console.log(scope); // -> local
グローバル汚染
.グローバル環境を時に意味のない変数で汚染すること。グローバル汚染が進むと下記のような弊害が起きる。
まとめ
- 関数やブロックはスコープを持つ
- スコープはネストできる
- もっとも外側にはグローバルスコープがある
- スコープチェーンは内側から外側のスコープへと順番に変数が定義されているか探す仕組みのこと
【Vue】Loading画面の実装方法を✅
脚に鉄アレイ入ってる?胸Bカップくらいある?と彼女に言われてすごく上機嫌な23期酒ケジュール作成中です。
進捗
多少の進捗あり!
SSLとは
WebブラウザとWebサーバー間においてデータのやりとりを暗号化させる技術。Secure Socket Layerの略。
クレジットカード情報や個人情報などの秘匿情報が外部に漏れたり、ハッキングされたりすることを防ぐ。
SSL対応のサイトには、webブラウザの左側に南京錠マークが出る。また、http://ではなく、https://になることも特徴とされている。
SSLの仕組み|情報セキュリティ関連の技術|基礎知識|国民のための情報セキュリティサイト
さて、今回はVue.jsを使ったアプリに「いい感じのloading中画面」とつける方法を学習していきたいと思います。
Loading画面とは
データを取ってくるまで表示させておく画面
サークルやボール、サイクロンなどの種類がある。決済中画面や電波の悪いときによく見る。
これって、画面がフリーズしてるの?それとも待ち時間なの?とユーザーを困惑させないための配慮として使われたりする。
$ yarn add vue-loading-template
main.js
import Vue from 'vue';
import App from '../App';
import VueLoading from 'vue-loading-template';
Vue.use(VueLoading /** options **/);
これだけでもう使用できてしまう。
次に、ローディングを付与させたい画面に下記コンポーネントを記述する。
<VueLoading
type="cylon"
color="#d9544e"
:size="{ width: '50px', height: '50px' }"
></VueLoading>
任意でカスタマイズする事ができる
type:
ローディングの種類を変更させる事ができる。
cyclon
<VueLoading
type="cylon"
color="#d9544e"
:size="{ width: '50px', height: '50px' }"
></VueLoading>
spiningDubbles
<VueLoading
type="spiningDubbles"
color="#d9544e"
:size="{ width: '50px', height: '50px' }"
></VueLoading>
spin
<VueLoading
type="spin"
color="#d9544e"
:size="{ width: '50px', height: '50px' }"
></VueLoading>
color:
任意の色にする事ができる
#000
<VueLoading
type="spin"
color="rgb(0,0,0)"
:size="{ width: '50px', height: '50px' }"
></VueLoading>
[![Image from Gyazo](<https://i.gyazo.com/22214020b89ad57f3929872315c6db83.gif>)](<https://gyazo.com/22214020b89ad57f3929872315c6db83>)
size:
ローディング中画面の大きさを変更する事ができる
<VueLoading
type="spin"
color="rgb(0,0,0)"
:size="{ width: '200px', height: '50px' }"
></VueLoading>
全体像
もしloadingCircleがtrueだった場合、VueLoadingが表示され、falseだった場合、酒ケジュールが表示されるようにしている。
frontend/components/result/ShowShucheduleModal.vue
<div class="text-center black--text outer-layer" style="white--text">
<div v-if="loadingCircle">
<VueLoading
type="cylon"
color="#d9544e"
:size="{ width: '50px', height: '50px' }"
></VueLoading>
</div>
<div v-else>
<img :src="drunknessEagerToImg" width="50" height="50" />
</div>
<span class="mb-6 text-center">への 酒ケジュール</span>
</div>
<br />
<div v-if="loadingCircle">
<VueLoading
type="spiningDubbles"
color="#d9544e"
:size="{ width: '200px', height: '50px' }"
></VueLoading>
</div>
<div v-else>
<v-row justify="center" align-content="center">
<v-col
v-for="data in alcoholItems"
:key="data.id"
class="justify-space-between outer-layer"
>
<v-icon>{{
data.alcohol_percentage === 0 ? 'mdi-cup' : 'mdi-glass-mug'
}}</v-icon>
<p>
{{ data.name }}
</p>
<p>{{ data.alcohol_percentage }}%</p>
<p>{{ data.alcohol_amount }}ml</p>
</v-col>
</v-row>
</div>
<template>
</template>
<script>
import { VueLoading } from 'vue-loading-template';
export default {
props: {
alcoholDatas: {
type: Array,
},
loadingCircle: {
type: Boolean,
},
},
components: {
VueLoading,
},
computed: {
alcoholItems() {
const targetAlcohols = (this.alcohols = this.alcoholDatas);
return targetAlcohols;
},
},
}
</script>
v-ifを使用し、loadingCircleがtrueの場合、VueLoadingが表示され、それ以外はfalseになるようにした。
loadingCircleがfalseになるタイミングは、親コンポーネントがapiを叩いてデータを取ってくることが成功したタイミングになっている。
frontend/pages/Result.vue
<template>
<div>
<ShowShucheduleModal
v-if="showShuchedule"
:alcoholDatas="alcoholContents"
:loadingCircle="loading"
:motivationImg="nextMotivationImg"
@twitterShare="snsUrl"
@closeModal="closeShucheduleModal"
/>
<div>
</div>
</template>
<script>
import { mapActions, mapGetters, mapMutations } from 'vuex';
export default {
components: {
ShowShucheduleModal,
},
data(){
loading: true,
},
created() {
axios
.get('/alcohols')
.then((alcoholResponse) => {
this.alcohols = alcoholResponse.data;
return this.alcohols;
})
.then((alcohols) => {
this.loading = false; ←ここでloadingをfalseにしている
const thisAnalyze = this.analyzes;
const targetValues = alcohols;
const analyzeShuchedule = thisAnalyze[thisAnalyze.length - 1]['shuchedule'];
const contentsOfTarget = Object.values(targetValues)[analyzeShuchedule];
this.alcoholOrders = contentsOfTarget;
this.alcoholContents = contentsOfTarget;
});
},
}
</script>
親子コンポーネントに分けているため、少々見づらいかもしれない。
ライフサイフルフックのcreatedメソッドの中にapiを叩く処理を記述することで、mountedに書いたときに比べてサーバーに対する負荷を軽減される。
非同期処理に順番をつける(Promise構文)axiosにより、alcoholsパスからalcoholを取得した後(.thenの中身)this.loadingがfalseになるように書いた。
このように記述することで、お酒の種類を取ってくるまではloadingをtrueにしておく事が可能になった。
完成図
<div v-if="loadingCircle">
<VueLoading
type="spiningDubbles"
color="#d9544e"
:size="{ width: '200px', height: '50px' }"
></VueLoading>
</div>
<div v-else>
<v-row justify="center" align-content="center">
<v-col
v-for="data in alcoholItems"
:key="data.id"
class="justify-space-between outer-layer"
>
<v-icon>{{
data.alcohol_percentage === 0 ? 'mdi-cup' : 'mdi-glass-mug'
}}</v-icon>
<p>
{{ data.name }}
</p>
<p>{{ data.alcohol_percentage }}%</p>
<p>{{ data.alcohol_amount }}ml</p>
</v-col>
</v-row>
</div>
【Vue】.syncを✅
分からない事を分かることで前に進むことってあるんだなぁと感じている23期酒ケジュール作成中です。
さて、今回は.sync修飾子に関して学習していきたいと思います。
.sync修飾子とは
vueのカスタムイベントの一つ。
親が子に渡した値Aを、子が編集し、編集後の値Aを子から親に返す。
.syncを使うことで、受け取った値Aを親は親は比較的少ない記述量で反映させることができる。
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
この書き方を、以下のようにまとめることができるところ。
:title.sync="doc.title"
具体的にどう使うか
下記のように、v-bindに対して使う。
<text-document
:title.sync="doc.title"
></text-document>
公式Docを見て学習していく。
子は、methodで下記のように記述することで、titleのアップデートを親に送ることができる。
this.$emit('update:title', newTitle)
$emitを使ってnewTitleをupdate:titleに反映させて親に渡している。親は$eventで受け取ることができる。
親は、下記のように$eventを記述することでupdate:titleを取得している。
<text-document
:title="doc.title"
@update:title="doc.title = $event"
></text-document>
このパターンを .sync
修飾子で短く書くことができる。
<text-document
:title.sync="doc.title"
></text-document>
要は、親⇨子⇨親でキャッチボールをする際
親⇨子⇨親 = .sync
親⇨子 = props
子⇨親 = $emit, $event
.sync = props + $emit + $emit
もっと分かりやすく
あります。
参考記事にあった、ユーザーのログインフォームを実装を通して理解を深めてみる。
front/pages/signup.vue
<template>
...
<!-- :name.sync 追加 -->
<user-form-name
:name.sync="params.user.name"
/>
<!-- :email.sync 追加 -->
<user-form-email
:email.sync="params.user.email"
/>
<!-- :password.sync 追加 -->
<user-form-password
:password.sync="params.user.password"
/>
export default {
layout: 'beforeLogin',
data () {
return {
isValid: false,
// 追加
params: { user: { name: '', email: '', password: '' } }
}
}
}
UserNameForm
<user-name-form
:name="params.user.name" # : はv-bindの省略形
@update:name="params.user.name = $event" # @はv-onの省略形
/>
- バインド
:name
で親 → 子へデータを送信し、 - 子でデータを編集。
- 子から編集後のデータを送信し、
- 親は
@update:name
で受け取り、"params.user.name = $event"
で値を代入する。
通常の書き方は上記のようになるが、.sync修飾子を使うと下記のように書くことができる。
<user-name-form
:name.sync="params.user.name"
/>
子⇨親の連動バインディングが.syncで省略されている。
@update:nameで子から編集後のデータを受け取って$eventで親の該当箇所を更新する、という処理が.syncで完結できている。
ちなみに、v-bind は「:」v-onは「@」v-slotは「#」という記述ができる。
front/components/user/userFormName.vue
<template>
<!-- v-model 追加 -->
<v-text-field
v-model="setName"
label="ユーザー名を入力"
placeholder="あなたの表示名"
outlined
/>
</template>
<script>
export default {
props: {
name: {
type: String,
default: ''
}
}
computed: {
setName: {
get () { return this.name },
set (newVal) { return this.$emit('update:name', newVal) }
}
}
}
</script>
- v-modelに入力されたStringの値は、setNameに保存される。
- setNameにはgetterとsetterの二つがある。
- getterを通して、prop(親⇨子の値を受け取ることができるプロパティ )のnameにアクセスすることができる。
- getterで取得したthis.nameは、v-modelに入力された値に更新される。
- 更新された値はnewValとしてsetterに入り、this.$emit('update:name', newVal)によって親に渡される。
まとめ
- .syncを使うことで、子から$emitされた値を親は簡潔な記述で受け取ることができる。
- v-bind は「:」v-onは「@」v-slotは「#」
- propは親⇨子の流れで値が移動する。
【Vue】ルートメタフィールドを✅
RUNTEQのインタビュー記事やTwitterでしか見たことのない偉大な卒業生たちと会話をする事ができてとても喜んでいること23期髪切って誰か分からないです。
村田講師に助けていただき、酒ケジュール取得のコンポーネントを外部に切り出すことに成功しました!javascriptの非同期処理、そしてwebpackerは謎が多い。。
さて、今回はルートメタフィールドについてを学習していきます。
ルートメタフィールドとは
この、metaによって囲まれた場所を指す。
// メタフィールド
meta: { requiresAuth: true }
ルーターの各レコードを、routes
設定の中の各ルートオブジェクトはルートレコードと呼ばれる。
どう使うのか
下記のようにルートレコードにmetaフィールドを作成する。
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/login',
component: UserLogin,
name: 'UserLogin',
},
{
path: '/profile',
component: UserProfile,
name: 'UserProfile',
meta: { requireAuth: true },
props: true,
},
router.js内にルートメタフィールドを作る。
公式に基づいて作成してみた。
下記コードは、store/users.jsにあるfetchAuthUserが成功した場合のみUserProfileにアクセスできるようにしている。
router.beforeEach((to, from, next) => {
if (to.matched.some((record) => record.meta.requireAuth)) { #...①
store.dispatch('users/fetchAuthUser').then((authUser) => {
if (!authUser) {
next({ name: 'UserLogin' });
} else {
next();
}
});
} else {
next();
}
});
①: ルートにマッチした全てのルートレコードは $route.matched
配列として $route
オブジェクト上でアクセスできるので、①では「record.meta.requireAuthが設定されたルートの場合、authUserしかアクセスできない」ようになっている。
つまり、ルートメタフィールドを使用すると、sorceryでいうところのrequire_loginを実装することができるわけだ。
他の用途
下記のようにナビゲーションガードと組み合わせることで、遷移先のタイトルを動的に表示させることもできる。
const router = new Router({
routes: [
{ path: '/analyze', component: Analyze, meta: { title: '診断ページ' } },
{ path: '/result', component: Result, meta: { title: '結果表示ページ' } }
]
})
router.afterEach((to, from) => {
if (to.meta && to.meta.title) {
document.title = to.meta.title
}
})
ルーターの機能によってタイトルを差し替えられるわけではないため、ルートメタフィールドで各ルートのタイトルを定義しておいて、ナビゲーションガードでページ遷移時にタイトルを変更している。
ページ遷移後にmetaのtitleを描画したいのでafterEachを使用している。
まとめ
- ルートメタフィールドを使用すると、sorceryでいうrequire_loginを実装することができる。
- ナビゲーションガードと組み合わせることで、content_forのようにタイトルをページごとに設定することができる。
【Vue】SPA SSG SSRを✅
どう考えてもフィナンシェとシュークリームの響きが可愛すぎるのでかわいすぎるので疲れてる時に小声で連呼して元気をもらうようにしている23期酒ケジュール作成中です。
今日の進捗
https://zeroken.herokuapp.com/
酒ケジュールを別コンポーネントに切り出してみたけど、値の伝播がうまく行く時と行かない時がある。
上手く行った時
上手くいかなかった時
親側で酒の情報を取ることはできているんだけどな、、
地味なエラーと戦い続けています。楽しいです。特にコンポーネントの切り出しが一番楽しいです。
さて、Vueを使っていながらSSG,SSR,SPAのことを知らないのはまずいかもなと思い始めたので3つに関して学習していきたいと思います。
SSRとは
サーバー側でjsとhtmlの処理を実行する技術。
メリット
- SEOが向上する。Googleのクローラはブラウザに表示される前のページを読んで「このサイトいいねぇ悪いねぇ」と判断するため、SSRによって完全に描画されたページをクローラにみてもらうことができるから。
- ユーザーエクスペリエンスが向上する。SPAは差分のみ描画するだけでいいとはいえ、リクエストとレスポンスを行う必要があり少々時間がかかってしまう。一方でSSRはサーバーサイドでまるっとページをレンダリングしてクライアントに描画するため、画像や動画のリロード時間が短縮される。
デメリット
- サーバー側の負担が大きくなる。クライアントとサーバーで負担を分担していたものをサーバーが全て担うようになるため負担が大きくなることは言うまでもなさそう。
Vue.js サーバサイドレンダリングガイド | Vue SSR ガイド
SPAとは
ページ遷移をすることなく、同一ページ内で遷移をするアプリケーションを指す。
ポイント
ブラウザ側でjsを処理している。
クライアントでjsを処理するので、リクエスト⇨レスポンスといった二段階の処理をしないといけない。SSRに比べて処理速度が遅くなる。
メリット
普通のhtml,css,javascriptで構成されているアプリと比べて処理速度が早い。
初回レンダリングでページを全て持ってきた後、2回目以降サーバーにデータをリクエストする際は、ページの差分のみレンダリングをするため処理が早くなる。
サーバーへのリクエストは、Ajax(非同期通信)で行い、レスポンスはjson形式で帰ってくる。
デメリット
jsのコードが増えると初回レンダリングの処理速度が遅くなる。
SSGとは
静的サイトを生成する技術。
SPAとSSRは、クライアントからの要求に応じてその場でページのレンダリングを行うことによる処理速度の低下が欠点だった。SSGはアプリをビルドするタイミングでHTML文書を作成するため、レンダリングの際に処理速度が低下する恐れはないそう。
また、サイトを生成した後にデータを改変することができないため、その特性を生かすことのできるページにSSGを適用させることが推奨されている。
Nextの公式サイトによると、以下のようなサイト形式の場合にSSGは効果を発揮するそう。
確かに、ユーザーの入力によって変形されるようなことのないページだからSSGが相性良さそうだ。
使われている技術を公式ページにある画像を使ってSSGで使われているpre-renderingの理解を深めてみる。
pre-rendering
- Javascript以外の要素がサーバーサイドで既に構築された静的ページをブラウザ(クライアント)にレンダリングしている
- ブラウザで受け取った静的ページにJavascriptで動きをつける。
non pre-rendering
- サーバー側ではまだHTML,CSS,Javascriptが使われたページが構築されていない
- クライアント側に渡されて初めてページが作られている。
このpre-renderingのすごいところが、先に述べた、アプリをビルドするタイミングでページを作成する点にある。下記のロジックでSSGはすごいと言える。
- ページを事前に用意
- 用意したページをCDNに入れておく
- ユーザーがそのページにアクセスする時には既にページが出来上がっているため、ローディング時間がない
- つまり、ユーザーの離脱する要因を一つ消すことができるため、UXの向上に繋がっていると言える。
一言
- 次にアプリを作るとしたら、フロント及びバックをNext.js、インフラはVercelの外部APIてんこもりアプリにする。