18143453325 在线咨询 在线咨询
18143453325 在线咨询
所在位置: 首页 > 营销资讯 > 网站运营 > Vuex4.0的基本使用方法

Vuex4.0的基本使用方法

时间:2023-04-21 02:27:01 | 来源:网站运营

时间:2023-04-21 02:27:01 来源:网站运营

Vuex4.0的基本使用方法:

vuex 4.0

Vuex 是一个专为 Vue.js 应用程序开发的**状态管理模式**。它采用集中存储管理应用的所有组件的状态的方式,并以相应的规则保证状态以一种可预测的方式发生变化。

Vuex 也集成到 Vue 的官方调试工具 devtools extension (opens new window),提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

两种使用方法:

官网图片




官方给了两个流程图,一个简单的,我们就不看了。

还有一个复杂一点的,如下图:

研究了好久,总算是了解了一点。并且尝试翻译了一下。

翻译图片

应该没有翻译错吧。

不太理解,为啥设置了可以直接访问后端的方法,前端的状态和后端有啥关系呢?目前能想到的就只有登录状态了。

组件问vuex,用户登录了没?vuex就只能问后端。

等等,不是应该在前端缓存一个登录状态吗?

用户在登录页面完成登录后,后端会返回一个token,然后缓存在前端,以后其他地方问是否登录,直接看这个token不就可以了吗?为啥还有问后端要数据?

如果不需要的话,我就想不出来还有啥状态关系到后端API了。

所以另一个理解就是,vuex其实是支持直接从后端获取数据,然后存入state的,也就是说可以把state当作大号data来看待。

  1. 组件发起一个申请,要获得一个状态。
2. action收到申请后,可以去后端询问,然后通过 commit(mutations) 把需要的状态写入 state 里面,这是可以通过浏览器插件看到 state 的数据。

4. 最后通过 getters 返回给组件。

我在这个流程里面加上了一个前端存储的功能,也就是说可以把 state 存在前端,这样刷新、关掉浏览器、关机重启等情况,state 的数据都不会丢失了。便于恢复状态。

state 的类型

我们先来定义一个简单的state,看看在vue3里面有什么变化。

state: { count: 0, myObject: { time: '现在的时间' } }一个简单类型(number)的count,还有一个引用类型的myObject。

我们打印出来看看效果:

const store = useStore() // 看看state的类型 console.log('state:', store.state) console.log('state.count:', store.state.count) console.log('state.myObject:', store.state.myObject)整个state变成了reactive的形式,看来在vue3里面,vuex直接采用reactive来实现state的响应性。

简单类型没有啥变化,没有变成ref的形式,所以直接使用简单类型的话,还是需要以前的方式。

直接查看 state 的时候,里面的对象还是普通对象,但是当单独看的时候就变成了 reactive 的形式。

这是啥时候变的?想不出来。

尝试了一下,可以用,不会报错,也会正常运行,但是由于 vuex 可以自动把普通对象变成 reactive 的形式了,那么我们再自己加上就没啥意义了。

这个也尝试了一下,可以用,但是有点小变化。

如果是简单类型的 ref,ref 会失效、消失,直接变成普通的简单类型。

如果是对象类型的 ref,会变成 reactive 的形式。

所以用 ref 也是没啥意义。

不建议直接改变 state(状态)

首先官方说要用“提交 mutation”的方式来改变状态,那么这个“mutation”是啥意思呢?我查了一下字典,是“突变”的意思。

没有用常见的“set”,而是用了这个“突变”,大概是想强调“状态的变化”吧。

从面向对象的角度来看,state是状态管理类的内部成员,外部不应该可以直接读写,而是要通过“属性”来操作,只不过js环境嘛,有的时候有点控制不住。

而vuex虽然说要用 mutation 来改变state,但是并没有把其他的修改方式给堵死,留了很多漏洞,但是我们还是应该遵守官方的建议,不要直接修改状态。

好吧,其实我也不太清楚为啥不能这样做,直接读写不是很爽的事情吗?

只是 vuex 都第四个大版本了,这么建议应该是有一定道理的,所以我们应该还是尽量遵守一下的好。

我们先来看看读取状态的各种方式。

state的类型

先定义一个简单的state

state: { count: 0, myObject: { time: '现在的时间:' }, myArray: [1,2,2,3,4] },简单类型、对象和数组,比较有代表性的类型。

这里的类型指的不是 js 的基础类型,而是说vue3提供的ref和reactive。

因为 vuex 把 state 变成了 reactive 的形式,所以我们要先弄清楚这一点,然后才能更好的使用其带来的优势,否则岂不是白白浪费了吗。

// 整体获取 const allStateManage = () => { const store = Vuex.useStore() // 看看state的类型 console.log('state:', store.state) console.log('state.count:', store.state.count) console.log('state.myObject:', store.state.myObject) return { allState } }整个state就是reactive的,成员又是什么样子的呢?

setup里面的vuex的使用方式

import { useStore } from 'vuex'setup() { const store = useStore()}这样获取store,然后就可以按照“$store”的习惯来操作了。

直接获取 state

// 整体获取 const allStateManage = () => { const store = Vuex.useStore() // 获得整体的state const allState = store.state console.log('allState:', allState) console.log('================') // 定时修改 count 看响应性 setTimeout(() => { // store.commit('setCount') // allState.count += 101 // 会直接改vuex的state }, 1000) return { allState } }

获取state的成员

// 直接获取成员 const stateMemberManage = () => { const store = Vuex.useStore() // 看看state的类型 let memberCount = store.state.count // 失去响应性 const memberObject = store.state.myObject console.log('memberCount', memberCount) console.log('memberObject', memberObject) console.log('================') // 定时修改 count 看响应性 setTimeout(() => { memberCount += 101 // const 定义的会报错,不允许赋值,常量。 // let 定义的可以修改,但是没有相应性 }, 1000) return { memberCount, memberObject } }

间接获取state

// 间接获取 const indirectManage = () => { const store = Vuex.useStore() // 用toRef获取 count,有相应性,可以直接修改state const refCount = Vue.toRef(store.state, 'count') // 计算属性获取count,有相应性,不可以直接修改state const comCount = Vue.computed(() => store.state.count) // 只读的对象,有相应性,浅层不可以修改,但是深层还是可以修改。 const readonlyObject = Vue.readonly(store.state.myObject) console.log('refCount:', refCount) console.log('comCount:', comCount) console.log('readonlyObject:', readonlyObject) console.log('================') // 定时修改 count 看响应性 setTimeout(() => { // store.commit('setCount') // refCount.value += 200 // 会直接改vuex的state }, 2000) return { refCount, comCount, readonlyObject } }因为引用类型会自动变成reactive的形式,而reactive又可以直接修改state,那么就有可能误操作,导致修改state的情况,那么如何预防呢?可以使用vue提供的readonly。

处理后返回state

// 处理后返回 const operationManage = () => { const store = Vuex.useStore() // 计算属性获取count const addCount = '' // Vue.computed(() => store.state.count++) const getAddCount = store.getters.getAddCount const comGetAddCount = Vue.computed(() => store.getters.getAddCount) const filterArray = store.getters.filterArray(2) const comFilterArray = Vue.computed(() => store.getters.filterArray(2)) console.log('addCount :', addCount) console.log('getAddCount :', getAddCount) console.log('comGetAddCount :', comGetAddCount) console.log('filterArray :', filterArray) console.log('================') return { addCount, getAddCount, comGetAddCount, filterArray, comFilterArray } }

封装读取方式

看到上面这些读取方式,是不是一个头两个大?

这也太复杂了吧,我到底应该用什么方式?

我觉得把这个问题留给组件里面决定,这是不负责任的方式。

我们应该做一个独立的js文件,然后在里面根据公司的要求,或者事先的约定,依据业务需求、项目需求来确定采用哪种方式。

在js文件里面封装好之后,组件里直接调用就好,不用管其他。

这样就好维护多了。

我们做一个js文件

const map = () => { const store = Vuex.useStore() /** * 获取count, * 用computed实现相应 */ const getCount = () => { return Vue.computed(() => store.state.count) } /** * 获取count, ** 用 ref 实现相应 */ const getRefCount = () => { return Vue.ref(store.state.count) } /** * 获取对象 */ const getObject = () => { return store.state.myObject } /** * 获取只读对象 */ const getReadonlyObject = () => { return Vue.readonly(store.state.myObject) } /** * 获取数组 */ const getArray = () => { return store.state.myArray } /** * 获取只读数组 */ const getReadonlyArray = () => { return Vue.readonly(store.state.myArray) } /** * 查询数组 ** id:要查询的数据 */ const filterArray = (id) => { return Vue.computed(() => store.getters.filterArray(id)) } return { getCount, getRefCount, getObject, getReadonlyObject, getArray, getReadonlyArray, filterArray }}export default map这样就可以把麻烦事交给抽离处理的js来处理了,组件只需要调用就好,不用管其他的。

setup 组合API的威力

最后附一下setup的代码,vue3的composition的方式实在是太方便了

setup() { // 传说中的setup const store = Vuex.useStore() // 状态的控制事件 const setCount = () =>{ store.commit('setCount') store.commit('setTime') store.commit('setArray') store._mutations.setCount[0] // 这是什么? } // 获取state const { allState } = allStateManage() // 直接获取成员 const { memberCount, memberObject } = stateMemberManage() // 间接获取成员 const { refCount, comCount, readonlyObject } = indirectManage() // 间接获取成员 const { addCount, getAddCount, comGetAddCount, filterArray, comFilterArray } = operationManage() // 通过map 获取 count // 可以使用别名 const { getCount: mapGetCount } = map() const mapCount = mapGetCount() return { // 返回给模板,否则模板访问不到。 // 直接获取state allState, // 直接获取成员 memberCount,memberObject, // 间接获取 refCount, comCount,readonlyObject, // 操作后获取 addCount,getAddCount,comGetAddCount,filterArray,comFilterArray, // map mapCount, // 设置state setCount } }具体实现方式写成各种函数,可以放在单独的js文件里面。

setup只需要引用进来就行,然后return给模板。

这样代码就不乱了。

可否放飞自我?

对于简单的项目来说,应该可以把state当作大号data来做,负责组件直接的数据传递,不用管是不是状态,是不是可以直接修改的要求。

这个总比自己去研究 Provide / Inject 要好吧,因为这个也是想利用reactive的响应性来实现需求的呀,那么state就是一个reactive,我们何必另起炉灶呢?

另外,以后需求变化了,业务更复杂了,自己想的解决方法就要不断的改进,到最后就会发现,自己又实现了一个vuex。




vuex的修改状态的方式就简单多了,严格的说只有一个,另外还有一个间接的,一个属于漏洞。




直接修改?强烈不建议

因为vuex没有完全把state封装进去,又或者由于js太过开放,无法完全制止随意修改的行为,所以vuex干脆就没堵这个漏洞,只是看我们自觉了。

到处修改有哪些危害呢?

所以要统一修改,统一管理,这样不容易乱套。

mutations

这个是官方推荐的方法,其含义是“突变”,只能用这种方法来修改状态。

我们来写几个简单的例子

mutations: { // 计数器 setCount(state, num = 1) { state.count += num }, // 设置当前时间 setTime(state) { state.myObject.time = '现在时间:' + new Date() }, // 设置数组的值 setArray(state) { state.myArray[1] = 4 } },固定为state,这是默认设置没法改的。

这个是我们可以由外部传入的参数,如果我们需要传入多个参数的话,需要作出对象的形式。

使用方法也很简单:

store.commit('setCount', val)我们还是看看内部结构:

红框是我们调用 mutations 里面的事件的函数。

篮框是我们定义的 mutations 的各种事件。

桔色框是我们定义的state。




那么那个【f】是啥呢?我们再开一层

反正不是正常的函数。




Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:
官方这么说的,我水平有限,大概涉及到了原型链那方面的了。

mutations 必须是同步操作

官网说,如果异步操作的话,devtool 会弄不清楚上下文,我也没搞懂。

反正需要异步的话,可以用action。就不纠结这个事情了。

使用常量代替字符串

mutations 的事件名称是字符串的形式,这个容易写错,那么怎么办呢?我们可以使用常量。

在单独文件里面定义

// mutation-types.jsexport const Set_Count = 'set_count'然后引入这个文件,并且定义事件

import { Set_Count, Set_Count_sy } from './mutation-types.js' mutations: { // 计数器 setCount(state, num = 1) { state.count += num }, [Set_Count](state, num = 1) { state.count += num },调用

// 需要先引入 mutation-types.jsstore.commit(Set_Count_sy)这样你就可以,,,等等,我要是故意写错会给出提示吗?

这个,,,要看你用的编辑器是否支持了。至少我用的这个就没有啥提示,运行的时候,单击按钮才会保存,不执行到那一行,都不带出错的。so。。。

使用symbol

那么是否可以使用ES6新出的symbol呢?

我尝试了一下,报错了。

要么是我的代码有问题,要么是vuex的 mutations 不支持symbol。

action 的用法

由于mutation不支持异步操作,所以vuex又提供了action,这个可以支持异步,在有些情况下就会非常方便。

异步操作分为两种情况,一个是内部的异步操作,一个是外部的异步操作。

比如访问后端API获取数据的时候,又或者把数据存入前端存储的时候。

我们先写个简单的例子

actions: { // 异步获取数组 getArray(context) { console.log('================================') console.log('异步getArray——context', context) setTimeout(() => { context.commit('setArray', new Date().valueOf()) }, 1000) }, // 异步获取数组 getArrayPromise(context) { return new Promise((resolve, reject) => { console.log('================================') console.log('异步 getArrayPromise ——context', context) setTimeout(() => { const time = new Date().valueOf() context.commit('setArray', time) resolve(time) }, 2000) }) }看看参数

获取数据后,通过commit改变状态,然后就完成了,简单方便。

缺点:调用者不知道啥时候加载完毕。

有的时候外部需要明确获知啥时候加载完成了,这样就需要返回一个promise。

我们new 一个 Promise就可以了,里面可以用axios等各种操作。

最后记得返回new出来的实例。

组件里的调用方法

// 异步操作 const actionManame = () => { const store = Vuex.useStore() const getArray = store.dispatch('getArray') console.log('外部调用 getArray', getArray) getArray.then((data) => { console.log('===========') console.log('getArray 异步操作完成,返回数据:', data) console.log('===========') }) const getArrayPromise = store.dispatch('getArrayPromise') console.log('外部调用 getArrayPromise', getArrayPromise) getArrayPromise.then((data) => { console.log('===========') console.log('getArrayPromise 异步操作完成,返回数据:', data) console.log('===========') }) return { getArray, getArrayPromise } } 如果不需要知道确定的完成时间的话,可以直接调用

store.dispatch('getArray') 如果要知道的话,可以写个then

store.dispatch('getArrayPromise').then((data) => { console.log('异步操作完成,返回数据:', data) })这个用法和axios非常相似。

看看运行效果

虽然我们没有返回 promise实例的时候,外部也可以用then,但是这个是默认的,并不是我们真正执行操作后返回的。
我测试了一下,里面的 setTimeout 改成十秒,结果外面的then先执行了,十秒后内部操作完毕,外面没有任何反应。

所以外部调用,想要准确获知完成时间的话,还是要返回一个promise实例才行。

和axios配合使用

我们可以自己new 一个promise把axios包含进去,也可以直接返回axios的实例,比如这样

// axios,返回自己 的 new promise getData(context) { return new Promise((resolve, reject) => { axios.get('demo.json') .then((response) => { const arr = response.data.company.formItem console.log('getData - axios - response', response) // context.commit('reloadArray', response.data.company.formItem) resolve(arr) }) .catch((error) => { console.log('getData - axios - error', error) }) }) }, // axios 返回 axios的promise getData(context) { const ajax = axios.get('demo.json') ajax.then((response) => { const arr = response.data.company.formItem console.log('getData - axios - response', response) // context.commit('reloadArray', response.data.company.formItem) }) .catch((error) => { console.log('getData - axios - error', error) }) return ajax // 直接返回 axios 的promise的实例 }调用方式

store.dispatch('getData').then((data) => { console.log('===========') console.log('getData 异步操作完成,返回数据:', data) console.log('===========') })这样内部和外部都可以得到axios返回的数据。

如何选择就看对返回数据的封装程度了。

module 的用法

一开始以为模块只能是一层,仔细看了官网才发现,原来模块还可以有n层,即子模块、子子模块、子子子模块无穷匮也。而且还可以动态注册模块。这是要疯的节奏呀。

我还是感觉模块应该扁平化设计,最多两层,多了就容易乱套,理解起来也费劲。

定义模块

const user = { // namespaced: true, // 避免重名 state: () => ({ userInfo: { userId: 123, userCode: '', userNick: '' }, userRole: [] }), mutations: { setUser(state, code) { state.userInfo.userCode = code } }, getters: { getUser(state) { return state.userInfo } }, actions: { // { state, commit, rootState } setUsera (store, code) { store.commit('setUser', code) } }}export default user

state

这里有点变化,需要用返回函数的方式,原因和组件里的data的原因类似。这样也就意味着一个模块可以被多次加载进来。

const userInfo = store.state.user.userInfo

getter

简单的说,没啥变化,传递进来的state只包含自己模块的state。

const userInfo2 = store.getters.getUser

mutations

简单的说,也是没啥变化,传递进来的state只包含自己模块的state,另一个参数是组件调用时传递进来的参数。

store.commit('setUser', '要修改的数据')

action

简单的说呢,也是只需要操作自己的模块的就行。

这个参数是一个比较复杂的对象,全部成员如下图,一般可以用结构的方式获取需要的参数,比如这样:{ state, commit, rootState }。

参数详细内容:

可以依据这些内容实现各种功能。

第二个大参数,就是组件调用的时候传递过来的参数。

store.dispatch('setUsera', '要修改的数据')

加载模块到store

模块代码写好了,如何加入进来呢?其实很方便,可以直接写,也可以挂个别名。

export default Vuex.createStore({ state: { count: 0, myObject: { time: '现在的时间:' }, myArray: [1,2,2,3,4] }, getters: {}, mutations: {}, actions: {}, modules: { // 注意:这个myObject会覆盖掉根的myObject。 // 设置命名空间也没有用,还是会覆盖。 // 所以要避免重名。 myObject: user, user: user, person: user }})

命名空间

简单的说,这个是避免命名重复的问题的。

我们可以把user模块加载三次来看看区别。

不加命名空间

模块的**导入名称**会和根的 **根state的成员** 并列,如果重名的话,根的state成员会被覆盖掉。

比如这里根state的成员myObject,就被覆盖掉了。

蓝色框是根的state的成员。

红色框是被覆盖的。

橙色框是两个模块。

会变成数组形式,每个模块的同名都会被调用。

也会变成数组的形式,每个模块的同名action都会被调用

只会保留一个,另外两个会报错。

加上命名空间

state不受影响,其他都会加上“路径”作为区分。




* mutations 由斜线分隔组成的路径,这样可以明确区分是哪个模块的函数,不会弄错。

访问方式:

store.commit('user/setUser', 'user的code')store.commit('myObject/setUser', 'myObject的code')store.commit('person/setUser', 'person的code')可以分别给每个模块的state设置内容,那么其实就是一个模块。

也是由斜线区分。

访问方式:

store.dispatch('user/setUsera', 'action的user的code')访问方式:

store.getters['user/getUser']因为有斜线,所以需要用字符串的方式来访问。

在线演示:




源码:




关键词:使用,方法,基本

74
73
25
news

版权所有© 亿企邦 1997-2025 保留一切法律许可权利。

为了最佳展示效果,本站不支持IE9及以下版本的浏览器,建议您使用谷歌Chrome浏览器。 点击下载Chrome浏览器
关闭