Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

19,771 views

Published on

2017.05.27 の 初夏の JavaScript 祭 in mixi でお話した時のスライドです。

Published in: Technology
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

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

  1. 1. 初夏のJavaScript 祭in mixi Vue.js 2.0 で自社プロダクトを SPA + SSR 化した話
  2. 2. Yutaro Miyazaki (@vwxyutarooo) ニート↓ フリーランス(Web制作)↓ アプリ屋のWeb(フロントエンド)
  3. 3. 今日話すこと 導入してみてどうだった?ってとこ 地味に困ったこと
  4. 4. 今日話さないこと Vue.jsSSRのしくみ、ロジックなど 他フレームワークとの比較
  5. 5. サービスの概要 マンガ無料配信サービス アプリを主軸に展開しているサービス Webでもコンテンツを活かそう
  6. 6. リニューアルと導入の背景 Web経験者無しでv1を作ってしまった イケてない
  7. 7. Webでももっとこう アプリっぽい体験できないですかね
  8. 8. v1:Riotでページ毎にマウント →SPA クライアントレンダリング →SSR(SEOほんとにいいのか?) Vue.jsの評判がいい どっかの調査で満足度1位
  9. 9. 構成 (全体)
  10. 10. 構成 (Web)
  11. 11. 前提知識
  12. 12. vuejs/vue‑hackernews‑2.0 公式が作るSPA+SSRプロジェクト https://github.com/vuejs/vue‑hackernews‑2.0
  13. 13. Webpack の例 https://ssr.vuejs.org
  14. 14. 地味に悩んだポイント集
  15. 15. SSRは誰がやる? 404ハンドリング デバイス切り替えってどうする? 共通処理どうする? メタタグの管理めんどいやりたくない Analyticsどうしよう? 広告 メモリリーク
  16. 16. Q: SSR は誰がやる?
  17. 17. バックエンドからもJSが起動できる go go‑duktape goja+goja‑node
  18. 18. 素直にExpressから起動することに
  19. 19. Q: 404 ハンドリング
  20. 20. vue‑hackernews2では ルータ設定にマッチするページが見つからなければ Expressが404返すようになってる
  21. 21. if (err && err.code === 404) { res.status(404).end('404 | Page Not Found') }
  22. 22. 404ページのデザイン欲しい // router.js [ { path: '/', name: 'top', component: top }, ... { path: '*', name: 'not-found', component: notFound } ]
  23. 23. // server.js const termRoute = (context.state.route.name === 'not-found'); if (termRoute) res.status(404);
  24. 24. ルータにマッチするけど404の時は? // router.js { path: '/comics/tag/:id', name: 'tag-archive', component: tagArchive },
  25. 25. APIリクエスト時に、メインクエリを設定 // preFetch const options = { isMainQuery: (key === mainQueryKey) }
  26. 26. メインクエリの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 }); }
  27. 27. Expressサーバで、コンテキストを通じて stateのエラーからステータスを打つ // server.js const termState = (context.state.error); // 404 | 50x if (termState) res.status(context.state.error);
  28. 28. ちょっとイケてないけど 対象Viewコンポーネント内でnotfoundを表示させた <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>
  29. 29. Q: デバイス切り替えってどうする?
  30. 30. 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') }
  31. 31. テンプレートも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'] }),
  32. 32. 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) };
  33. 33. 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)... }
  34. 34. 2.3.0からcreateRenderer にバンドル突っ込むのは非推奨に...
  35. 35. 別の方法を考え中
  36. 36. Serverバンドルはエントリーポイント分けず contextにデバイス情報渡して切り替えるのもありか?
  37. 37. Q: 共通処理どうする?
  38. 38. vuejs/vue-class-component もともとTypeScriptで書けるようにするため Classでコンポーネントを定義できる 継承は非対応だが、Decoratorと組み合わせる
  39. 39. 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() } ... }
  40. 40. 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' } }; });
  41. 41. 使い方は君しだい!
  42. 42. Q: メタタグの管理めんどいやりたくない
  43. 43. declandewet/vue-meta
  44. 44. 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 } ... ] } }
  45. 45. コンポーネントの深いやつが勝つ 全コンポーネント検査してるからパフォーマンスは疑問 SSR対応(Vueの公式にも例あり)
  46. 46. Q: Analytics どうしよう?
  47. 47. WebAnalytics:MatteoGabriele/vue-analytics AppAnalytics:ScreamZ/vue-analytics
  48. 48. routerとくっつけて自動でPV|SV送れる
  49. 49. Q: 広告
  50. 50. GoogleAdsenseはSPA非対応
  51. 51. ページのリフレッシュ無しに広告を打ち直すことは禁止 impが絶望的 GoogleAdsense以外のSSP等広告運用が必須
  52. 52. Q: メモリリーク
  53. 53. 起こしてた
  54. 54. 1日でメモリを食い尽くし ガベージコレクション走りまくり、CPU回りまくり APIキャッシュ周りが原因 最新のVuehackernewsでは直ってる
  55. 55. 所感
  56. 56. SSR の実装はそれほど大変ではないのかも 普通にSPAを作る+ちょっとの手間でいい メタタグとかアプリケーション側で扱いたいからついでに SSRしちゃってたり
  57. 57. まあまあ安定稼働もする CPUはそこそこ回るためページキャッシュを併用 コンポーネントキャッシュは考えて設計すべし 状態変化によるケースやslotが多いと効果的ではないかも?
  58. 58. KPI 的には PV/セッション上がり 滞在時間は平行 回遊しやすくなってるって思いたい なんか下がることはなかった
  59. 59. まとめ あり SEO対策としてだけやるなら要らない メタタグさえサーバ側で作れていれば 堅いこと言わずに作ってみようぜ
  60. 60. Vue.js 楽しいな! おい!!
  61. 61. ありがとうございました!

×