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.

Nuxt なしで Vue App 作る時に乗り越えるべき5つの壁

48,458 views

Published on

Vue.js Tokyo v-meetup #5 LT の資料です
https://vuejs-meetup.connpass.com/event/65442/

Published in: Engineering
  • Be the first to comment

Nuxt なしで Vue App 作る時に乗り越えるべき5つの壁

  1. 1. Vue.jsTokyov‑meetup#5
  2. 2.
  3. 3. Q.秋といえば?
  4. 4. A.読書の秋
  5. 5. Q.Vue.jsで作られたSPASSRな 電子書籍(マンガ)配信サービスは?
  6. 6. A.マンガZERO
  7. 7. Nuxt無しで作ってしまった 恨み辛み妬み
  8. 8. Vue.jsTokyov‑meetup#5 Nuxt less SPA SSR で乗り越えるべきnの壁Nuxt less SPA SSR で乗り越えるべきnの壁
  9. 9. const n = 5;
  10. 10. 構成構成 SPAofVue.js+Vuex SSRbyExpress DeviceswitchingbetweenPC/SP BackendasAPIbyGo
  11. 11. vue‑hackernews‑2.0頼み
  12. 12. 1の壁: メタタグ1の壁: メタタグ SSR対応 Fetchの結果から動的に扱えること 手間がかからないこと
  13. 13. declandewet/vue‑metadeclandewet/vue‑meta メタタグ吐く SSR対応 公式ドキュメントにも載ってる
  14. 14. export default { metaInfo() { let metaBase = { title: this.title, meta: [ { vmid: 'og:title', property: 'og:title', content: this.title }, { vmid: 'description', name: 'description', content: this.description }, ... ], link: [ { vmid: 'canonical', rel: 'canonical', href: this.canonical } ] }; if (this.prev) { metaBase.link.push({ rel: 'prev', href: 'https://manga-zero.coroco3.com' + this.prev }); }
  15. 15. // server.js .on('beforeStart', () => { const { title, link, meta } = context.meta.inject(); context.head = (context.head || '') + title.text() + meta.text() + link })
  16. 16. Nuxtにも入ってる
  17. 17. 2の壁: Google Analytics2の壁: Google Analytics 非SPAのPVと同じになること 手間がかからないこと
  18. 18. MatteoGabriele/vue‑analyticsMatteoGabriele/vue‑analytics SSR対応 vue‑routerと同期し自動でPVを送ってくれる
  19. 19. // router.js Vue.use(Router); Vue.use(Meta); if (process.env.VUE_ENV === 'client') { Vue.use(VueAnalytics, { id: 'UA-xxxxxxxx-xx', router, ready() { const ga = window.ga || {}; ga('require', 'linkid'); ga('set', 'dimension1', val); } }); } export default router;
  20. 20. イベントとか送るのもちょっと楽 this.$ga.event('グローバル', 'メニュータップ', '', 0, { nonInteraction: true });
  21. 21. Appanalyticsを使うならScreamZ/vue‑analytics
  22. 22. あるのかよ
  23. 23. 3の壁: デバイス切り替え3の壁: デバイス切り替え 同一Expressから配信したい Storeとか共通で使いたい 手間がかからないこと デバイス切り替ェ
  24. 24. WebpackのMultiCompileでいける...
  25. 25. デバイス切り替ェ // webpack.config.server.js { ... plugin: [ ... new VueSSRServerPlugin({ filename: 'sp/vue-ssr-server-bundle.json' }) ] ... }, { ... plugins: [ ... new VueSSRServerPlugin({ filename: 'pc/vue-ssr-server-bundle.json' }) ] ... }
  26. 26. デバイス切り替ェ const template = { pc: fs.readFileSync(resolve('./src/app/templates/pc.html'), 'utf-8'), sp: fs.readFileSync(resolve('./src/app/templates/sp.html'), 'utf-8') };
  27. 27. デバイス切り替ェ // srever.js if (!isDevelop) { const bundle = { sp: require('./dist/sp/vue-ssr-server-bundle.json'), pc: require('./dist/pc/vue-ssr-server-bundle.json') }; const clientManifest = { sp: require('./dist/sp/vue-ssr-client-manifest.json'), pc: require('./dist/pc/vue-ssr-client-manifest.json') }; renderer = { sp: createRenderer(bundle.sp, { clientManifest: clientManifest.sp }, pc: createRenderer(bundle.pc, { clientManifest: clientManifest.pc }, }; } else { readyPromise = setupDevServer(app, (bundle, options) => { renderer = { sp: createRenderer(bundle.sp, options, template.sp), pc: createRenderer(bundle.pc, options, template.pc) }; }); }
  28. 28. webpack.config.client.js も同様 dev‑middlewareとhot‑middlewareも同様
  29. 29. デバイス切り替ェ // server.js function render(req, res) { ... const device = (req.useragent.isMobile) ? 'sp' : 'pc'; const context = { url: req.url, device }; renderer[device].renderToStream(context) ... }
  30. 30. 流石に無いよな... ?
  31. 31. 4の壁: 他民族4の壁: 他民族 国内ではまだ若干弱め 1.0の時のイケてない印象 手間がかからないこと
  32. 32. ☝ Angularぽく書けるよ ☝
  33. 33. vue‑class‑componentvue‑class‑component TSでいい感じに書ける ESでも使える Angularぽく書けるイメージ
  34. 34. import Vue from 'vue'; import Component from 'vue-class-component'; @Component({ name: 'component-hoge', metaInfo: { ... }, proprs: { ... } }) export default class ComponentHoge extends Vue { // initial data msg = 123; // lifecycle hook mounted () { this.greet(); } // computed get computedMsg () { return 'computed ' + this.msg; } // method greet () { alert('greeting: ' + this.msg); } }
  35. 35. 他民族においても他民族においても 直帰率の低下⬆⬆⬆ リテンションの底上げ⬆⬆⬆ 布教にも貢献
  36. 36. 親クラス作って継承したり
  37. 37. vue‑class‑component/createDecoratorvue‑class‑component/createDecorator デコレータを作れる
  38. 38. 例えばPC/SP間で 殆ど同じだけど微妙に異なる(Propsとか) コンポーネントを沢山作る時
  39. 39. 同じ:Template,Style,Props 違う:Method,Computed
  40. 40. import Vue from 'vue'; import createDecorator from 'vue-class-component'; function ComponentTopDefault() { return createDecorator((options) => { options = object.assign({}, { name: 'view-top', props: { hoge: { ... } } }, options); }); } @Component() @ComponentTopDefault() export default class ViewTop extends Vue {}
  41. 41. metaInfoとかproprsとか Mixinはちょっと違うなってものを createDecratorで定義した
  42. 42. v5.0.2でこの使い方はできなくなった
  43. 43. 次の日にはPRできてた
  44. 44. Nuxt関係ない
  45. 45. 5の壁: HTTP Status5の壁: HTTP Status 状況に合わせたHTTPステータスが打てる 404デザインがVueでレンダリングできる 手間がかからない
  46. 46. vue‑hackernews‑2.0の404は
  47. 47. さすがにこれはない
  48. 48. のでやってみた
  49. 49. Router にマッチしなかった時Router にマッチしなかった時 /unkounko
  50. 50. Router にマッチしなかった時Router にマッチしなかった時 /unkounko // router [ ... { path: '*', name: 'not-found', component: notFound } ] // server.js .once('data', () => { if (context.state.route.name === 'not-found') res.status(404); })
  51. 51. API のステータスからAPI のステータスから /product/999999 // store/action.js export const FETCH_ITEMS = ({ commit }, { path, key, type }) => { return fetchItem(path, key).then((result) => { if (result.status === 200) { ... } else if (result.mainQuery) { /* ↑ メインクエリに値する API のレスポンスが200 じゃない時 ↑ */ /* ↓ state に error コードをセットする ↓ */ commit(types.SET_STATUS, { key: 'error', value: result.status }); } else { ... } }).catch((error) => { console.error('catch FETCH_ITEMS', error); }); };
  52. 52. API のステータスからAPI のステータスから // server.js .once('data', () => { const termState = (context.state.error && context.state.error !== 0); if (context.state.error !== 0) { res.status(context.state.error); } })
  53. 53. Nuxtだと
  54. 54. でいいらしい async fetch({ store, route, app, error }) { let url = `/api/${route.params.id}` let data = await app.$axios.$get(url) if (data.items.length === 0) { // there's no data this should be a 404 // No! there is no Content so throw `204 No Content` response: // https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_Succes error({statusCode: 204, message: "The server successfully processed t } store.commit('item', data.Items[0]) }
  55. 55. 結論結論 色々頑張ったけど... もしかしてNuxt使えば楽なの ?
  56. 56. 最後に最後に
  57. 57. お前誰?
  58. 58. Yutaro Miyazaki (@vwxyutarooo)Yutaro Miyazaki (@vwxyutarooo) ニート↓ フリーのWeb屋↓ アプリ屋のフロントエンド Dvorak+Vimに悩んでいる
  59. 59. ありがとうございましたありがとうございました

×