Upgrade to Pro — share decks privately, control downloads, hide ads and more …

この夏モテたい人のための2stepで起動できるNuxtjs + Rails(ID/PW認証付き)サンプルを公開! / sample nuxtjs rails

この夏モテたい人のための2stepで起動できるNuxtjs + Rails(ID/PW認証付き)サンプルを公開! / sample nuxtjs rails

WEBエンジニア勉強会12 ( #WEM13) でのLT資料です。

『この夏モテたい人のための2stepで起動できるNuxtjs + Rails(ID/PW認証付き)サンプルを公開!』

https://web-engineer-meetup.connpass.com/event/128855/

A92d05c5da067dc28a32527430f762ca?s=128

WalkerSumida

May 24, 2019
Tweet

Transcript

  1. ͜ͷՆϞς͍ͨਓͷͨΊͷTUFQ ͰىಈͰ͖Δ/VYUKT 3BJMT *% 18ೝূ෇͖ αϯϓϧΛެ։ʂ גࣜձࣾϚπϦΧ/PUJBࣄۀ੹೚ऀ"ZVNV4VNJEB ˞͜ͷࢿྉ͸5XJUUFS8&.Ͱྲྀ͍ͯ͠·͢

  2. ࣗݾ঺հ

  3. גࣜձࣾ ϚπϦΧ 4FOTFT 4'"$3.  /PUJB &NBJM5SBDLJOH  3BJMTPS3FBDU ΤϯδχΞ

    ઈࢍืूதͰ͢ʂ
  4. w େֶߦ͖ͳ͕Β*5ϕϯνϟʔاۀͰ໿೥ؒ༗ঈΠϯλʔϯ w 1)1 .Z42- $BLF1)1 Y 8PSEQSFTT $ "41

    /&5 w ౦ࣳ৘ใγεςϜגࣜձࣾͰ೥ؒϔϧεέΞࣄۀʹैࣄ w 7#/&5 $ 0SBDMF%BUBCBTF w ̍ਓͰىۀ͠Α͏ͱࢼΈΔ͕ࣦഊ w 3VCZPO3BJMT 'VFM1)1 .Z42- "84 "OTJCMF w גࣜձࣾϚπϦΧʹΤϯδχΞೋਓ໨Ͱ+PJOͯ͠4FOTFTΛ։ൃ w 3VCZPO3BJMT "OHVMBS+4 .Z42- "84 ܦྺ
  5. w ̍ਓͰ/PUJBࣄۀΛ੒௕͍ͤͯ͞·͢ ݱࡏࣾ௒  w ΤϯδχΞͷྖҬ͸ɺϑϩϯτɺόοΫΤϯυɺ
 Πϯϑϥ w #J[ྖҬ͸ɺ1.ɺ$4ɺϚʔέɺӦۀ ࠷ۙͯ͠ͳ͍

     w ෭ۀͰ΋ͭͷ৽͍͠ࣄۀΛ্ཱͪ͛த ݱࡏ
  6. ͳͥ͜ͷλΠτϧʹ ͳͬͯ͠·ͬͨͷ͔

  7. None
  8. None
  9. /VYUKTͱ͸

  10. /VYUKTͱ͸

  11. αϯϓϧͷઆ໌

  12. ͳͥ͜ͷ044Λ࢝ΊΑ͏ͱ ࢥͬͨͷ͔

  13. ৽͍͍͍͠αʔϏεΛࢥ͍ͭ ͍ͨʂ

  14. ͱ͍͏ͱ͖ʹɺ

  15. ͙͢ʹ࣮૷Ͱ͖Δ؀ڥ͕ ͳ͍ʜ

  16. ͦ͜Ͱࢥߟ͕ࢭ·ͬͨΓɺ໘౗ ʹͳͬͯ΍ΊͨΓ͠ͳ͍Α͏ʹ

  17. ͜ͷ044Λ΍Ζ͏ͱࢥ͍ͬͯ ·͢ʂ

  18. w αϯϓϧ͸શͯެ։͍ͯ͠·͢ w /VYUKTIUUQTHJUIVCDPN XBMLFSTVNJEBOVYUKTTBNQMF w 3BJMTIUUQTHJUIVCDPN XBMLFSTVNJEBSBJMTBQJGPS OVYUKT w

    DMPOFͨ͠Βͭͱ΋ANBLF EPDLFS@VQAىಈͰ͖·͢ w OVYUKTͷىಈ͸গ͔͔࣌ؒ͠Γ· ͢ αϯϓϧ ˒ελʔ˒௖͚Δͱخ͍͠Ͱ͢ʂ ΍Δؾग़·͢ʂ
  19. # Nuxtjsͷ࡞੒ $ yarn create nuxt-app <my-project> # TypeScript༻ͷؔ࿈ϥΠϒϥϦ $

    yarn add -D @nuxt/typescript $ yarn add ts-node $ yarn add vue-property-decorator /VYUKTͷ࡞੒
  20. None
  21. ҎԼͷ3ͭͷϑΝΠϧΛ௥Ճ pages/posts/index.vue components/PostPreview.vue models/Post.ts ػೳ1PTUҰཡ ˡϦϑΝΫλϦϯάͯ͠ݱࡏ͸ 1PTU4IPXʹίϯϙʔωϯτ໊ม Θ͍ͬͯ·͢

  22. w ˢAQBHFTAσΟϨΫτϦ͸ಛघͳσΟϨΫτϦͰɺͦͷ഑Լ ʹ഑ஔ͞ΕͨύεͲ͓ΓͷϧʔςΟϯά͕ࣗಈతʹઃఆ͞Ε· ͢ w IUUQQPTUTJOEFY˞JOEFY͸লུ͞ΕΔ w IUUQQPTUTʹΞΫηε͢Δ͜ͱͰAQBHFT QPTUTJOEFYWVFAϑΝΠϧ͕ݺͼग़͞ΕΔ QBHFTQPTUTJOEFYWVF

  23. ಺෦తʹ͸ҎԼͷΑ͏ʹϧʔςΟϯά͕ు͖ग़͞Ε͍ͯΔ router: { routes: [ { path: '/posts', component: 'pages/posts.vue',

    children: [ { path: '', component: 'pages/posts/index.vue', name: 'posts' } ] } ] } QBHFTQPTUTJOEFYWVF
  24. <template> <div> <PostPreview v-for="post in posts" :key="post.id" :post="post" /> </div>

    </template> <script lang="ts"> import { Component, Vue } from 'vue-property-decorator' import Post from '~/models/Post' @Component({ components: { PostPreview: () => import('~/components/PostPreview.vue') }, asyncData() { return { // TODO: call api server posts: [ { id: 1, title: 'aaa', description: 'AAA' } ] } } }) export default class FeedPage extends Vue { posts: Post[] = [] } </script> QBHFTQPTUTJOEFYWVF
  25. export default Post { id: number title: string description: string

    } NPEFMT1PTUUT
  26. <template> <div> <h2>{{ post.title }}</h2> <p>{{ post.description }}</p> </div> </template>

    <script lang="ts"> import { Component, Vue, Prop } from 'vue-property-decorator' import Post from '~/models/Post' @Component export default class PostPreview extends Vue { @Prop({ type: Object, required: true }) post: Post } </script> DPNQPOFOUT1PTU1SFWJFXWVF
  27. ͜Μͳײ͡Ͱ 1PTUҰཡ͕ग़ དྷ্͕Γ·͢

  28. // plugins/axios.ts import axios from 'axios' export default axios.create({ baseURL:

    process.env.apiUrl }) // nuxt.config.js env: { apiUrl: process.env.API_URL || 'http://0.0.0.0:3000' }, BQJTFSWFSͱͷ௨৴෦෼
  29. BQJTFSWFSͱͷ௨৴෦෼

  30. ೝূํ๏

  31. ࠓճ࢖༻ͨ͠ͷ͸ 5PLFOCBTFೝূ

  32. w *%1BTTXPSEΛ4FSWFSʹૹ৴ w *%1BTTXPSE͕ਖ਼͚͠Ε͹ηο γϣϯ*%Λൃߦ͠ɺ$MJFOUଆʹฦ ٫ w $MJFOUଆͰηογϣϯ*%Λ$PPLJF ʹอଘ w

    Ҏ߱ͷϦΫΤετʹൃߦ͞Εͨ ηογϣϯ*%ΛҰॹʹૹ৴͢Δ͜ ͱͰ4FSWFSଆͰϢʔβΛࣝผ Ұൠతͳೝূ ᶃϩάΠϯ৘ใ *%EFNP!YYYDPN 18EFNPEFNP ᶄϩάΠϯ৘ใΛอଘ ᶅηογϣϯ*% LEGKJBEBKGKGBʜ ᶆ$PPLJFʹηογϣϯ*% Λอଘ
  33. w *%1BTTXPSEΛ4FSWFSʹૹ৴ w *%1BTTXPSE͕ਖ਼͚͠Ε͹5PLFOΛ ൃߦ͠ɺ$MJFOUଆʹฦ٫ w $MJFOUଆͰ5PLFOΛ$PPLJFʹอଘ w ࣍ͷϦΫΤετʹ5PLFOΛҰॹʹૹ ৴͢Δ͜ͱͰϢʔβΛࣝผɻͦͷϨ

    εϙϯεʹ৽ͨͳ5PLFO͕4FSWFSଆ ͔ΒૹΒΕͯ͘ΔͷͰɺͦͷ5PLFO Λอଘ͢Δ w 5PLFO͸࢖͍ࣺͯͳͷͰɺϦΫΤε τຖʹҧ͏ 5PLFOCBTFೝূ ˞EFWJTF@UPLFO@BVUIHFNΛ࢖༻
  34. ҰൠతͳೝূΑΓ΋ ηΩϡΞ

  35. ࠷ۙͷҰൠతͳೝূͰ΋ϥΠϒϥϦʹΑͬͯ͸ ηογϣϯ*%Λ࢖͍ࣺͯͷ஋ʹ͍ͯ͠Δ 5PLFOCBTFͱ΄ͱΜͲมΘΒͳ͍͔΋

  36. // plugins/axios.ts axiosInstance.interceptors.response.use((response) => { // See https://devise-token-auth.gitbook.io/devise-token-auth/conceptual setCookies(response.headers) return

    response }, (error) => { removeCookies() // FIXME: redirect without using window.location if(error.response.status === 401) { window.location.href = '/sign_in' } return Promise.reject(error) }) ೝূ෦෼ͷ࣮૷
  37. // plugins/axios.ts axiosInstance.interceptors.request.use((config) => { config.headers = setHeaders(config.headers) return config

    }, (error) => { return Promise.reject(error) }) ೝূ෦෼ͷ࣮૷
  38. ೝূ෦෼ͷ࣮૷

  39. ը໘ભҠ ᶃϩάΠϯ੒ޭ ᶄ"%%ϘλϯΛΫϦοΫ ᶅ͏·͘อଘ͞Εͨ৔߹ ᶆ5PLFO͕ਖ਼͘͠ͳ͍৔߹

  40. 7VFUJGZͷ࢖͍ํ

  41. 7VFUJGZ IUUQTWVFUJGZKTDPN

  42. <template> <v-form ref="form" v-model="valid"> <p> <v-text-field v-model="title" label="Title" :rules="titleRules"> </v-text-field>

    </p> <p> <v-textarea v-model="body" label="Body"></v-textarea> </p> <p> <v-btn color="info" :disabled="!valid" @click="savePost">Save</v-btn> </p> </v-form> </template> DPNQPOFOUTQPTU'PSNWVF
  43. <script lang="ts"> import { Component, Vue, Prop } from 'vue-property-decorator'

    import Post from '~/models/Post' @Component export default class PostForm extends Vue { @Prop({ type: Object }) post: Post data() { return { title: '', titleRules: [v => !!v || 'Title is required'], body: '', valid: false } } DPNQPOFOUTQPTU'PSNWVF
  44. 7VFUJGZ

  45. +FTUͷ࢖͍ํ

  46. w 'BDFCPPL͕ࣾ044ͱͯ͠։ൃΛਐ Ί͍ͯΔ+BWB4DSJQUͷϢχοτς ετͷͨΊͷπʔϧ w /PEF 3FBDU "OHVMBS 7VFͳͲ ৭ʑͳ؀ڥͰ࢖༻͢Δ͜ͱ͕Ͱ͖

    Δ +FTUͱ͸
  47. import { shallowMount } from "@vue/test-utils" import PostShow from "@/components/post/Show.vue"

    describe("PostShow component", () => { let wrapper; beforeEach(() => { wrapper = shallowMount(PostShow, { propsData: { post: { id: 1, title: "AAA", body: "aaa" } } }); }); it("has the expected html structure", () => { expect(wrapper.element).toMatchSnapshot(); }); it("has the expected text", () => { expect(wrapper.text()).toBe('AAA aaa'); }); }); 1PTU4IPXWVFͷςετ 1PTU4IPXίϯϙʔωϯτʹ1PTUͷ஋Λ౉ͯ͠ ΤϨϝϯτΛੜ੒
  48. import { shallowMount } from "@vue/test-utils" import PostShow from "@/components/post/Show.vue"

    describe("PostShow component", () => { let wrapper; beforeEach(() => { wrapper = shallowMount(PostShow, { propsData: { post: { id: 1, title: "AAA", body: "aaa" } } }); }); it("has the expected html structure", () => { expect(wrapper.element).toMatchSnapshot(); }); it("has the expected text", () => { expect(wrapper.text()).toBe('AAA aaa'); }); }); 1PTU4IPXWVFͷςετ exports[`PostShow component has the expected html structure 1`] = ` <div> <h2> AAA </h2> <p> aaa </p> </div> `; ΋ͱ΋ͱ߹ͬͨεφοϓγϣοτͱ৽ͨʹੜ੒͞Εͨ εφοϓγϣοτ͕Ұக͢Δ͔Λςετ͍ͯ͠Δ
  49. ࠷ޙʹ

  50. ͋ͱ͸ɺDMPOFͯ͠σόοά͠ ͯײ֮௫ΜͰ͍ͩ͘͞

  51. (JUIVCελʔ௖͚Ε͹ࢲ΋΋ͬͱࠓ ޙ΋ؤுͬͯίϛοτ͍͖ͯ͠·͢স

  52. ༧ఆͱͯ͠͸(PPHMFೝূͱ͔ ΋࣮૷͍͖ͯ͠·͢

  53. ͜ͷՆϞς͍ͨํ͸ւͳΜ͔ʹߦ͔ͣʹҾ ͖͜΋ͬͯ044׆ಈ͍͖ͯ͠·͠ΐ͏ʂ

  54. 5IBOLZPV