Slide 1

Slide 1 text

ϑϩϯτΤϯυͷ03.ͱ σʔλͷϊʔϚϥΠζ Kia Ishii Vue.js v-tokyo Meetup #9

Slide 2

Slide 2 text

ੴҪًѥ Kia King Ishii ໌Δ͘ɺָ͘͠ɺָ؍ओ͕ٛϞοτʔͷ σβΠφʔ/σϕϩούʔ Skills Vue / Laravel

Slide 3

Slide 3 text

1SJODJQBM5FDI5BMFOU IUUQTHMPCBMCSBJOTDPN

Slide 4

Slide 4 text

7VFY03.ͱ͸ʁ

Slide 5

Slide 5 text

7VFY03.ͱ͸ʁ w σʔλΛʮϊʔϚϥΠζʯ͢Δɻ w 7VFY4UPSF΁03.ϥΠΫͳΞΫηεΛఏڙɻ w -BSBWFM&MPRVFOU΍"DUJWF3FDPSEͷΑ͏ͳ"1*ɻ

Slide 6

Slide 6 text

ͬ͘͟ΓͲΜͳײ͔͡

Slide 7

Slide 7 text

.PEFMΛఆٛ͢Δ import { Model } from '@vuex-orm/core' class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), name: this.string(''), posts: this.hasMany(Post, 'user_id') } } } import { Model } from '@vuex-orm/core' class Post extends Model { static entity = 'posts' static fields () { return { id: this.attr(null), user_id: this.attr(null), title: this.string(''), user: this.belongsTo(User, 'user_id') } }

Slide 8

Slide 8 text

.PEFMΛఆٛ͢Δ import { Model } from '@vuex-orm/core' class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), name: this.string(''), posts: this.hasMany(Post, 'user_id') } } } Ϟσϧͷ໊લɻ 7VFY4UBUFͷΩʔ໊ͱͳΔɻ

Slide 9

Slide 9 text

.PEFMΛఆٛ͢Δ import { Model } from '@vuex-orm/core' class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), name: this.string(''), posts: this.hasMany(Post, 'user_id') } } } ϞσϧͷϑΟʔϧυɻ ͜ͷεΩʔϚʹԊͬͯσʔλ͕ ੜ੒͞ΕΔɻ

Slide 10

Slide 10 text

.PEFMΛఆٛ͢Δ import { Model } from '@vuex-orm/core' class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), name: this.string(''), posts: this.hasMany(Post, 'user_id') } } } ϦϨʔγϣϯɻ 6TFS͸ෳ਺ͷ1PTUΛ࣋ͭɻ AVTFS@JEA͕֎෦ΩʔͱͳΔɻ

Slide 11

Slide 11 text

%BUBCBTFʹ.PEFMΛొ࿥͢Δ import { Database } from '@vuex-orm/core' import User from '@/models/User' import Post from '@/models/Post' const database = new Database() database.register(User) database.register(Post)

Slide 12

Slide 12 text

7VFYʹΠϯετʔϧ͢Δ import Vuex from 'vuex' import VuexORM from '@vuex-orm/core' import database from '@/database' const store = new Vuex.Store({ plugins: [VuexORM.install(database)] })

Slide 13

Slide 13 text

σʔλΛอଘ͢Δ // Postͷσʔλ const post = { id: 1, user_id: 1, title: 'Hello, world!', user: { id: 1, name: 'John Doe' } }

Slide 14

Slide 14 text

σʔλΛอଘ͢Δ // Postͷσʔλ const post = { id: 1, user_id: 1, title: 'Hello, world!', user: { id: 1, name: 'John Doe' } } ϦϨʔγϣϯɻ 1PTUͷதʹ6TFS͕ωετ͞Ε͍ͯΔɻ

Slide 15

Slide 15 text

σʔλΛอଘ͢Δ // Postͷσʔλ const post = { id: 1, user_id: 1, title: 'Hello, world!', user: { id: 1, name: 'John Doe' } } import Post from '@/models/Post' Post.insert({ data: post })

Slide 16

Slide 16 text

7VFY4UPSFͷத਎ // Vuex StoreͷState { posts: { data: { 1: { id: 1, user_id: 1, title: 'Hello, world!' } } }, users: { data: { 1: { id: 1, name: 'John Doe' } } } }

Slide 17

Slide 17 text

7VFY4UPSFͷத਎ // Vuex StoreͷState { posts: { data: { 1: { id: 1, user_id: 1, title: 'Hello, world!' } } }, users: { data: { 1: { id: 1, name: 'John Doe' } } } } 1PTUͱ6TFSΛ෼཭ϊʔϚϥΠζ

Slide 18

Slide 18 text

σʔλͷऔಘ import Post from '@/models/Post' // શͯͷPostΛऔಘ Post.all() // PublishedͳPostΛશͯऔಘ Post.query().where('published', true).get() // UserΛҰॹʹऔಘ Post.query().with('user').get()

Slide 19

Slide 19 text

7VFY03. w σʔλΛʮϊʔϚϥΠζʯ͢Δɻ w 7VFY4UPSF΁03.ϥΠΫͳΞΫηεΛఏڙɻ w -BSBWFM&MPRVFOU΍"DUJWF3FDPSEͷΑ͏ͳ"1*ɻ

Slide 20

Slide 20 text

ͳͥඞཁͳͷ͔ʁ

Slide 21

Slide 21 text

՝୊ ಉ͡σʔλ͕ࢄΒ͹Δ

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

6TFS 6TFS

Slide 24

Slide 24 text

// User͕ωετ͞ΕͯΔ { id: 1, title: 'Star Vuex ORM repository', assignee: { id: 1, name: 'Jane Doe' } }

Slide 25

Slide 25 text

՝୊ ॏෳͨ͠σʔλΛશͯߋ৽͢Δͷ͸ࠎ͕ંΕΔɻ

Slide 26

Slide 26 text

՝୊ ॏෳͨ͠σʔλΛશͯߋ৽͢Δͷ͸ࠎ͕ંΕΔɻ ωετͨ͠σʔλ͸ѻ͏ͷ͕େมɻ

Slide 27

Slide 27 text

՝୊ ॏෳͨ͠σʔλΛશͯߋ৽͢Δͷ͸ࠎ͕ંΕΔɻ ωετͨ͠σʔλ͸ѻ͏ͷ͕େมɻ ͳΜ͔ؾ࣋ͪѱ͍ɻ

Slide 28

Slide 28 text

ͳΜ͔ؾ࣋ͪѱ͍

Slide 29

Slide 29 text

Ͳ͏͢Δʁ

Slide 30

Slide 30 text

7VFYΛ%#ͱͯ͠ѻ͏

Slide 31

Slide 31 text

7VFYΛ%#ͱͯ͠ѻ͏ // User { users: { 1: { id: 1, name: 'John Doe' }, 2: { id: 2, name: 'Jane Doe' } } } // Todo { todos: { 1: { id: 1, user_id: 1, title: '...' }, 2: { id: 2, user_id: 2, title: '...' } } }

Slide 32

Slide 32 text

7VFYΛ%#ͱͯ͠ѻ͏ // User { users: { 1: { id: 1, name: 'John Doe' }, 2: { id: 2, name: 'Jane Doe' } } } // Todo { todos: { 1: { id: 1, user_id: 1, title: '...' }, 2: { id: 2, user_id: 2, title: '...' } } }

Slide 33

Slide 33 text

͍͍ͷ͔ʁ

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

l/PSNBMJ[JOH4UBUF4IBQFz l6QEBUJOH/PSNBMJ[FE%BUBz

Slide 36

Slide 36 text

l5IFSFDPNNFOEFEBQQSPBDIUP NBOBHJOHSFMBUJPOBMPSOFTUFEEBUBJO B3FEVYTUPSFJTUPUSFBUBQPSUJPOPG ZPVSTUPSFBTJGJUXFSFBEBUBCBTFz — Redux Documentation

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

3FEVY03.

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

͍͍ͷͩ

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

Ͳ͏΍ͬͯ࢖͏͔

Slide 43

Slide 43 text

.PEFMΛఆٛ͢Δ import { Model } from '@vuex-orm/core' class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), name: this.string(''), posts: this.hasMany(Post, 'user_id') } } }

Slide 44

Slide 44 text

.PEFMΛఆٛ͢Δ import { Model } from '@vuex-orm/core' class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), name: this.string(''), posts: this.hasMany(Post, 'user_id') } } } Ϟσϧͷ໊લɻ 7VFY4UBUFͷΩʔ໊ͱͳΔɻ

Slide 45

Slide 45 text

.PEFMΛఆٛ͢Δ // Vuex Store State { entities: { users: { data: {} } } } σϑΥϧτͰ͸AFOUJUJFTAΩʔ഑Լʹ શͯͷϞσϧ͕ొ࿥͞ΕΔɻ

Slide 46

Slide 46 text

.PEFMΛఆٛ͢Δ // Vuex Store State { entities: { users: { data: {} }, posts: { data: {} } } } Ϟσϧ͕૿͑ΔͱΩʔ͕૿͑Δɻ

Slide 47

Slide 47 text

.PEFMΛఆٛ͢Δ import { Model } from '@vuex-orm/core' class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), name: this.string(''), posts: this.hasMany(Post, 'user_id') } } } ϞσϧͷϑΟʔϧυɻ ͜ͷεΩʔϚʹԊͬͯσʔλ͕ ੜ੒͞ΕΔɻ

Slide 48

Slide 48 text

.PEFMΛఆٛ͢Δ import { Model } from '@vuex-orm/core' class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), name: this.string(''), posts: this.hasMany(Post, 'user_id') } } } 5ZQF"UUSJCVUFɻ ϦϨʔγϣϯͰ͸ͳ͍ɺϑΟʔϧυͷܕɻ

Slide 49

Slide 49 text

.PEFMΛఆٛ͢Δ import { Model } from '@vuex-orm/core' class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), name: this.string(''), posts: this.hasMany(Post, 'user_id') } } } 5ZQF"UUSJCVUFͷछྨ UIJTBUUS UIJTTUSJOH UIJTOVNCFS UIJTCPPMFBO UIJTJODSFNFOU

Slide 50

Slide 50 text

.PEFMΛఆٛ͢Δ // ݩσʔλ { id: 1, age: 30 } // อଘ͞Εͨޙͷσʔλ { id: 1, name: '' } .PEFMͰఆ͍ٛͯ͠ͳ͍ϑΟʔϧυ͸ແࢹ͞ΕΔɻ .PEFMͰఆٛ͞Ε͍ͯΔ͕ଘࡏ͠ͳ͍ϑΟʔϧυ͸ σϑΥϧτ஋Ͱੜ੒͞ΕΔɻ

Slide 51

Slide 51 text

.PEFMΛఆٛ͢Δ class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), name: this.string(''), active: this.boolean(true) } } }

Slide 52

Slide 52 text

.PEFMΛఆٛ͢Δ // ݩσʔλ { id: 1, name: 'John Doe', active: 1 } // อଘ͞Εͨޙͷσʔλ { id: 1, name: 'John Doe’, active: true }

Slide 53

Slide 53 text

.PEFMΛఆٛ͢Δ import { Model } from '@vuex-orm/core' class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), name: this.string(''), posts: this.hasMany(Post, 'user_id') } } } 3FMBUJPO"UUSJCVUFɻ ϞσϧͷϦϨʔγϣϯΛఆٛ͢Δܕɻ

Slide 54

Slide 54 text

.PEFMΛఆٛ͢Δ import { Model } from '@vuex-orm/core' class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), name: this.string(''), posts: this.hasMany(Post, 'user_id') } } } 3FMBUJPO"UUSJCVUF UIJTIBT0OF UIJTCFMPOHT5P UIJTIBT.BOZ UIJTIBT.BOZ#Z UIJTIBT.BOZ5ISPVHI UIJTCFMPOHT5P.BOZ UIJTNPSQI0OF UIJTNPSQI.BOZ UIJTNPSQI5P UIJTNPSQI5P.BOZ UIJTNPSQIFE#Z.BOZ

Slide 55

Slide 55 text

.PEFMΛఆٛ͢Δ class User extends Model { static entity = 'users' static primaryKey = 'userId' static fields () { return { userId: this.attr(null), name: this.string('') } } } ϓϥΠϚϦΩʔ͸มߋՄೳɻ

Slide 56

Slide 56 text

.PEFMΛఆٛ͢Δ class RoleUser extends Model { static entity = 'role_user' static primaryKey = ['roleId', 'userId'] static fields () { return { roleId: this.attr(null), userId: this.attr(null) } } } ෳ߹ΩʔʢίϯϙδοτΩʔʣʹ΋ ରԠՄೳɻ

Slide 57

Slide 57 text

.PEFMΛఆٛ͢Δ class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), name: this.string(''), role: this.string('') } } isAdmin () { return this.role === 'admin' } } ϞσϧͳͷͰ"DDFTTPSͷఆٛ΋ࣗ༝ࣗࡏɻ user.isAdmin()

Slide 58

Slide 58 text

.PEVMFͷఆٛ export function state () { return { count: 0 } } export const mutations = { async increment (state) { state.count++ } }

Slide 59

Slide 59 text

.PEFMΛఆٛ͢Δ // Vuex Store State { entities: { users: { count: 0, data: {} } } } ޷͖ͳ7VFY.PEVMFͷػೳΛ௥ՃՄೳɻ

Slide 60

Slide 60 text

%BUBCBTFʹ.PEFMΛొ࿥͢Δ import { Database } from '@vuex-orm/core' import User from '@/models/User' import Post from '@/models/Post' const database = new Database() database.register(User) database.register(Post)

Slide 61

Slide 61 text

%BUBCBTFʹ.PEFMͱ.PEVMFΛొ࿥͢Δ import { Database } from '@vuex-orm/core' import User from '@/models/User' import users from '@/modules/users' import Post from '@/models/Post' const database = new Database() database.register(User, users) database.register(Post) .PEVMFΛొ࿥͢Δ৔߹͸ୈೋҾ਺ʹ౉͢ɻ

Slide 62

Slide 62 text

7VF$PNQPOFOUͰͷ࢖͍ํ
  • {{ user.name }}
import User from '@/models/User' export default { computed: { users () { return User.all() } } } Template Script

Slide 63

Slide 63 text

import User from '@/models/User' export default { methods: { addUser () { User.new() } } } ADD USER 7VF$PNQPOFOUͰͷ࢖͍ํ Template Script

Slide 64

Slide 64 text

σʔλͷ࡞੒ɾอଘ const data = { id: 1, user_id: 1, title: 'Hello, world!', user: { id: 1, name: 'John Doe' } } Post.insert({ data }) .PEFMϝιου͕ར༻Մೳɻ

Slide 65

Slide 65 text

σʔλͷ࡞੒ɾอଘ const data = { id: 1, user_id: 1, title: 'Hello, world!', user: { id: 1, name: 'John Doe' } } this.$store.dispatch('entities/users/insert', { data }) 7VFY.PEVMF΋ར༻Մೳɻ

Slide 66

Slide 66 text

σʔλͷ࡞੒ɾอଘ // Vuex StoreͷState { posts: { data: { 1: { id: 1, user_id: 1, title: 'Hello, world!' } } }, users: { data: { 1: { id: 1, name: 'John Doe' } } } }

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

/PSNBMJ[Sͷ࢖͍ํ import { normalize, schema } from 'normalizr' // UserΛఆٛɻ const user = new schema.Entity('user') // PostΛఆٛɻ const post = new schema.Entity('post', { user }) // σʔλΛϊʔϚϥΠζɻ normalize(data, post)

Slide 69

Slide 69 text

σʔλͷ࡞੒ɾอଘ Post.insert() Post.create() Post.new() Post.update() Post.insertOrUpdate() Ϩίʔυͷ௥ՃɻID͕ॏෳͯͨ͠Β্ॻ͖ɻ طଘͷϨίʔυΛશͯ࡟আͯ͠ϨίʔυΛ௥Ճɻ શͯσϑΥϧτ஋ͷϨίʔυΛ௥Ճɻ ϨίʔυΛߋ৽ɻ Ϩίʔυ͕ଘࡏ͢Ε͹ߋ৽ɻͳ͚Ε͹৽ن௥Ճɻ

Slide 70

Slide 70 text

σʔλͷߋ৽ Post.update({ where: 1, data: { title: 'Modified title.' } }) JE͕ͷ1PTUͷUJUMFΛߋ৽ɻ

Slide 71

Slide 71 text

σʔλͷߋ৽ Post.update({ where (post) { return post.published }, data: { published: false } }) XIFSF͸ΫϩʔδϟΛࢦఆՄೳɻ ෳ਺ͷϨίʔυΛߋ৽͢Δ৔߹ʹศརɻ

Slide 72

Slide 72 text

σʔλͷߋ৽ Post.update({ data: { id: 1, title: 'Modified title.' } }) 1SJNBSZ,FZΛEBUBʹؚΊΔܗͰ΋0,ɻ

Slide 73

Slide 73 text

σʔλͷߋ৽ Post.update({ where: 1, data (post) { post.tags.push('new tag') } }) EBUB΋ΫϩʔδϟͰࢦఆՄೳɻ ഑ྻ΍ΦϒδΣΫτͷϑΟʔϧυΛߋ৽ ͍ͨ͠৔߹ʹศརɻ

Slide 74

Slide 74 text

7VFY03.͸7VFY

Slide 75

Slide 75 text

4UBUFΛ.VUBUFͯ͠͸ ͍͚ͳ͍

Slide 76

Slide 76 text

ඞͣ7VFYܦ༝ͰσʔλΛߋ৽ post.title = 'New title' Post.update({ where: 1, data: { title: 'Modified title.' } }) NO! YES!

Slide 77

Slide 77 text

σʔλͷ࡟আ Post.delete(1) JE͕ͷ1PTUΛ࡟আ͢Δɻ

Slide 78

Slide 78 text

σʔλͷ࡟আ Post.delete((post) => { return !post.published }) ΫϩʔδϟͰ΋ࢦఆՄೳɻ 1VCMJTIFE͕GBMTZͳ1PTUΛશͯ࡟আɻ

Slide 79

Slide 79 text

σʔλͷऔಘ Post.all() [ { id: 1, title: '...' }, { id: 2, title: '...' } ] શͯͷ1PTUΛऔಘ͢Δɻ ໭Γ஋͸഑ྻɻ

Slide 80

Slide 80 text

σʔλͷऔಘ export default { computed: { posts () { return Post.all() } } } ݪଇDPNQVUFEϓϩύςΟͷதͰ ݺͼग़͢ɻ

Slide 81

Slide 81 text

σʔλͷऔಘ export default { computed: { posts () { return this.$store .getters['entities/posts/all']() } } } 7VFY(FUUFSTͱͯ͠ݺͼग़ͯ͠΋0,ɻ

Slide 82

Slide 82 text

σʔλͷऔಘ Post.find(1) JE͕ͷ1PTUΛऔಘɻ ໭Γ஋͸ΦϒδΣΫτɻ

Slide 83

Slide 83 text

σʔλͷऔಘ const post = Post.find(1) post.hasTag('vuex-orm') औಘͨ͠σʔλ͸ϞσϧͷΠϯελϯεɻ Ϟσϧʹఆٛͨ͠ϝιου͕ར༻Մೳɻ

Slide 84

Slide 84 text

2VFSZ#VJMEFS Post.query() .where('published', true) .get() QVCMJTIFE͕USVFͷ1PTU͚ͩΛ શͯऔಘɻ

Slide 85

Slide 85 text

2VFSZ#VJMEFS Post.query() .where('published', true) .orWhere('category', 'vue') .get() QVCMJTIFE͕USVFɺ·ͨ͸ΧςΰϦ͕ WVFͷ1PTUΛશͯऔಘɻ

Slide 86

Slide 86 text

2VFSZ#VJMEFS Post.query() .where((post) => { return post.published || post.category === 'vue' }) .get() ΫϩʔδϟΛ࢖ͬͯ͞ΒʹϫΠϧυʹɻ

Slide 87

Slide 87 text

2VFSZ#VJMEFS Post.query() .orderBy('created_at', 'desc') .get() Post.query() .offset(1) .limit(15) .get() PSEFS#ZʹΑΔιʔτɺP⒎TFUɺMJNJUʹ ΑΔϖʔδϯάͳͲ΋Մೳɻ

Slide 88

Slide 88 text

2VFSZ#VJMEFS Post.query().count() Post.query().max('likes') Post.query().min('likes') Post.query().sum('likes') DPVOUɺNBYͱ͍ͬͨ"HHSFHBUF ϝιου΋ଘࡏɻ

Slide 89

Slide 89 text

ϦϨʔγϣϯͷϩʔυ Post.query().with('user').first() { id: 1, title: '...', user: { id: 1, name: 'John Doe' } } XJUIΛ࢖ͬͯϦϨʔγϣϯΛϩʔυ͢Δɻ

Slide 90

Slide 90 text

ϦϨʔγϣϯͷϩʔυ const post = Post.query() .with('user') .first() post.user.fullName() ϦϨʔγϣϯ΋΋ͪΖΜϞσϧͷ Πϯελϯεɻ

Slide 91

Slide 91 text

ϦϨʔγϣϯͷϩʔυ

{{ post.title }}

Author: {{ post.user.fullName() }}

import Post from '@/models/Post' export default { computed () { post () { return Post.query(). .with('user') .where( 'slug', this.$route.params.slug ) .first() } } } Template Script

Slide 92

Slide 92 text

ϦϨʔγϣϯͷϩʔυ User.query() .has('posts', '>=', 3) .get() 1PTUΛ݅Ҏ্͍࣋ͬͯΔϢʔβͷΈ શͯऔಘɻ

Slide 93

Slide 93 text

6TFSΛVQEBUF͢Δͱ͖ɺEP/PU.PEJGZ ϓϩύςΟ͕5SVUIZͳϞσϧ͸VQEBUFΛ Ωϟϯηϧ͢Δɻ class User extends Model { // ... beforeUpdate (model) { if (model.doNotModify) { return false } } } -JGFDZDMF)PPL

Slide 94

Slide 94 text

৭ʑͳλΠϛϯάͷ)PPL͕ར༻Մೳɻ beforeCreate() afterCreate() beforeUpdate() afterUpdate() beforeDelete() afterDelete() beforeSelect() -JGFDZDMF)PPL

Slide 95

Slide 95 text

7VFͱಉ͡ܗͰ 1MVHJO͕࡞੒Մೳɻ const plugin = { install ({ Model }, options) { Model.prototype.globalMethod = function () { // Logic... } } } VuexORM.use(plugin) 1MVHJOT

Slide 96

Slide 96 text

No content

Slide 97

Slide 97 text

No content

Slide 98

Slide 98 text

Vuex ORM Search Fuse.jsΛ࢖ͬͨᐆດݕࡧ͕Մೳʹɻ Vuex ORM Soft Delete ιϑτσϦʔτ͕Մೳʹɻ Vuex ORM Change Flags Ϟσϧʹมߋ͕͔͋ͬͨͲ͏͔ʢisDirtyʣΛ൑ผՄೳʹɻ 0⒏DJBM1MVHJOT

Slide 99

Slide 99 text

Join our Slack IUUQTWVFYPSNTMBDLDPN

Slide 100

Slide 100 text

ΤϯδχΞɺ୳ͯ͠·͢ʂ IUUQTHMPCBMCSBJOTDPN

Slide 101

Slide 101 text

Thank you!

Slide 102

Slide 102 text

ϑϩϯτΤϯυͷ03.ͱ σʔλͷϊʔϚϥΠζ Kia Ishii Vue.js v-tokyo Meetup #9