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

Vuex ORM –フロントエンドのORMと、データのノーマライズ– | Vue.js v-tokyo Meetup #9

Vuex ORM –フロントエンドのORMと、データのノーマライズ– | Vue.js v-tokyo Meetup #9

僕が開発しているVuexにORMライクなアクセスを提供するVuex Plugin、“Vuex ORM”のエントリー向けデックです。Vuex ORMとは何か、なぜ必要か、どうやって使うかをご覧いただけます。

Kia King Ishii

April 24, 2019
Tweet

More Decks by Kia King Ishii

Other Decks in Programming

Transcript

  1. .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') } }
  2. .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ͷΩʔ໊ͱͳΔɻ
  3. .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') } } } ϞσϧͷϑΟʔϧυɻ ͜ͷεΩʔϚʹԊͬͯσʔλ͕ ੜ੒͞ΕΔɻ
  4. .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͕֎෦ΩʔͱͳΔɻ
  5. %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)
  6. 7VFYʹΠϯετʔϧ͢Δ import Vuex from 'vuex' import VuexORM from '@vuex-orm/core' import

    database from '@/database' const store = new Vuex.Store({ plugins: [VuexORM.install(database)] })
  7. σʔλΛอଘ͢Δ // Postͷσʔλ const post = { id: 1, user_id:

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

    1, title: 'Hello, world!', user: { id: 1, name: 'John Doe' } } ϦϨʔγϣϯɻ 1PTUͷதʹ6TFS͕ωετ͞Ε͍ͯΔɻ
  9. σʔλΛอଘ͢Δ // 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 })
  10. 7VFY4UPSFͷத਎ // Vuex StoreͷState { posts: { data: { 1:

    { id: 1, user_id: 1, title: 'Hello, world!' } } }, users: { data: { 1: { id: 1, name: 'John Doe' } } } }
  11. 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Λ෼཭ϊʔϚϥΠζ
  12. σʔλͷऔಘ import Post from '@/models/Post' // શͯͷPostΛऔಘ Post.all() // PublishedͳPostΛશͯऔಘ

    Post.query().where('published', true).get() // UserΛҰॹʹऔಘ Post.query().with('user').get()
  13. 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: '...' } } }
  14. 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: '...' } } }
  15. .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') } } }
  16. .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ͷΩʔ໊ͱͳΔɻ
  17. .PEFMΛఆٛ͢Δ // Vuex Store State { entities: { users: {

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

    data: {} }, posts: { data: {} } } } Ϟσϧ͕૿͑ΔͱΩʔ͕૿͑Δɻ
  19. .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') } } } ϞσϧͷϑΟʔϧυɻ ͜ͷεΩʔϚʹԊͬͯσʔλ͕ ੜ੒͞ΕΔɻ
  20. .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ɻ ϦϨʔγϣϯͰ͸ͳ͍ɺϑΟʔϧυͷܕɻ
  21. .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
  22. .PEFMΛఆٛ͢Δ // ݩσʔλ { id: 1, age: 30 } //

    อଘ͞Εͨޙͷσʔλ { id: 1, name: '' } .PEFMͰఆ͍ٛͯ͠ͳ͍ϑΟʔϧυ͸ແࢹ͞ΕΔɻ .PEFMͰఆٛ͞Ε͍ͯΔ͕ଘࡏ͠ͳ͍ϑΟʔϧυ͸ σϑΥϧτ஋Ͱੜ੒͞ΕΔɻ
  23. .PEFMΛఆٛ͢Δ class User extends Model { static entity = 'users'

    static fields () { return { id: this.attr(null), name: this.string(''), active: this.boolean(true) } } }
  24. .PEFMΛఆٛ͢Δ // ݩσʔλ { id: 1, name: 'John Doe', active:

    1 } // อଘ͞Εͨޙͷσʔλ { id: 1, name: 'John Doe’, active: true }
  25. .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ɻ ϞσϧͷϦϨʔγϣϯΛఆٛ͢Δܕɻ
  26. .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
  27. .PEFMΛఆٛ͢Δ class User extends Model { static entity = 'users'

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

    static primaryKey = ['roleId', 'userId'] static fields () { return { roleId: this.attr(null), userId: this.attr(null) } } } ෳ߹ΩʔʢίϯϙδοτΩʔʣʹ΋ ରԠՄೳɻ
  29. .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()
  30. .PEVMFͷఆٛ export function state () { return { count: 0

    } } export const mutations = { async increment (state) { state.count++ } }
  31. .PEFMΛఆٛ͢Δ // Vuex Store State { entities: { users: {

    count: 0, data: {} } } } ޷͖ͳ7VFY.PEVMFͷػೳΛ௥ՃՄೳɻ
  32. %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)
  33. %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Λొ࿥͢Δ৔߹͸ୈೋҾ਺ʹ౉͢ɻ
  34. 7VF$PNQPOFOUͰͷ࢖͍ํ <ul> <li :key="user.id" v-for="user in users" > {{ user.name

    }} </li> </ul> import User from '@/models/User' export default { computed: { users () { return User.all() } } } Template Script
  35. import User from '@/models/User' export default { methods: { addUser

    () { User.new() } } } <button @click="addUser"> ADD USER </button> 7VF$PNQPOFOUͰͷ࢖͍ํ Template Script
  36. σʔλͷ࡞੒ɾอଘ const data = { id: 1, user_id: 1, title:

    'Hello, world!', user: { id: 1, name: 'John Doe' } } Post.insert({ data }) .PEFMϝιου͕ར༻Մೳɻ
  37. σʔλͷ࡞੒ɾอଘ 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΋ར༻Մೳɻ
  38. σʔλͷ࡞੒ɾอଘ // Vuex StoreͷState { posts: { data: { 1:

    { id: 1, user_id: 1, title: 'Hello, world!' } } }, users: { data: { 1: { id: 1, name: 'John Doe' } } } }
  39. /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)
  40. σʔλͷߋ৽ Post.update({ where (post) { return post.published }, data: {

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

    }) EBUB΋ΫϩʔδϟͰࢦఆՄೳɻ ഑ྻ΍ΦϒδΣΫτͷϑΟʔϧυΛߋ৽ ͍ͨ͠৔߹ʹศརɻ
  42. σʔλͷऔಘ Post.all() [ { id: 1, title: '...' }, {

    id: 2, title: '...' } ] શͯͷ1PTUΛऔಘ͢Δɻ ໭Γ஋͸഑ྻɻ
  43. σʔλͷऔಘ export default { computed: { posts () { return

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

    this.$store .getters['entities/posts/all']() } } } 7VFY(FUUFSTͱͯ͠ݺͼग़ͯ͠΋0,ɻ
  45. ϦϨʔγϣϯͷϩʔυ Post.query().with('user').first() { id: 1, title: '...', user: { id:

    1, name: 'John Doe' } } XJUIΛ࢖ͬͯϦϨʔγϣϯΛϩʔυ͢Δɻ
  46. ϦϨʔγϣϯͷϩʔυ <article> <h1>{{ post.title }}</h1> <p> Author: {{ post.user.fullName() }}

    </p> </article> import Post from '@/models/Post' export default { computed () { post () { return Post.query(). .with('user') .where( 'slug', this.$route.params.slug ) .first() } } } Template Script
  47. 6TFSΛVQEBUF͢Δͱ͖ɺEP/PU.PEJGZ ϓϩύςΟ͕5SVUIZͳϞσϧ͸VQEBUFΛ Ωϟϯηϧ͢Δɻ class User extends Model { // ...

    beforeUpdate (model) { if (model.doNotModify) { return false } } } -JGFDZDMF)PPL
  48. 7VFͱಉ͡ܗͰ 1MVHJO͕࡞੒Մೳɻ const plugin = { install ({ Model },

    options) { Model.prototype.globalMethod = function () { // Logic... } } } VuexORM.use(plugin) 1MVHJOT
  49. Vuex ORM Search Fuse.jsΛ࢖ͬͨᐆດݕࡧ͕Մೳʹɻ Vuex ORM Soft Delete ιϑτσϦʔτ͕Մೳʹɻ Vuex

    ORM Change Flags Ϟσϧʹมߋ͕͔͋ͬͨͲ͏͔ʢisDirtyʣΛ൑ผՄೳʹɻ 0⒏DJBM1MVHJOT