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/

WalkerSumida

May 24, 2019
Tweet

More Decks by WalkerSumida

Other Decks in Technology

Transcript

  1. 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 ܦྺ
  2. w αϯϓϧ͸શͯެ։͍ͯ͠·͢ w /VYUKTIUUQTHJUIVCDPN XBMLFSTVNJEBOVYUKTTBNQMF w 3BJMTIUUQTHJUIVCDPN XBMLFSTVNJEBSBJMTBQJGPS OVYUKT w

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

    yarn add -D @nuxt/typescript $ yarn add ts-node $ yarn add vue-property-decorator /VYUKTͷ࡞੒
  4. ಺෦తʹ͸ҎԼͷΑ͏ʹϧʔςΟϯά͕ు͖ग़͞Ε͍ͯΔ router: { routes: [ { path: '/posts', component: 'pages/posts.vue',

    children: [ { path: '', component: 'pages/posts/index.vue', name: 'posts' } ] } ] } QBHFTQPTUTJOEFYWVF
  5. <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
  6. <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
  7. // 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ͱͷ௨৴෦෼
  8. w *%1BTTXPSEΛ4FSWFSʹૹ৴ w *%1BTTXPSE͕ਖ਼͚͠Ε͹ηο γϣϯ*%Λൃߦ͠ɺ$MJFOUଆʹฦ ٫ w $MJFOUଆͰηογϣϯ*%Λ$PPLJF ʹอଘ w

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

    εϙϯεʹ৽ͨͳ5PLFO͕4FSWFSଆ ͔ΒૹΒΕͯ͘ΔͷͰɺͦͷ5PLFO Λอଘ͢Δ w 5PLFO͸࢖͍ࣺͯͳͷͰɺϦΫΤε τຖʹҧ͏ 5PLFOCBTFೝূ ˞EFWJTF@UPLFO@BVUIHFNΛ࢖༻
  10. // 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) }) ೝূ෦෼ͷ࣮૷
  11. <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
  12. <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
  13. 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ͷ஋Λ౉ͯ͠ ΤϨϝϯτΛੜ੒
  14. 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> `; ΋ͱ΋ͱ߹ͬͨεφοϓγϣοτͱ৽ͨʹੜ੒͞Εͨ εφοϓγϣοτ͕Ұக͢Δ͔Λςετ͍ͯ͠Δ