We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在开始之前,我表明一下自己的观点:vuex 很优秀,我很认同 vuex 的设计理念,我只是想实现一个用起来更简单的 vuex
只要用过Vue的前端,基本没有人没接触过 Vuex,vuex的 api 简洁明了,功能简单但强大,上手也是很快的。
vuex 的数据流大致是这样的
state -> vue component -> action -> mutation -> state | ^ |_________or___________|
这个数据流很清晰,而且职责分的很明确,如果完全按照这个官方标准去编码,项目维护性可变得很高。
但是,不知道有没有开发者感觉到,编写 action 和 mutation 有些繁琐,因为本来可以一步到位的工作,就因为mutation的限制,不合适做异步、调用action等等原因,不得不分成两步去实现。
mobx 示例
import { observable, computed, action } from 'mobx'; class Counter { @observable num = 0 @computed get numPlus() { return this.num + 1 } @action plus() { this.num++ } @action reset() { this.num = 0 } @action async delayPlus() { return new Promise(resolve => { setTimeout(() => { runInAction(() => { this.num++ }) resolve() }, 500) }) } }
在使用 mobx 的时候,我被它那种简洁写法吸引了,没有mutation,只是在需要更改状态的函数用 mobx 的工具函数包装一下,很好的控制了状态变更的范围。在代码简洁方面,比 vuex 做的更好。
习惯了mobx这种写法后,在我看来, vuex 的 mutation 根本就是多余的 。
在 vuex 的文档中,提到了严格模式, 如果状态变更不是有mutation函数引起的,将会抛出错误。
在 action 中其实也是可以直接修改状态的,并且修改后依然是响应式的,对应视图依然会更新,只是 vuex 强烈不建议这么做,不然也不会有严格模式出现。
mutation 和 action 的调用,需要使用 dispatch 调用action, 使用commit 调用 mutation,而且参数只能有一个,这个其实能理解,vuex 推崇提交一个更新的 payload,而不是多个payload。这个理念是不错的,但是用起来有时候还是不方便。
在 vue 组件中有 watch,能监听到状态变化后自动触发,这个在 vue 组件这么普遍的功能,在 vuex 居然没有,所以要监听 vuex 状态变化,只能通过在组件作为载体去做 watch 功能。
上面写了使用 vuex 的一些不太舒服的方面点。我该着重解决他们了
vuex 已经被大众所接受,如果我的更改会导致原本 vuex api 不兼容,那很难在原本项目中迁移过去,所以vuex原本的 api 不能动,而是要在原本 api 上扩展。
所以我的做法就是,原本注入到 Vue 组件中的 $store 变量不动,没有对它有任何的侵入或者 hack,而是利用 $store 现有的api,重新构建一个类似 $store 的变量,我取名为 $s。而这次改造我大量使用 Object.defineProperties 去代理原本的 state、action、mutation 等等, 所以我给这个项目命名为 vuex-proxy
$store
$s
Object.defineProperties
vuex-proxy
vuex-proxy 就是我基于以上的所述的优化,我直接引用了原本的readme过来
Vuex Proxy that mean Vuex Proxy vue 的增强组件,基于 vuex,让 vuex 更简单 使用方法 首先,vuex 那整套完全兼容,所以可以从 vuex 无缝迁移到 vuex-proxy,但是反之不行,因为 vuex-proxy 在 vuex 的api上有扩展 API 在 vue 组件实例中,增加了一个 $s 属性,这是 vuex-proxy store,事实上这是一个 vuex store 的代理,目的就是为了简化 vuex 使用,当然,原本 vuex 注入的 $store 依然有效 注意在定义 store 的 state,actions,getters,mutation 时,不要和这些 api 名字重复了 vuex-proxy store 格式 在组件内使用 this.$s 访问 store 定义 store 的定义和vuex完全兼容, { // 完全兼容 vuex 的 store 定义 namespaced: true, state: { list: [], total: 0, }, modules: {}, // 但是 actions 和 mutations 的作用变得平等,没有区别,并且this指向当前 vuex proxy store,详见下文 actions: {}, mutations: {}, // 新增 api,在state发生变化的时候,触发函数 watch: { list(newValue, oldValue) { console.log('list change:', oldValue, ' => ', newValue) } }, } this.$s.$store 原始的 vuex store,没有任何侵入和 hack this.$s.$rootVM 挂载 store 最顶层的组件实例 this.$s.$root 最顶层 vuex-proxy store 对象 this.$s.$registerModule 动态注册新模块,参数和功能与 vuex 的 registerModule 基本一致 this.$s.$unregisterModule 动态删除模块,参数和功能与 vuex 的 unregisterModule 基本一致 this.$s.$state 该模块级别的 vuex store 状态数据 this.$s[moduleName] 模块级别的 vuex-proxy store,api 和根 vuex-proxy store 无区别,只是状态数据不一样 this.$s[fieldName] fieldName 是指 state,getters,actions,mutation 里面的所有字段名,vuex-p 把所有的状态、计算属性、方法都放到了同一层级里面,当你访问 vuex-proxy 的数据时,内部是知道你访问的是 state,还是getters,,还是 actions ,是一个 module,所以这也要求 state,getters,actions,mutation 里面的字段不能有重复,如果有重复则在初始化的时候会报错误 示例 import Vue from 'vue' import vuexProxy from 'vuex-proxy' // 使用插件 Vue.use(vuexProxy) new Vue({ // 在根组件使用 store 属性定义 vuex-proxy store,vuexp store 的 api 和 vuex store 完全兼容,说明请看下文 store: { // store state 状态数据,和 vuex state 完全一致,无任何变化 state: { num: 0, }, // store getters 计算属性,和 vuex state 完全一致,无任何变化 getters: { numPlus: state => state + 1 }, // watch 与 vue 的 watch 相似,当 state 变化后触发,支持 state 和 getters 的监听 watch: { num: 'consoleNum', // 值可以是字符串,表示 action 或 mutation 的函数名 numPlus(newV, oldV) { // 值可以是函数 console.log('num change:', oldV, ' => ', newV) }, }, actions: { // 第一种 action 写法,和 vuex state 完全一致,无任何变化,在组件调用的时候,也没有区别,使用 this.$store.dispatch('reset') // 注意:该写法 // this 指向 vuex store // 第一个参数是 vuex 的固定格式 { dispatch, commit, getters, state, rootGetters, rootState } // 第二个参数是 action 参数 // 只有两个参数,不支持更多参数 reset({commit}) { commit('RESET_NUM') }, // 第二种 action 写法,增强版本,在组件调用的时候,使用 this.$s.plus() // this 指向 vuex-proxy store // 参数无限个数,可在里面直接更改 state,把它当做 vuex mutation 来用,支持异步,注意异步函数里的 this 是指向的 vuex-proxy store就没问题了 plus() { return ++this.num }, setNum(n) { // 在action 函数内部,可以访问 state console.log(this.num) // 也可以访问 getters 计算属性 console.log(this.numPlus) // 也可以调用其他 action 和 mutation this.plus() // 也可以修改 state this.num = n }, consoleNum() { console.log(this.num) } }, mutations: { // 第一种 mutations 写法,和 vuex state 完全一致 RESET_NUM(state) { state.num = 0 } // 第二种 mutations 写法,和第二种 action 写法没有区别,用法也没有区别 resetNum() { this.num = 0 } }, // 嵌套模块,支持无限嵌套 modules: { testMod: { state: { test: 100 }, getters: {}, actions: {}, } } }, data() { return { name: 'my name is vue plus' } } // 映射到计算属性中,用 $computed,完全兼容原本 vue 组件的 computed 功能 // 使用字符串数组形式,直接写key,多层级直接使用 . 或者 / 分隔,最终映射的key名字是最后一层的key,并且自动绑定了 get 和 set,也就是可以直接给绑定的对象赋值 $computed: ['num', 'numPlus', 'testMod.test'], mounted() { this.num = 2 // 相当于 this.$s.num = 2 this.test = 20 // 相当于 this.$s.testMod.test = 20 }, // 使用对象形式 // this 指向组件实例 $computed: { num: 'num', // 会自动绑定 get 和 set xnum: { get($s) { return $s.num } // get 函数只有一个参数,该参数为 vuex-proxy store 实例,也就是 this.$s set(n, $s) { return $s.num = n } // set 函数有两个参数,第一个是修改后的值,第二个是 this.$s }, numPlus() { // 这算是 get 函数 return this.$s.num }, myname() { // 还可以访问组件内部 data return this.name }, }, // 绑定 actions 和 mutations 到组件实例中 // 字符串数组形式,根据key名字自动映射,映射后函数的this指向为函数所在的层级的 vuex-proxy store 实例 $methods: ['plus', 'setNum'], $methods: { plus: 'plus', setNum(n) { return this.$s.setNum(n) }, sayMyName() { console.log(this.myname) } }, watch: { // 这样监听值改变,api 无变化 '$s.num': function(oldv, newv) { console.log(this.newv) } } }) 对比 与 Vuex 对比,api 变化 目的:不破坏 vuex 前提下,让 vuex 变得更简单,更强大 兼容性 Vuex 的原有功能一切正常,可以无缝的将 vuex 迁移到 vuex-proxy 为什么要改变 vuex vuex 是 vue 官方指定并维护的状态管理插件,和 vue 的结合无疑非常好的,但是在我看来在使用vuex的时候,有一些让我不舒服的地方 actions 和 mutations 的参数,只能有一个,我理解初衷其实是为了只有一个 payload,更好记录,调试,跟踪变更等等,但是却不好用 mutations 的存在,我觉得就是多余的,明明可以直接改状态,为什么还要多包装一层呢。我觉得有几个原因: 2.1 方便调试工具的 Time Revel,redo,undo,变化跟踪等等。但是相信我,这些功能你基本不会用得上的,调试工具最大的作用就是用来看当前状态数据。 2.2 隔离 actions 的副作用,让状态变更更好跟踪和调试。但是实际上用的时候,我基本上不会去调试 mutation 函数 在组件调用的时候,必须要用 dispatch 或 commit 去调用,为什么呢,直接调用不是更好吗 变化点 初始化时,new vues.Store 是可选的,可以 new vuex.Store 再传入,也可以直接传入,内部自动识别 actions 和 mutations 兼容原有的,并且支持不同写法 state,getters,modules 没有变更 vue开发工具依然可用,不过每次更改状态都会有一个名为 VUEXP_CHANGE_STATE 的 type vuex 生态的插件都可以继续使用 新增 watch api,监听 state 和 getters 变化,和 vue 组件的 watch 功能类似 简单地说,就是原有的 vuex 的功能都没有阉割,没有改变,只是增加了其他用法,使其变得使用更简单
that mean Vuex Proxy
vue 的增强组件,基于 vuex,让 vuex 更简单
首先,vuex 那整套完全兼容,所以可以从 vuex 无缝迁移到 vuex-proxy,但是反之不行,因为 vuex-proxy 在 vuex 的api上有扩展
在 vue 组件实例中,增加了一个 $s 属性,这是 vuex-proxy store,事实上这是一个 vuex store 的代理,目的就是为了简化 vuex 使用,当然,原本 vuex 注入的 $store 依然有效
vuex-proxy store
vuex store
vuex
注意在定义 store 的 state,actions,getters,mutation 时,不要和这些 api 名字重复了
vuex-proxy store 格式
在组件内使用 this.$s 访问
this.$s
store 的定义和vuex完全兼容,
{ // 完全兼容 vuex 的 store 定义 namespaced: true, state: { list: [], total: 0, }, modules: {}, // 但是 actions 和 mutations 的作用变得平等,没有区别,并且this指向当前 vuex proxy store,详见下文 actions: {}, mutations: {}, // 新增 api,在state发生变化的时候,触发函数 watch: { list(newValue, oldValue) { console.log('list change:', oldValue, ' => ', newValue) } }, }
this.$s.$store
原始的 vuex store,没有任何侵入和 hack
this.$s.$rootVM
挂载 store 最顶层的组件实例
this.$s.$root
最顶层 vuex-proxy store 对象
this.$s.$registerModule
动态注册新模块,参数和功能与 vuex 的 registerModule 基本一致
this.$s.$unregisterModule
动态删除模块,参数和功能与 vuex 的 unregisterModule 基本一致
this.$s.$state
该模块级别的 vuex store 状态数据
this.$s[moduleName]
模块级别的 vuex-proxy store,api 和根 vuex-proxy store 无区别,只是状态数据不一样
this.$s[fieldName]
fieldName 是指 state,getters,actions,mutation 里面的所有字段名,vuex-p 把所有的状态、计算属性、方法都放到了同一层级里面,当你访问 vuex-proxy 的数据时,内部是知道你访问的是 state,还是getters,,还是 actions ,是一个 module,所以这也要求 state,getters,actions,mutation 里面的字段不能有重复,如果有重复则在初始化的时候会报错误
import Vue from 'vue' import vuexProxy from 'vuex-proxy' // 使用插件 Vue.use(vuexProxy) new Vue({ // 在根组件使用 store 属性定义 vuex-proxy store,vuexp store 的 api 和 vuex store 完全兼容,说明请看下文 store: { // store state 状态数据,和 vuex state 完全一致,无任何变化 state: { num: 0, }, // store getters 计算属性,和 vuex state 完全一致,无任何变化 getters: { numPlus: state => state + 1 }, // watch 与 vue 的 watch 相似,当 state 变化后触发,支持 state 和 getters 的监听 watch: { num: 'consoleNum', // 值可以是字符串,表示 action 或 mutation 的函数名 numPlus(newV, oldV) { // 值可以是函数 console.log('num change:', oldV, ' => ', newV) }, }, actions: { // 第一种 action 写法,和 vuex state 完全一致,无任何变化,在组件调用的时候,也没有区别,使用 this.$store.dispatch('reset') // 注意:该写法 // this 指向 vuex store // 第一个参数是 vuex 的固定格式 { dispatch, commit, getters, state, rootGetters, rootState } // 第二个参数是 action 参数 // 只有两个参数,不支持更多参数 reset({commit}) { commit('RESET_NUM') }, // 第二种 action 写法,增强版本,在组件调用的时候,使用 this.$s.plus() // this 指向 vuex-proxy store // 参数无限个数,可在里面直接更改 state,把它当做 vuex mutation 来用,支持异步,注意异步函数里的 this 是指向的 vuex-proxy store就没问题了 plus() { return ++this.num }, setNum(n) { // 在action 函数内部,可以访问 state console.log(this.num) // 也可以访问 getters 计算属性 console.log(this.numPlus) // 也可以调用其他 action 和 mutation this.plus() // 也可以修改 state this.num = n }, consoleNum() { console.log(this.num) } }, mutations: { // 第一种 mutations 写法,和 vuex state 完全一致 RESET_NUM(state) { state.num = 0 } // 第二种 mutations 写法,和第二种 action 写法没有区别,用法也没有区别 resetNum() { this.num = 0 } }, // 嵌套模块,支持无限嵌套 modules: { testMod: { state: { test: 100 }, getters: {}, actions: {}, } } }, data() { return { name: 'my name is vue plus' } } // 映射到计算属性中,用 $computed,完全兼容原本 vue 组件的 computed 功能 // 使用字符串数组形式,直接写key,多层级直接使用 . 或者 / 分隔,最终映射的key名字是最后一层的key,并且自动绑定了 get 和 set,也就是可以直接给绑定的对象赋值 $computed: ['num', 'numPlus', 'testMod.test'], mounted() { this.num = 2 // 相当于 this.$s.num = 2 this.test = 20 // 相当于 this.$s.testMod.test = 20 }, // 使用对象形式 // this 指向组件实例 $computed: { num: 'num', // 会自动绑定 get 和 set xnum: { get($s) { return $s.num } // get 函数只有一个参数,该参数为 vuex-proxy store 实例,也就是 this.$s set(n, $s) { return $s.num = n } // set 函数有两个参数,第一个是修改后的值,第二个是 this.$s }, numPlus() { // 这算是 get 函数 return this.$s.num }, myname() { // 还可以访问组件内部 data return this.name }, }, // 绑定 actions 和 mutations 到组件实例中 // 字符串数组形式,根据key名字自动映射,映射后函数的this指向为函数所在的层级的 vuex-proxy store 实例 $methods: ['plus', 'setNum'], $methods: { plus: 'plus', setNum(n) { return this.$s.setNum(n) }, sayMyName() { console.log(this.myname) } }, watch: { // 这样监听值改变,api 无变化 '$s.num': function(oldv, newv) { console.log(this.newv) } } })
目的:不破坏 vuex 前提下,让 vuex 变得更简单,更强大
Vuex 的原有功能一切正常,可以无缝的将 vuex 迁移到 vuex-proxy
vuex 是 vue 官方指定并维护的状态管理插件,和 vue 的结合无疑非常好的,但是在我看来在使用vuex的时候,有一些让我不舒服的地方
VUEXP_CHANGE_STATE
简单地说,就是原有的 vuex 的功能都没有阉割,没有改变,只是增加了其他用法,使其变得使用更简单
The text was updated successfully, but these errors were encountered:
还是mobx好使,vuex一开始接触到看到是一整个树上,还是感觉挺好的。但是使用起来发现是没有mobx好用的,平常用的都是mobx-state-tree,真的很方便。但是vuex,和你有同感,就是感觉变得繁琐了。
Sorry, something went wrong.
@SouWinds mobx 集成 vue 有点难搞,我曾经尝试过,可以看看这个项目,后面有些bug实在修不好,就改变思路对 vuex 下手了
No branches or pull requests
改变 Vuex 的原因
在开始之前,我表明一下自己的观点:vuex 很优秀,我很认同 vuex 的设计理念,我只是想实现一个用起来更简单的 vuex
只要用过Vue的前端,基本没有人没接触过 Vuex,vuex的 api 简洁明了,功能简单但强大,上手也是很快的。
vuex 的数据流大致是这样的
这个数据流很清晰,而且职责分的很明确,如果完全按照这个官方标准去编码,项目维护性可变得很高。
mutation
但是,不知道有没有开发者感觉到,编写 action 和 mutation 有些繁琐,因为本来可以一步到位的工作,就因为mutation的限制,不合适做异步、调用action等等原因,不得不分成两步去实现。
mobx 示例
在使用 mobx 的时候,我被它那种简洁写法吸引了,没有mutation,只是在需要更改状态的函数用 mobx 的工具函数包装一下,很好的控制了状态变更的范围。在代码简洁方面,比 vuex 做的更好。
习惯了mobx这种写法后,在我看来, vuex 的 mutation 根本就是多余的 。
在 vuex 的文档中,提到了严格模式, 如果状态变更不是有mutation函数引起的,将会抛出错误。
在 action 中其实也是可以直接修改状态的,并且修改后依然是响应式的,对应视图依然会更新,只是 vuex 强烈不建议这么做,不然也不会有严格模式出现。
dispatch & commit
mutation 和 action 的调用,需要使用 dispatch 调用action, 使用commit 调用 mutation,而且参数只能有一个,这个其实能理解,vuex 推崇提交一个更新的 payload,而不是多个payload。这个理念是不错的,但是用起来有时候还是不方便。
watch
在 vue 组件中有 watch,能监听到状态变化后自动触发,这个在 vue 组件这么普遍的功能,在 vuex 居然没有,所以要监听 vuex 状态变化,只能通过在组件作为载体去做 watch 功能。
放飞自我
上面写了使用 vuex 的一些不太舒服的方面点。我该着重解决他们了
改造前思考
vuex 已经被大众所接受,如果我的更改会导致原本 vuex api 不兼容,那很难在原本项目中迁移过去,所以vuex原本的 api 不能动,而是要在原本 api 上扩展。
所以我的做法就是,原本注入到 Vue 组件中的
$store
变量不动,没有对它有任何的侵入或者 hack,而是利用 $store 现有的api,重新构建一个类似$store
的变量,我取名为$s
。而这次改造我大量使用Object.defineProperties
去代理原本的 state、action、mutation 等等, 所以我给这个项目命名为vuex-proxy
改造结果
vuex-proxy
就是我基于以上的所述的优化,我直接引用了原本的readme过来The text was updated successfully, but these errors were encountered: