Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

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

59.491 visualizaciones

Publicado el

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

Publicado en: Ingeniería
  • Sé el primero en comentar

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. ありがとうございましたありがとうございました

×