开始尝试做自己的状态管理。
上文说了,我们可以定义一个 reactive 结构的userOnline ,然后注入到根组件里面,那么相关的处理函数要怎么办呢?
我们可以写一个单独的js文件,然后用import的方式引入进来,然后我们就可以做统一的处理了。
js文件如下:
import { provide, inject, reactive, readonly } from 'vue' import sysUserInfo from './symbol' const userState = () => { // 定义标识 // const sysUserInfo = Symbol('userOnlineInfo') // 存放当前登录的用户信息 const userInfo = reactive({ isLogon: false, isOnline: false, userId: 1, userCode: 'jyk', userNike: '海洋', departmentId: 123, departmentName: '', role: [1], power: [1, 2, 3] }) // 在当前组件里面注入 const register = () => { provide(sysUserInfo, userInfo) } // 获取共享的用户状态 const _user = inject(sysUserInfo) // 设置登录用户的信息 const setUser = (user) => { Object.assign(_user, user) } // 返回注入的状态,只读形式 const getUser = () => { return readonly(_user) } // 返回当前用户是否登录 const isLogon = () => { return _user.isLogon } return { // 返回组件内状态 userInfo: readonly(userInfo), // 在组件里注入状态,可以实现子组件共享 register, // 修改状态的函数 setUser, // 获取共享的状态 getUser, // 返回是否登录的状态 isLogon } } export default userState 复制代码
定义一个 reactive的 userInfo,实现响应式
在函数内部定义 userInfo,然后在return的时候加上readonly作为限制,这样可以就以避免误操作而导致直接改变状态。
这里获取状态有两种方式,一个是在注入的组件的获取方式,一个是在子组件获取的方式。
因为在注入的组件似乎不能用 inject 来获取,所以只好直接返回 userInfo。于是就出现了两种获取状态的方式。
使用的时候不要弄混。
如果可以保证在注入的组件里面不会用到状态的话,可以把直接返回状态的给去掉,这样就不会混淆了。
使用 register 方式注入
引入js文件并不会自动注入,而是需要显性使用 register 来注入。
这样可以明确注入的组件,另外也可以避免每个组件都注入一个。
如果不使用 register 的话,那么就是完全的本组件使用。
getUser
获取状态的时候,一个要注意使用 inject ,这样获取的才是共享的状态,否则就是本组件内部的单独的状态了。
另外要加上 readonly,确保是只读状态。 当然如果你就是喜欢直接改变状态,那么也可以不加readonly。
setUser
这里有两个注意点。 一个是要加到 inject 获取出来的状态上,否则就是只能改变本组件的状态,除非你在跟组件改状态。
另一个就是应为用的是 reactive,所以不能直接赋值的方式来修改,但是一个一个属性修改也太麻烦了,所以这里采用 Object.assign(_user, user) 的方式来修改属性。
这是ES6提供的一种方法,用后面的对象的属性,覆盖前面的对象的属性。
注意:这种方法有可能导致增加属性。
使用 Symbol 避免重名
export const sysUserInfo = Symbol('userOnlineInfo') 复制代码
一开始我没把这个写在 单独的js文件里面,但是发现子组件里面读取不出来状态,所以只好把 Symbol 放在单独的js文件里面了。
一开始用他是想彻底堵住直接修改状态的漏洞,但是发现好像还是不行。
所以现在就变成了避免重名、避免“魔术”的功能。
似乎应该用纯大写字母来命名,但是,英语重来没及格过,看纯大写的实在头疼。如果你们喜欢的话,你们可以用。
反正组件里面也看不到。
根组件注入
import userState from './store-nf' setup () { const { userInfo, register } = userState() // 在这个组件里面注入,子组件里面可以共享状态 register() setTimeout(() => { userInfo.userCode = '222222' // 只读状态,不会修改,F12会给出警告。 console.log('userInfo--定时修改', userInfo) }, 500) } 复制代码
子组件调用
import userState from '../store-nf' setup () { const { userInfo, getUser } = userState() // 测试控件内部修改 const test = getUser() test.userNike = '4345555' // 只读,不让改。 // 使用指定的方法修改状态 setUser({ userCode: '22222' }) } 复制代码
基本思路就是这样。 现在这个样子还是比较简陋,大概会有一些不足的地方。
模块化
这样使用本身就变成了一个个独立的模块,或者是一个个独立的管理类。
如果都是在根组件里面注入的话,那么就都是兄弟模块,没有上下级关系。
如果在不同的组件里面注入,那么依赖组件的层级关系来确定上下级的关系。
这样就避免的Vuex里面的模块的命名空间的问题。
不过这样也太分散了,是不是有点不便于统一管理呢?