vue.js 2.0 で自社プロダクトを spa + ssr 化した話

64
JavaScript in mixi Vue.js 2.0 SPA + SSR

Upload: yutaro-miyazaki

Post on 21-Jan-2018

4.219 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

初夏の JavaScript 祭 in mixi

Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Page 2: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Yutaro Miyazaki (@vwxyutarooo)

ニート ↓

フリーランス (Web 制作) ↓

アプリ屋の Web (フロントエンド)

Page 3: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話
Page 4: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

今日話すこと

導入してみてどうだった? ってとこ

地味に困ったこと

Page 5: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

今日話さないこと

Vue.js SSR のしくみ、ロジックなど

他フレームワークとの比較

Page 6: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

サービスの概要

マンガ無料配信サービス

アプリを主軸に展開しているサービス

Web でもコンテンツを活かそう

Page 7: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話
Page 8: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

リニューアルと導入の背景

Web 経験者無しで v1 を作ってしまった

イケてない

Page 9: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Web でももっとこう

アプリっぽい体験できないですかね

Page 10: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

v1: Riot でページ毎にマウント

→ SPA

クライアントレンダリング

→ SSR (SEO ほんとにいいのか?)

Vue.js の評判がいい

どっかの調査で満足度1位

Page 11: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

構成 (全体)

Page 12: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

構成 (Web)

Page 13: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

前提知識

Page 14: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

vuejs/vue‑hackernews‑2.0

公式が作る SPA + SSR プロジェクト

https://github.com/vuejs/vue‑hackernews‑2.0

Page 15: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Webpack の例

https://ssr.vuejs.org

Page 16: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

地味に悩んだポイント集

Page 17: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

SSR は誰がやる?

404 ハンドリング

デバイス切り替えってどうする?

共通処理どうする?

メタタグの管理めんどいやりたくない

Analytics どうしよう?

広告

メモリリーク

Page 18: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Q: SSR は誰がやる?

Page 19: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

バックエンドからも JS が起動できる

go

go‑duktape

goja + goja‑node

Page 20: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

素直に Express から起動することに

Page 21: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Q: 404 ハンドリング

Page 22: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

vue‑hackernews 2 では

ルータ設定にマッチするページが見つからなければ

Express が 404 返すようになってる

Page 23: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

if (err && err.code === 404) { res.status(404).end('404 | Page Not Found') }

Page 24: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

404 ページのデザイン欲しい

// router.js [ { path: '/', name: 'top', component: top }, ... { path: '*', name: 'not-found', component: notFound } ]

Page 25: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

// server.js const termRoute = (context.state.route.name === 'not-found');

if (termRoute) res.status(404);

Page 26: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

ルータにマッチするけど 404 の時は?

// router.js { path: '/comics/tag/:id', name: 'tag-archive', component: tagArchive },

Page 27: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

API リクエスト時に、メインクエリを設定

// preFetch const options = { isMainQuery: (key === mainQueryKey) }

Page 28: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

メインクエリの API レスポンスが

200 じゃなかったら state にエラーをセット

// action.js if (result.status === 200) { commit(mutation, { key, result: result.data }); } else if (options.isMainQuery) { commit(types.SET_STATUS, { key: type, value: {} }); commit(types.SET_STATUS, { key: 'error', value: result.status // 404 }); }

Page 29: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Express サーバで、コンテキストを通じて

state のエラーからステータスを打つ

// server.js const termState = (context.state.error); // 404 | 50x

if (termState) res.status(context.state.error);

Page 30: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

ちょっとイケてないけど

対象 View コンポーネント内で not found を表示させた

<div :key="`tag-archives-${id}-${currentPage}`"> <div v-if="isLoading" class="l-root"> <screen-spinner></screen-spinner> </div> <content-not-found v-else-if="status === 404"></content-not-found> <template v-else="v-else"> ... </template> </div>

Page 31: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Q: デバイス切り替えってどうする?

Page 32: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

PC/SP 用エントリーポイントをそれぞれ用意

// webpack.config.client.js entry: { 'polyfills': [path.join(..., 'app/entry/polyfills.js')], 'vendor': [path.join(..., 'app/entry/vendor.js')], 'app.pc': [path.join(..., 'app/entry/pc/client-entry.js')], 'app.sp': [path.join(..., 'app/entry/sp/client-entry.js')] },

// webpack.config.server.js entry: { 'server-bundle.pc': path.join(..., 'app/entry/pc/server-entry.js'), 'server-bundle.sp': path.join(..., 'app/entry/sp/server-entry.js') }

Page 33: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

テンプレートも2つ

// webpack.config.client.js new HTMLPlugin({ template: path.join(..., 'templates/pc.html'), filename: 'index.pc.html', excludeChunks: ['app.sp'] }), new HTMLPlugin({ template: path.join(..., 'templates/sp.html'), filename: 'index.sp.html', excludeChunks: ['app.pc'] }),

Page 34: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

createRenderer でレンダラを2つ作成

// server.js const bundle = { pc: fs.readFileSync(resolve('./dist/js/server-bundle.pc.js'), 'utf-8'), sp: fs.readFileSync(resolve('./dist/js/server-bundle.sp.js'), 'utf-8') } const template = { pc: fs.readFileSync(resolve('./dist/index.pc.html'), 'utf-8'), sp: fs.readFileSync(resolve('./dist/index.sp.html'), 'utf-8') } renderer = { pc: createRenderer(bundle.pc, template.pc), sp: createRenderer(bundle.sp, template.sp) };

Page 35: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Express で UA 判定して起動するレンダラを切り替え

// server.js const useragent = require('express-useragent'); ... app.use(useragent.express()); app.get('*', (req, res) => { ... const device = (req.useragent.isMobile) ? 'sp' : 'pc'; ... renderer[device].renderToStream(context)... }

Page 36: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

2.3.0 から createRenderer

にバンドル突っ込むのは非推奨に...

Page 37: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

別の方法を考え中

Page 38: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Server バンドルはエントリーポイント分けず

context にデバイス情報渡して切り替えるのもありか?

Page 39: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Q: 共通処理どうする?

Page 40: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

vuejs/vue-class-component

もともと TypeScript で書けるようにするため

Class でコンポーネントを定義できる

継承は非対応だが、Decorator と組み合わせる

Page 41: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

import Vue from 'vue' import Component from 'vue-class-component'

@Component({ props: { propMessage: String } }) export default class App extends Vue { // initial data msg = 123

// use prop values for initial data helloMsg = 'Hello, ' + this.propMessage

// lifecycle hook mounted () { this.greet() } ... }

Page 42: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

import { createDecorator } from 'vue-class-component';

export const Options = createDecorator((options) => { Object.assign(options, { ... watch: { // call again the method if the route changes '$route': 'routeUpdated' } }; });

Page 43: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

使い方は君しだい!

Page 44: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Q: メタタグの管理めんどいやりたくない

Page 45: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

declandewet/vue-meta

Page 46: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

export default { name: 'App', metaInfo: { title: METAINFO.title, titleTemplate: METAINFO.titleTemplate, meta: [ { vmid: 'og:title', name: 'og:title', content: METAINFO.title }, { vmid: 'description', name: 'description', content: METAINFO.description } ... ] } }

Page 47: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

コンポーネントの深いやつが勝つ

全コンポーネント検査してるからパフォーマンスは疑問

SSR 対応 (Vue の公式にも例あり)

Page 48: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Q: Analytics どうしよう?

Page 49: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Web Analytics: MatteoGabriele/vue-analytics

App Analytics: ScreamZ/vue-analytics

Page 50: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

router とくっつけて自動で PV | SV 送れる

Page 51: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Q: 広告

Page 52: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Google Adsense は SPA 非対応

Page 53: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

ページのリフレッシュ無しに広告を打ち直すことは禁止

imp が絶望的

Google Adsense 以外の SSP 等広告運用が必須

Page 54: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Q: メモリリーク

Page 55: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

起こしてた

Page 56: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

1日でメモリを食い尽くし

ガベージコレクション走りまくり、CPU 回りまくり

API キャッシュ周りが原因

最新の Vuehackernews では直ってる

Page 57: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

所感

Page 58: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

SSR の実装はそれほど大変ではないのかも

普通に SPA を作る + ちょっとの手間でいい

メタタグとかアプリケーション側で扱いたいからついでに

SSR しちゃってたり

Page 59: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

まあまあ安定稼働もする

CPU はそこそこ回るためページキャッシュを併用

コンポーネントキャッシュは考えて設計すべし

状態変化によるケースや slot が多いと効果的ではないかも?

Page 60: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

KPI 的には

PV/セッション上がり

滞在時間は平行

回遊しやすくなってるって思いたい

なんか下がることはなかった

Page 61: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

まとめ

あり

SEO 対策としてだけやるなら要らない

メタタグさえサーバ側で作れていれば

堅いこと言わずに作ってみようぜ

Page 62: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

Vue.js 楽しいな! おい!!

Page 63: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話
Page 64: Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話

ありがとうございました!