目录
安装vuex与简单使用
使用命令:npm install vuex
安装vuex.。
初始化vuex
引入vuex –> 声明使用vuex –> 创建Vuex.Store实例(创建一个store容器) –> 把store容器全局注册到vue上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import Vue from 'vue' import Vuex from 'vuex'//引入vuex import App from './App.vue' Vue.use(Vuex);//声明使用vuex //创建Vuex.Store实例,也即是创建一个store容器 const store = new Vuex.Store({ //state用于定义初始值,需要存储到vuex的数据都必须要到这里定义初始值 state:{ count:0 }, //mutations用于定义修改state的值的方法,state的数据不能被直接修改 //定义的方法,必有两个参数, //第一个是固定的state对象,靠它来操作state的数据 //第二个是需要修改的数据 mutations:{ updateCount(state,num){ state.count = num; } } }); new Vue({ el:"#root", store,//把store容器全局注册到vue上 render:(h) => h(App) }); |
简单使用vuex
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<template> <div id="app"> {{count}}//输出computed的count()方法的值 </div> </template> <script> export default { name: "App", mounted(){//挂载完成就执行以下动作 console.log(this.$store)//this.$store获取vuex的store实例 let i = 0; setInterval(()=>{ this.$store.state.count = i++;//更改state的count字段的值 },1000) }, computed:{//监听值的变化 count(){ return this.$store.state.count;//取得state的count字段的值 } } } </script> <style scoped> </style> |
合理布局文件夹
在根目录的src文件夹下创建名为:store的文件夹,用于存放vuex的文件。
创建store.js文件,作为vuex的入口文件,用于实例vuex的store:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import Vuex from 'vuex'//引入vuex //使用方法的方式出实例Vuex.Store,避免内存溢出问题 export default ()=>{ return new Vuex.Store({ state:{ count:0 }, mutations:{ updateCount(state,num){ state.count = num; } } }); }; |
在vue入口文件main.js,定义使用vuex:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import Vue from 'vue' import Vuex from 'vuex'//引入vuex import App from './App.vue' import createStore from './store/store'//引入vuex的入口 Vue.use(Vuex);//声明使用Vuex const store = createStore();//创建vuex.store实例 new Vue({ el:"#root", store,//vuex.store注册到vue全局 render:(h) => h(App) }); |
还可以进一步分离,把state和mutations单独分出去。
在src/store文件夹下,创建名为state的文件夹,这个文件夹用于存放state的文件:
1 2 3 4 |
//在vuex需要用到的字段一定要在state中声明好 export default { count:0 } |
在src/store文件夹下,创建名为mutations的文件夹,这个文件夹用于存放mutations的文件:
1 2 3 4 5 6 |
//mutations用于定义修改state数据的方法 export default { updateCount(state,num){ state.count = num; } } |
最后在vuex的入口文件中引入它们:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import Vuex from 'vuex' //为什么不写state,而是defaultSate,因为方便更换state文件 import defaultSate from './state/state'//引入state import mutations from './mutations/mutations'//引入mutations export default ()=>{ return new Vuex.Store({ state:defaultSate, mutations }); }; |
Vuex简介
vuex是什么?
vuex就是一个状态(数据)管理工具,每一个vuex都有一个store(仓库),store是一个容器,它包含着你的应用中大部分的状态(state)。Vuex 和单纯的全局对象有以下两点不同:
- 1.Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 2.你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交(commit) mutations。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
Vuex 和单纯的全局对象有以下两点不同:
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Vue.use(Vuex); const store = new Vuex.Store({ // 数据状态 state {...}, // 更改状态 store.commit mutations: {...}, // 类似于mutation(不能直接变更状态,可以异步操作) store.dispatch actions: {...}, // 派生状态(如,过滤、计数) getters: {...} }) // 将状态从根组件“注入”到每一个子组件中,且子组件能通过 this.$store 访问到。 const app = new Vue({ el: '#app', store, data() {} }); |
核心概念 state
单一状态树
Vuex 使用 单一状态树 —— 是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个『唯一数据源(SSOT)』而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
单状态树和模块化并不冲突 —— 在后面的章节里我们会讨论如何将状态和状态变更事件分布到各个子模块中。
在组件中获取state状态
由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。
1 2 3 4 5 |
computed:{ msg(){ return store.state.msg } }, |
因为我们在根实例已经注册了store,所以我们也可以使用这种写法
1 2 3 4 5 |
computed:{ msg(){ return this.$store.state.msg } }, |
这样我们在模块化的构建系统中,在每个需要使用 state 的组件中就不需要频繁地导入,并且在测试组件时不需要模拟状态。
mapState 辅助函数
当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState
辅助函数帮助我们生成计算属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import { mapState } from 'vuex' computed: mapState({ // 映射 this.count 为 store.state.count 'count', // 箭头函数可使代码更简练 count: state => state.count, // 传字符串参数 'count' 等同于 `state => state.count` countAlias: 'count', // 为了能够使用 `this` 获取局部状态,必须使用常规函数 countPlusLocalState (state) { return state.count + this.localCount }, // 使用对象展开运算符将此对象混入到外部对象中 ...mapState({ // ... }) }) |
对象展开运算符
mapState 函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?
对象展开运算符可以将mapstate与局部计算属性混合使用
1 2 3 4 5 6 7 |
computed: { localComputed () { /* ... */ }, // 使用对象展开运算符将此对象混入到外部对象中 ...mapState({ // ... }) } |
组件仍然保有局部状态
使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。
核心概念 mutations
mutation 必须是同步函数!!!
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
state的数据可以直接修改,但不推荐
虽然在组件上可以通过this.$store.state.字段=值
来直接修改store的state里的值,但是官方并不推荐这么做。使用vuex是为了更好的管理数据,而不是简单的修改值。通过mutations修改state,可以追踪到state的修改,而不是像直接修改state数据那样,无声无息。
禁止直接修改state的值
禁止直接修改state的值,仅且只能通过mutations来修改state的数据,那么,只需在vuex实例store的时候开启严格模式即可:
1 2 3 |
new Vuex.Store({ strict:true,//开启严格模式,使用this.$store.state.字段=值,直接修改state会报错 }); |
严格模式只推荐在开发环境下开启(用作规范vuex代码),不推荐正式环境使用,所以,应该区别开启:
1 2 3 4 5 6 7 8 9 10 |
import Vuex from 'vuex' const isDev = process.env.NODE_ENV === "development";//判断当前是否开发环境 export default ()=>{ return new Vuex.Store({ strict:isDev,//根据环境自动开启或关闭严格模式 }); }; |
mutations的使用
Vuex 中的 mutations 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler),这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。调用 store.commit(type, payload)
方法来触发mutations中的相关方法。
1 2 3 4 5 6 7 |
mutations: { increment (state, n) { state.count += n } } store.commit('increment', 10) |
提交载荷(Payload)
你可以向 store.commit 传入额外的参数,即 mutation 的 载荷(payload):
1 2 3 4 5 |
mutations: { increment (state, n) { state.msg += n } } |
对象风格的提交方式
提交 mutation 的另一种方式是直接使用包含 type 属性的对象:
1 2 3 4 |
store.commit({ type: 'increment', amount: 10 }) |
当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变:
1 2 3 4 5 |
mutations: { increment (state, payload) { state.msg += payload.amount } } |
核心概念 getters
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var store = new Vuex.store({ // 数据放在state中 state:{ msg:1 }, // 新建方法 mutations:{ // es6 写法 jia(state){ state.msg++ } }, getters:{ jiajia(state){ state.msg+5 } } }) |
mapGetters
同样,vuex也为我们定义了mapGetters
1 2 3 4 5 6 |
computed: { // 使用对象展开运算符将 getters 混入 computed 对象中 ...mapGetters([ 'jiajia', ]) } |
核心概念 actions
actions的目的与mutations是一样的,只是actions是异步的,actions依赖于mutations已定义的方法。
actions使用的步骤:
mutations里定义方法 –> actions里定义方法(方法的第一个参数一定是state),并在方法里面使用commit方法调用mutations里定义的方法,state.commit('mutations里定义的方法名',需要传递的自定义参数)
–> 在组件里使用dispatch触发actions,this.$store.dispatch('Actions定义的方法名',需要传递的自定义参数)
。
让我们来使用一个简单的 action:
前提:
mutations:
1 2 3 4 5 |
export default { updateCount(state,num){ state.count = num; } } |
state:
1 2 3 |
export default { count:0, } |
开始:
定义一个actions:
1 2 3 4 5 6 7 |
export default { updateContentAsync(state,data){//自定义方法,第一个参数必定是state setInterval(()=>{ state.commit('updateCount',data.num)//使用commit调用mutations的方法 },data.time) } } |
把actions注册到vuex:
1 2 3 4 5 6 7 8 9 10 |
import Vuex from 'vuex' import actions from './actions/actions'//引入actions export default ()=>{ return new Vuex.Store({ actions//注册actions }); }; |
使用actions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<template> <div id="app"> {{count}} <!--这个跟actions定义的方法里commit的mutations的方法相关state字段有关,与actions的方法名无关--> </div> </template> <script> export default { name: "App", mounted(){ this.$store.dispatch('updateContentAsync',{num:2,time:3000})//触发actions,其实是调用了mutations } } </script> <style scoped> </style> |
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
- 通过
store.dispatch
方法触发
组合 Action:store.dispatch
可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch
仍旧返回 Promise。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
actions: { actionA ({ commit }) { return new Promise((resolve, reject) => { setTimeout(() => { commit('someMutation') resolve() }, 1000) }) } } //现在你可以: store.dispatch('actionA').then(() => { // ... }) |
辅助函数:…mapState …mapMutations …mapGetters …mapActions
…mapState
原来是这样写的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<template> <div id="app"> {{count}}<!--computed的count方法--> </div> </template> <script> export default { name: "App", computed:{//原来是在computed中通过定义一个方法并返回在store.state取得的对应的字段 count(){ return this.$store.state.count; } } } </script> <style scoped> </style> |
使用辅助函数…mapState代替:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<template> <div id="app"> {{count}} </div> </template> <script> import {mapState} from 'vuex'//引入mapState export default { name: "App", computed:{ ...mapState(['count']), //state的字段名与computed自定义的方法名一致就这样写 } } </script> <style scoped> </style> |
如果state的字段名与computed自定义的方法名 不一致 就这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<template> <div id="app"> {{counter}}<!--computed的方法名--> </div> </template> <script> import {mapState} from 'vuex'//引入mapState export default { name: "App", computed:{ //state的字段名与computed自定义的方法名 不一致 就这样写 ...mapState({ counter:'count'//counter是自定义的方法名,count是state里的字段 }) } } </script> <style scoped> </style> |
还有一种写法,以为基本上和原来的写法差不多,所以不太常用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<template> <div id="app"> {{counter}}<!--computed的方法名--> </div> </template> <script> import {mapState} from 'vuex'//引入mapState export default { name: "App", computed:{ //state的字段名与computed自定义的方法名 不一致 就这样写 ...mapState({ counter:(state)=>state.count//counter是自定义的方法名,count是state里的字段 }) } } </script> <style scoped> </style> |
扩展运算符…mapState,意思是把vuex的state数据映射到计算属性里面,映射后的名字为count(或者counter),所以,就不用写this.$store.state.count这个长串了。
…mapGetters
…mapGetters与 …mapState的用法是一样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<template> <div id="app"> {{fullName}} </div> </template> <script> import {mapGetters} from 'vuex'//引入mapGetters export default { name: "App", computed:{ ...mapGetters(['fullName']), //fullName是getters里定义的方法名 } } </script> <style scoped> </style> |
…mapMutations
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<template> <div id="app"> {{count}} </div> </template> <script> import {mapMutations} from 'vuex'//引入mapMutations export default { name: "App", mounted(){ let i = 0; setInterval(()=>{ //this.$store.commit('updateCount',i++)//原本是通过commit方法调用mutations里的方法 this.updateCount(i++)//直接调用方法名,并传参数 },1000) }, methods:{ ...mapMutations(['updateCount']),//参数是mutations的方法名,有多个可以写多个 } } </script> <style scoped> </style> |
…mapActions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<template> <div id="app"> {{count}} </div> </template> <script> import {mapActions} from 'vuex'//引入mapActions export default { name: "App", mounted(){ this.updateContentAsync({num:2,time:3000});//直接调用actions的方法,并传参数 }, methods:{ ...mapActions(['updateContentAsync'])//参数是actions的方法名 } } </script> <style scoped> </style> |
总结
mapState与mapGetters的使用方法是差不多的,都先在computed里进行解析。
mapMutations与mapActions的使用方法是差不多的,都先在methods里进行解析。然后直接使用this.方法名(参数)
进行使用
模块化:Module
转载于:https://www.jianshu.com/p/e6805c819abb
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
const moduleA = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... } } const moduleB = { state: { ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } }) store.state.a // -> moduleA 的状态 store.state.b // -> moduleB 的状态 |
模块的局部状态
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const moduleA = { state: { count: 0 }, mutations: { increment (state) { // 这里的 `state` 对象是模块的局部状态 state.count++ } }, getters: { doubleCount (state) { return state.count * 2 } } } |
同样,对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState:
1 2 3 4 5 6 7 8 9 10 |
const moduleA = { // ... actions: { incrementIfOddOnRootSum ({ state, commit, rootState }) { if ((state.count + rootState.count) % 2 === 1) { commit('increment') } } } } |
对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:
1 2 3 4 5 6 7 8 |
const moduleA = { // ... getters: { sumWithRootCount (state, getters, rootState) { return state.count + rootState.count } } } |
命名空间
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为命名空间模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
const store = new Vuex.Store({ modules: { account: { namespaced: true, // 模块内容(module assets) state: { ... }, // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响 getters: { isAdmin () { ... } // -> getters['account/isAdmin'] }, actions: { login () { ... } // -> dispatch('account/login') }, mutations: { login () { ... } // -> commit('account/login') }, // 嵌套模块 modules: { // 继承父模块的命名空间 myPage: { state: { ... }, getters: { profile () { ... } // -> getters['account/profile'] } }, // 进一步嵌套命名空间 posts: { namespaced: true, state: { ... }, getters: { popular () { ... } // -> getters['account/posts/popular'] } } } } } }) |
启用了命名空间的 getter 和 action 会收到局部化的 getter,dispatch 和 commit。换言之,你在使用模块内容(module assets)时不需要在同一模块内额外添加空间名前缀。更改 namespaced 属性后不需要修改模块内的代码。
在命名空间模块内访问全局内容(Global Assets)
如果你希望使用全局 state 和 getter,rootState 和 rootGetter 会作为第三和第四参数传入 getter,也会通过 context 对象的属性传入 action。
若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatch 或 commit 即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
modules: { foo: { namespaced: true, getters: { // 在这个模块的 getter 中,`getters` 被局部化了 // 你可以使用 getter 的第四个参数来调用 `rootGetters` someGetter (state, getters, rootState, rootGetters) { getters.someOtherGetter // -> 'foo/someOtherGetter' rootGetters.someOtherGetter // -> 'someOtherGetter' }, someOtherGetter: state => { ... } }, actions: { // 在这个模块中, dispatch 和 commit 也被局部化了 // 他们可以接受 `root` 属性以访问根 dispatch 或 commit someAction ({ dispatch, commit, getters, rootGetters }) { getters.someGetter // -> 'foo/someGetter' rootGetters.someGetter // -> 'someGetter' dispatch('someOtherAction') // -> 'foo/someOtherAction' dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction' commit('someMutation') // -> 'foo/someMutation' commit('someMutation', null, { root: true }) // -> 'someMutation' }, someOtherAction (ctx, payload) { ... } } } } |
带命名空间的绑定函数
当使用 mapState, mapGetters, mapActions 和 mapMutations 这些函数来绑定命名空间模块时,写起来可能比较繁琐:
1 2 3 4 5 6 7 8 9 10 11 12 |
computed: { ...mapState({ a: state => state.some.nested.module.a, b: state => state.some.nested.module.b }) }, methods: { ...mapActions([ 'some/nested/module/foo', 'some/nested/module/bar' ]) } |
对于这种情况,你可以将模块的空间名称字符串作为第一个参数传递给上述函数,这样所有绑定都会自动将该模块作为上下文。于是上面的例子可以简化为:
1 2 3 4 5 6 7 8 9 10 11 12 |
computed: { ...mapState('some/nested/module', { a: state => state.a, b: state => state.b }) }, methods: { ...mapActions('some/nested/module', [ 'foo', 'bar' ]) } |
而且,你可以通过使用 createNamespacedHelpers 创建基于某个命名空间辅助函数。它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import { createNamespacedHelpers } from 'vuex' const { mapState, mapActions } = createNamespacedHelpers('some/nested/module') export default { computed: { // 在 `some/nested/module` 中查找 ...mapState({ a: state => state.a, b: state => state.b }) }, methods: { // 在 `some/nested/module` 中查找 ...mapActions([ 'foo', 'bar' ]) } } |
模块动态注册
在 store 创建之后,你可以使用store.registerModule
方法注册模块:
1 2 3 4 5 6 7 8 |
// 注册模块 `myModule` store.registerModule('myModule', { // ... }) // 注册嵌套模块 `nested/myModule` store.registerModule(['nested', 'myModule'], { // ... }) |
之后就可以通过store.state.myModule
和store.state.nested.myModule
访问模块的状态。
模块动态注册功能使得其他 Vue 插件可以通过在 store 中附加新模块的方式来使用 Vuex 管理状态。例如,vuex-router-sync
插件就是通过动态注册模块将 vue-router 和 vuex 结合在一起,实现应用的路由状态管理。
你也可以使用store.unregisterModule(moduleName)
来动态卸载模块。注意,你不能使用此方法卸载静态模块(即创建 store 时声明的模块)。
在注册一个新 module 时,你很有可能想保留过去的 state,例如从一个服务端渲染的应用保留 state。你可以通过preserveState
选项将其归档:store.registerModule('a', module, { preserveState: true })
。
模块重用
有时我们可能需要创建一个模块的多个实例,例如:
- 创建多个 store,他们公用同一个模块 (例如当
runInNewContext
选项是false
或'once'
时,为了在服务端渲染中避免有状态的单例) - 在一个 store 中多次注册同一个模块
如果我们使用一个纯对象来声明模块的状态,那么这个状态对象会通过引用被共享,导致状态对象被修改时 store 或模块间数据互相污染的问题。
实际上这和 Vue 组件内的data
是同样的问题。因此解决办法也是相同的——使用一个函数来声明模块状态(仅 2.3.0+ 支持):
1 2 3 4 5 6 7 8 |
const MyReusableModule = { state () { return { foo: 'bar' } }, // mutation, action 和 getter 等等... } |
Vuex之热重载
使用webpack的热模替换(HMR)插件(Hot Module Replacement API)
Vuex支持在开发过程中热重载mutation、module、action和getter
对于mutation和模块,需要使用store.hotUpdate()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
// 导入vuex模块 import Vuex from 'vuex' // 导入自己写的store的状态文件 import defaultState from './state/state.js' import mutations from './mutations/mutations.js' import getters from './getters/getters.js' import actions from './actions/actions.js' const isDev = process.env.NODE_ENV === 'development' //创建store变量 export default () => { const store = new Vuex.Store({ strict: isDev, state: defaultState, mutations: mutations, getters, actions }) //此处开始定义热更新 if (module.hot) { module.hot.accept([ './state/state.js', './mutations/mutations.js', './getters/getters.js', './actions/actions.js' ], () => { //1.获取更新后的模块 //因为babel6的模块编译格式问题,这里需要加上.default const newState = require('./state/state.js').default const newMutations = require('./mutations/mutations.js').default const newGetters = require('./getters/getters.js').default const newActions = require('./actions/actions.js').default //2.加载新模块 store.hotUpdate({ state: newState, mutations: newMutations, getters: newGetters, actions: newActions }) }) } return store } |
表单处理
当在严格模式中使用 Vuex 时,在属于 Vuex 的 state 上使用 v-model 会比较棘手:
1 |
<input v-model="obj.message"> |
假设这里的 obj
是在计算属性中返回的一个属于 Vuex store 的对象,在用户输入时,v-model
会试图直接修改 obj.message
在严格模式中,由于这个修改不是在 mutation 函数中执行的, 这里会抛出一个错误
用 “Vuex 的思维” 去解决这个问题的方法是:
- 给
< input>
中绑定 value - 然后侦听
input
或者change
事件,在事件回调中调用 action:
1 |
<input :value="message" @input="updateMessage"> |
1 2 3 4 5 6 7 8 9 10 11 |
// ... computed: { ...mapState({ message: state => state.obj.message }) }, methods: { updateMessage (e) { this.$store.commit('updateMessage', e.target.value) } } |
1 2 3 4 5 |
mutations: { updateMessage (state, message) { state.obj.message = message } } |
双向绑定的计算属性:
1 |
<input v-model="message"> |
1 2 3 4 5 6 7 8 9 10 |
computed: { message: { get () { return this.$store.state.obj.message }, set (value) { this.$store.commit('updateMessage', value) } } } |
vuex的一些api
subscribe
subscribe(handler: Function): Function
1 2 3 4 5 |
mutation被调用时 store.subscribe((mutation,state)=>{ console.log(mutation.type) console.log(mutation.payload) }) |
订阅 store 的 mutation。handler 会在每个 mutation 完成后调用,接收 mutation 和经过 mutation 后的状态作为参数:
1 2 3 4 |
store.subscribe((mutation, state) => { console.log(mutation.type) console.log(mutation.payload) }) |
要停止订阅,调用此方法返回的函数即可停止订阅。
通常用于插件。
subscribeAction
subscribeAction(handler: Function): Function
1 2 3 4 5 |
action被调用时 store.subscribeAction((action,state)=>{ console.log(action.type) console.log(action.payload) }) |
2.5.0 新增
订阅 store 的 action。handler 会在每个 action 分发的时候调用并接收 action 描述和当前的 store 的 state 这两个参数:
1 2 3 4 |
store.subscribeAction((action, state) => { console.log(action.type) console.log(action.payload) }) |
要停止订阅,调用此方法返回的函数即可停止订阅。
3.1.0 新增
从 3.1.0 起,subscribeAction 也可以指定订阅处理函数的被调用时机应该在一个 action 分发之前还是之后 (默认行为是之前):
1 2 3 4 5 6 7 8 |
store.subscribeAction({ before: (action, state) => { console.log(`before action ${action.type}`) }, after: (action, state) => { console.log(`after action ${action.type}`) } }) |
该功能常用于插件。
watch
watch(fn: Function, callback: Function, options?: Object): Function
响应式地侦听 fn 的返回值,当值改变时调用回调函数。fn 接收 store 的 state 作为第一个参数,其 getter 作为第二个参数。最后接收一个可选的对象参数表示 Vue 的 vm.$watch 方法的参数。
要停止侦听,调用此方法返回的函数即可停止侦听。
1 2 3 4 |
相当于getter store.watch((state)=>state.count+1,(newCount)=>{ console.log('new count watched , '+newCount) }) |