Vuex 的使用偏好

这是我个人的使用偏好,而且是以抽象资料型别的使用方式来理解 vuex 的使用方式。也许,我是说也许啦!和官网的不太一样。

昨天介绍了物件设计的核心原则,就是不要直接修改资料,务必要透过抽象行为来修改,以保持弹性与添加限制的同时,可以维护概念整体性。

这个原则,在後端也适用於 MVC 的 Module

module 的 setter: mutation

Mutations | Vuex 的开章明义就说,想要改变 state ,就要 over my dead body (透过 mutation)。

The only way to actually change state in a Vuex store is by committing a mutation.

所以,其实 mutation 本身就隐含着 setter 的任务。必须单纯的修改值,并且适时的可以加入定义域限制。

「mutation 是唯一可以改变 state 的角色」,意思是说改变的同时要可以触发修改画面。所以,修改的原则,也是要符合 immutable,或者用 Vue 提供的 API Vue.set(obj, 'newProp', 123)

  • Use Vue.set(obj, 'newProp', 123), or

  • Replace that Object with a fresh one. For example, using the object spread syntax (opens new window)we can write it like this:

state.obj = { ...state.obj, newProp: 123 }

不过我觉得「靠自己,好自在」。所以只要修改值,就要注意 immutable,以及触发自动修改画面的机制 (符合 MVVM 的精神)

实务上的状况

通常 mutation 是在接受 Web API 之後将 Web API 的资料 (通常是 GET 的 API) 储存到 state。

而这个步骤就足以决定你的 vuex 管理是否简单或复杂。

请遵守这些原则

命名不要用动词
命名要与 getters 同名
命名要注意单数/复数

因为使用的时候是

  • this.$store.commit('user', data)
  • this.$store.commit('users', data)

光这一行,你看得出什麽样的资讯?看不出什麽样的资讯呢?

  • [x] 要 set 进 state
  • [x] 要储存到 user 的 module,是单数资料 (一笔)

看得出这样的资讯就够了。

至於 set 进 user 需不需要定义域的阻挡逻辑,通常 API 来的资料,都已经通过画面的栏位验证以及後端在接收到 POST 的时的验证,资料经过了这些验证而进入资料库,所以在取得资料时,其实应该相信这些机制的作用,不用在这个环节再阻挡或验证一次。

但如果,你的 commit 是直接透过画面把值设定进来,也许需要在 Mutation 加入验证,因为要确保 state 是正确的,才不会影响 getters 在输出资料时,还要写更多的逻辑处理。

Actions 呢?

Actions | Vuex

官网的 Actions 说,它可以写非同步行为,而且要用 action 呼叫 mutation

mutation 不要写非同步行为
不要用 action 直接改 state

官网说建议要透过 action 呼叫 mutation 的理由,是万一你要非同步的使用 mutation 的话,就可以直接添加在 action。

我自己的解读是「如果没有要非同步,直接 commit 也是可以」
所以,我自己的用法会是「同步行为与非同步行为,分成 action 和 muation」

实务上的状况

如果看见这样写

this.$store.dispatch('fetchUsers')

就是它要发一个 API 去请求 User 的列表,并且会在里面呼叫 commit('users', res.data) 将 response 的 body 储存在 state 里。

如果看见这样写

this.$store.commit('users', users)

就只是将资料储存到 state 里。

如果看见这样写

this.$store.dispatch('users', users)

我会不知道,这是非同步的还是同步的行为,中间经造了什麽将 users 储存到 state 里。

请遵守这些原则

命名要加上动词

以 User Module 为例 action 的命名会依 API 的用途为主。CRUD 的话已经有一定的命名原则。

行为 命名 实际使用
新增 createUser $store.dispatch('createUser')
读取 fetchUser $store.dispatch('fetchUser', id)
读取 fetchUsers $store.dispatch('fetchUsers')
修改 updateUser $store.dispatch('updateUser', id)
删除 deleteUsers $store.dispatch('deleteUsers')
删除 deleteUser $store.dispatch('deleteUser', id)

命名要具体的描述行为,不需与 API 相同

有时候网站会有一些留言的机制。而留言的目的也许有不同的 type
有时 API 的开法, type 并不是参数,而是 path 的一部份

  • POST /message/issue
  • POST /message/comments

这个 issues 和 comments 就会把 actions 设计成

  • $store.dispatch('createMessage', { type, body })
    • type: comments | issue

这样 actions 的命名,是一种「使用非同步行为」的描述,而不只是非同步行为转变形式的样子

命名要注意单数/复数

名词的部份和 commit 一样,而且也是隐喻着要 commit 到哪里去。

module 的 getter: getters

Getters | Vuex

根据 state 的状态衍生出其它的资料。
可以理解是一种共用的 computed 也可以理解是一种「整理资料的资料逻辑」

如果今天,在修改使用者 (ex: id=1) user 的表单中,需要设定该 user 的主管,这个主管会是由一个下拉式选单来显示。

那麽资料该怎麽取得呢?

  • 取得原本使用者的资料
    • $store.dispatch('fetchUser', 1)
  • 取得主管下拉式选单内容
    • $store.dispatch('fetchUsers')
    • $store.getters.leaders
    • leaderOptions (在 computed 里或 getters 也可以)

取得主管的下拉式选单中,有一个 $store.getters.leaders

getters: {
  leaders(state, getters) {
    const departments = getters.departments;
    return state.users.filter(user => departments.some(department => department.leader === user))
  }
}

将整理资料的程序码写在 getters ,而不是在 mutation 的时候整理资料,有几个好处

  • 後端的 API 可以单纯一点,前端依自己的画面需求整理资料。
  • 有些资料来源相同,可以降低发送 API 的资数

观察它们的参数

mutation

第一个参数是 state (所属的 module 里的 state)
第二个参数是 payload (带什麽新东西要来修改值)?

mutation: {
  user (state, payload) {
    state.user = payload
  }
}

getters

第一个参数是 state (所属的 module 里的 state)
第二个参数是跨 module 的 getters (不直接取得 state)

getters: {
  leaders(state, getters) {
    const departments = getters.departments;
    return state.users.filter(user => departments.some(department => department.leader === user))
  }
}

actions

第一个参数是 context 跨 module 取得 dispatch, commit, getters (不直接取得 state)
第二个参数这次传进来的参数,用起来和 mutation 的 payload 一样。

actions: {
  fetchUser ({ dispatch, commit, getters }, user_id) {
    // ...
  }
}

透过观察参数可以发现 mutation 和 getters 的设计,修改/读取都是以 state 为核心。而 actions 比较像是面对这些 module 的独立 function 。有点像是後端的 controller。

所以,我在使用 vuex 的资料夹规画上面。只会把 actions.js 独立成一个档案。state, mutation, getters 这三样,会是放在一起的,形成了一个有自己 getter/setter 的抽象资料型别。


<<:  Day27 过不去的槛就先绕过它 - LINE Notification

>>:  神兵利器 - Optimizer trace

[Day30] 让Web开发者森77之後 / 总结

正文 今天最後一天了,当初因为没存稿的关系,加上很多主题想写,但又没把握能写好,所以不知道能不能顺利...

【资料视觉化】COVID新冠疫苗施打一览 Seaborn

今晚来点轻松的。资料视觉化,复习一下DataFrame、seaborn 引用资料来源:country...

[24] 用 python 刷 Leetcode: 66 plus-one

原始题目 You are given a large integer represented as ...

6. Prototypal inheritance 的运作原理

(这篇会延续Constructor Function的内容,来解释 Prototype 和 Prot...

# Day28--让commit像战国时代一样分分合合

上一篇我们学到怎麽使用Vim,还有修改commit message,这次要做的事情呢,就是要来合并跟...