Vuex_State
安装
npm install vuex --save
使用
- 在src目录下新建store文件,在store中创建store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default store
Vuex 通过store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中。
通过在根实例中注册store选项,该store实例会注入到根组件下的所有子组件中,且子组件能通过this.$store访问。如下。
- store.js
创建一个组件,当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键:在computed中使用mapState,使用不同的变量接收不同的值。
首先要引入
可以简写如下
// 简写
storeCount: 'count', // 等同于 state => state.count
然后在页面中就可以渲染
Vuex_Getter
store的计算属性。getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Getter 接收state作为其第一个参数、getters作为其第二个参数。
在store.js中
getters: {
doubleCount (state) {
return state.count * 2;
}
}
当然我们可以让getter返回一个函数
我们用组件中的computed接收他
在页面就就可以渲染我们想要的结果
也可以通过属性访问
Getter会暴露为store.getters对象:this.$store.getters.doubleCount
getter也拥有他的mapGetters 辅助函数,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:
我们可以给他起个名字方便使用
Vuex_Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
如下我们把想要更改state中的name值在store.js中:
我们在组件中写一个方法,改变name值为当前我们设置的msg
这时我们渲染的值就会更改为我们当前data中设置的msg。
在这里 store.commit 传入的额外参数,即 mutation 的 载荷(payload):大多数情况下载荷为对象如下
mutations: {
showPeople (state, payload) {
state.name += payload.name
}
}
store.commit('showPeople', {
name: 欣欣
})
这时渲染值为原始名字+欣欣
当然我们可以使用mapMutations辅助函数
当然我们在store.js中修改age的值,然后在组建件页面提交后,值会被相应的修改
对象风格的提交方式
提交 mutation 的另一种方式是直接使用包含 type 属性的对象:
store.commit({
type: 'increment',
amount: 10
})
当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变:
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
表单处理
发现一个问题:在Vuex的state上使用v-model时,由于会直接更改state的值,所以Vue会抛出错误。
会报错如下
解决办法:
Vuex 强调的是单向数据库, state 只能被读取,要写入 state 必须靠 mutation ,因此 v-model 无法直接写入 state 。
- 手写v-model的实现
storeName为state传过来的name值
在这里提交修改,onInputName负责接收 input event,呼叫 setName mutations 修改 name state。
- Computed with Setter
这里的name不是data中的而是computed中的
get() :负责从name state 抓数据
set() :负责呼叫 setName mutation 写入 state
当然前提是要提交状态变更
感觉第二种更优雅
Mutation 必须是同步函数
mutations: {
[COUNT_INCREMENT] (state) {
setTimeout(() => {
state.count ++;
}, 1000)
},
}
执行上端代码,我们会发现更改state的操作是在回调函数中执行的,这样会让我们的代码在devtools中变的不好调试:当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用,任何在回调函数中进行的状态的改变都是不可追踪的。
Vuex_Action
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
分发Action
store.dispatch('increment')
支持同样的载荷方式和对象方式进行分发:
// 以载荷形式分发
store.dispatch('increment', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'increment',
amount: 10
})
虽然和mutation差不多,但是在action中,可以执行异步操作,但是mutation中不行!!!
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
在组件中分发 Action
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
// `mapActions` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}
}
组合 Action
Action 通常是异步的,那么如何知道 action 什么时候结束呢?
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
store.dispatch('actionA').then(() => {
// ...
})
Vuex 管理模式
Vuex_Module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter。
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
获取 state:this.$store.state.moduleName.xxx
获取 getter:this.$store.getters.xxx
提交 mutation:this.$store.commit(‘xxx’);
分发 action:this.$store.dispatch(‘xxx’);
可以通过mapXXX的方式拿到getters、mutations、actions,但是不能拿到state,如果想通过这种方式获得state,需要加命名空间。
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。例如:
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']
}
}
}
}
}
})