vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
vuex 安装
npm i vuex
1 vuex核心概念
vuex是存储组件数据的集中式的仓库(如何优雅的读写仓库中的数据)
-
state:仓库内存数据的配置
-
getters:仓库内的计算属性
-
action:操作数据的工具
-
mutation:操作数据的工具
仓库
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,
若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。 - 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地
提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,
从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
2 state-基本环境配置
创建文件store,文件store.js 引入并暴露
2.1 将vuex注册为vue的插件
在一个模块化的打包系统中,您必须显式地通过 Vue.use()
来安装 Vuex:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
注意
上述步骤必须在创建仓库之前完成!
2.2 新建仓库
Vuex 通过new Vuex.Store({配置对象})来新建一个仓库
仓库的所有数据保存在配置对象的state属性中
// 如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex)
// 在src目录下新建store目录 再新建index.js文件来存放仓库
import Vuex from "vuex"
const store = new Vuex.Store({
state: {
count: 0
}
})
export default store
2.3 注册仓库
Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex)):main.js
import store from './store'
new Vue({
store, //import一个文件,该文件内的代码会被执行,暴露出来的内容也会被拿到
})
2.4 访问仓库数据
由于上一步把 store 的实例注入了所有的子组件 所以任何组件都可以通过其自身的$store来访问仓库 在component新建count.vue
<template>
<div>
{{count}} // 没有响应式
//{{$store.state.count}} 有响应式 若被input直接使用,会出现脏数据
</div>
</template>
<script type="text/ecmascript-6">
export default {
name: "App",
data(){
return {
count:this.$store.state.count
}
}
}
</script>
注意:
- 仓库内部的state和getters都会转绑给仓库对象
- Vue.prototype. s t o r e : 代 表 的 就 是 仓 库 对 象 , 任 意 一 个 组 件 都 可 以 通 过 store:代表的就是仓库对象,任意一个组件都可以通过 store:代表的就是仓库对象,任意一个组件都可以通过srore这个属性;来访问到这个仓库{{$store.state.count}}
- 仓库中的数据也是响应式的
- 仓库中的数据也不能直接在组件上读取,任意产生脏数据,应该使用计算属性读取仓库中的数据
computed:{
count:{
get() {
return this.$store.state.count
},
set(newCount) {
console.log(newCount)
}
}
}
在整个组价的改造过程中,我们发现是需要修改计数器的值,
当我们通过 this.$store.state.count++
这种方式去修改store中的数据时发现仓库数据变了
可是界面上的数据没有更新. 在对仓库数据进行修改时 我们必须遵守 Vue 的响应规则
2.5 严格模式
开启严格模式,仅需在创建 store 的时候传入 strict: true
:
const store = new Vuex.Store({
// ...
strict: true
})
在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。
这能保证所有的状态变更都能被调试工具跟踪到。
3 Mutation-修改数据
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
Vuex 中的 mutation 非常类似于事件:
① 每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
② 这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数
3.1 注册Mutation
const store = new Vuex.Store({
strict: true,
state:{
count:0
}, //修改仓库数据的工具
mutations:{
add(state){
state.count++
},
}
})
export default store
3.2 使用mutation
不能直接调用一个 mutation handler。
这个选项更像是事件注册:“当触发一个类型为 increment
的 mutation 时,调用此函数。”
要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:
store.commit('add')
3.3 提交载荷(Payload)
你可以向 store.commit
传入额外的参数,即 mutation 的 载荷(payload):
mutations: {
add (state, step) {
state.count += step
}
}
methods:{
add() { this.$store.commit("add",2)}
}
在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:
mutations:{
add(state,{step,msg}) {
state.count += step
}
}
add() {this.$store.commit("add",{step:2,msg:"hello"})}
3.4 对象风格的提交方式
提交 mutation 的另一种方式是直接使用包含 type
属性的对象:
add() {
this.$store.commit({
type:"add",
step:2,
msg:"hello"
})
}
当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变:
mutations:{
add(state,{step,msg}) {
state.count += step
}
3.5 Mutation 需遵守 Vue 的响应规则
既然 Vuex 的 store 中的状态是响应式的,那么当我们变更状态时,监视状态的 Vue 组件也会自动更新。这也意味着 Vuex 中的 mutation 也需要与使用 Vue 一样遵守一些注意事项:
-
最好提前在你的 store 中初始化好所有所需属性。
-
当需要在对象上添加新属性时,你应该
- 使用
Vue.set(obj, 'newProp', 123)
注意:
Mutation 必须是同步函数
vue 的 devtool 中会记录每一条 mutation 日志。如果mutation中存在异步代码,会导致devtools中的记录变的混乱不可维护, 当我们在mutation中书写异步代码时,严格模式下vue会提出警告,非严格模式下vue不会做出任何反应
4 Action-操作数据
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
4.1 使用
让我们来注册一个简单的 action:
//进行同步注册
mutations:{
asyncAdd(state,step) {
state.count += step
}
},
//再异步修改仓库数据
actions:{
asyncAdd(store,step){
//store: 仓库对象
setTimeout(()=>{
store.commit("asyncAdd",step)
},2000)
}
},
4.2 分发 Action
Action 通过 store.dispatch
方法触发使用:
<button @click.stop="asyncAdd">aysnc btn(2000)</button> //会有冒泡,因此用stop阻止
methods:{
asyncAdd(){
// this.$store.commit("asyncAdd",3)
this.$store.dispatch("asyncAdd",3)
}
}
Actions 支持同样的载荷方式和对象方式进行分发:
- 以载荷形式分发:store.dispatch(‘asyncAdd’,{step:3}
- 以对象形式分发
- map辅助
5 Getter-计算属性
有时候我们需要从 store 中的 state 中派生出一些状态.如果有多个组件需要用到此状态
就可以在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,
getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Getter 接受 state 作为其第一个参数:
// 仓库文件
getters: {
flag(state) {
return state.count%2===0 ? "偶":"奇"
}
},
访问Getter
Getter 会暴露为 store.getters
对象,你可以以属性的形式访问这些值
computed: {
flag(){
return this.$store.getters.flag
}
}
6 辅助函数
import { mapState,mapGetters,mapMutations } from 'vuex'
简化业务组件的开发
- mapState({}) 或者 mapState([])
- mapGetters({}) 或者 mapGetters([])
- mapMutations({}) 或者 mapMutations([])
- mapActions({}) 或者 mapActions([])
6.1 一般规律
- vuex中的state 我们在组件中使用时通常需要有对应的computed
- vuex中的getters 我们在组件中使用时通常需要有对应的computed
- vuex中的mutations 我们在组件中使用时通常需要有对应的methods
- vuex中的actions 我们在组件中使用时通常需要有对应的methods
6.2 mapState用法
mapState(["count" ]) ==> 返回 {count(){return this.$store.state.count}}
mapState({
myCount:"count" ==> 返回 {myCount(){return this.$store.state.count}}
})
...mapState(["count"])
6.3 mapGetters用法
mapGetters(["flag"]) ==> return this.$store.getters.flag
...mapGetters([flag]) //...表示把里面里面的数据展开显示
6.4 mapActions用法
...mapActions(["add","asyncAdd"])
传参可以在点击事件之后进行
<div class="count" @click="add(2)">
{{count}}
<button @click.stop="asyncAdd(3)">异步的函数</button>
</div>
7 仓库中的数据读取到界面上(拿计算属性对应)
1. 仓库内部的state getters配置都会转绑给仓库对象
2. Vue.prototype.$store : 代表的就是仓库对象
任意一个组件都可以通过 $store 这个属性来访问到仓库
3. 仓库中的数据也是响应式的
4. 仓库中的数据也不能直接在组件上读取;任意产生脏数据
我们应该使用计算属性来读取仓库中数据
8 如何优雅的修改仓库中的数据
同步修改:
组件上转发action
action内部提交mutation
mutation内部同步的修改数据
界面得到更新
异步修改:
组件上转发action
action内部处理异步逻辑;在异步完成时提交mutation
mutation内部同步的修改数据
界面得到更新
9 组件上写法的优化
mapState,mapGetters, 结合 ...运算符在vue的computed配置中结合使用
computed:{
... mapState(["count"])
... mapGetters(["count"])
}
mapActions,mapMutations 结合 ...运算符在vue的methods配置中结合使用
methods:{
... mapMutations(["count"])
... mapActions(["count"])
}
传参: 在模板上使用方法时直接传参
mapState(["count"])
{
count:function(){
return this.$store.state.count
}
}
mapState({myCount:count})
{
myCount:function(){
return this.$store.state.count
}
}
mapActions(["add"])
{
add:function(payload){
this.$store.dispatch("add",payload)
}
}
mapActions({myAdd:add}})
{
myAdd:function(payload){
this.$store.dispatch("add",payload)
}
}
10 vuex模块
mapState([ ]),mapState([{}) :当前这种使用形式,默认读取总仓库中的数据
如何将仓库分模块使用
① 定义分仓库:
export default new Vuex.Store({
//vuex模块
modules:{
a
}
}
export default {
state:{test:"a模块中的test"},
getters:{},
mutations:{},
actions:{}
}
注册分仓库
② 如何读取总仓库的数据
mapState({key:()=>{}}); mapState中的参数是一个对象 {key:函数}
函数的返回值才是真正要被渲染的数据!
当前函数的参数是总仓库中的state
...mapState(["test"]),
③ 如何修改分仓库的数据
分仓库中的所有state数据是注册在当前模块下的;得通过 总仓库中的state.模块名.数据名 来访问
…mapState({组件上使用时的计算属性的名称:state=>state.模块名.数据名})
...mapState({testFromA:state=>state.a.test}),
④ 如何修改分仓库总仓库的数据
在总仓库中出现的所有 state都代表是总仓库自己的state
在分仓库中出现的所有 state都代表是分仓库自己的state
不管是总仓库中mutation,actions;getters 还是分仓库中的mutation,actions;getters 都是定义在全局的!
所以修改分仓库的数据 还是 按照总仓库的模式进行处理!
state:{
test:"总仓库中的test"
},
mutations:{
updateTest(state){
state.test = "总仓库中的 copy test"
}
},
actions:{
updateTest(store){
store.commit("updateTest")
}
},
//在分仓库中出现的所有 state都代表是分仓库自己的state
export default {
state:{
test:"a模块中的test"
},
getters:{
getDataFromA(state){
return state.test+"-getters"
}
},
mutations:{
updateATest(state){
state.test = "a模块中的 copy test"
}
},
actions:{
updateATest(store){
store.commit("updateATest")
}
}
}
注意:
- mapState([“state中数据的名称”]);mapState({key:“state中数据的名称”});
- mapState中的参数是一个数组 或者 对象 {key:string}的时候
- 默认读取的都是总仓库中的数据;
什么 样的数据应该进仓库?
多组件共享的数据都要放仓库,请求相关的数据也要放仓库
11 小练习
store.js npm i vuex 配置 暴露 拆分组件 新建文件并往外暴露对象
main.js注册vuex配置
state.js 把所有(list search)data中的数据放入到这里 辅助函数
search.js username需要修改,辅助做不到,因此自己写(get set)在actios mutations中进行编写
flag的修改同username actions修改稿数据后记得触发(methods)
list.vue 总线不要了 发送请求在actions中,数据都在仓库中
在search中发送请求,点击search才有的数据
【】拿到的是变量,不是字符串
store.commit("updateATest")
}
}
}
**注意:**
- mapState(["state中数据的名称"]);mapState({key:"state中数据的名称"});
- mapState中的参数是一个数组 或者 对象 {key:string}的时候
- 默认读取的都是总仓库中的数据;
什么 样的数据应该进仓库?
多组件共享的数据都要放仓库,请求相关的数据也要放仓库
# 11 小练习
store.js npm i vuex 配置 暴露 拆分组件 新建文件并往外暴露对象
main.js注册vuex配置
state.js 把所有(list search)data中的数据放入到这里 辅助函数
search.js username需要修改,辅助做不到,因此自己写(get set)在actios mutations中进行编写
flag的修改同username actions修改稿数据后记得触发(methods)
list.vue 总线不要了 发送请求在actions中,数据都在仓库中
在search中发送请求,点击search才有的数据
【】拿到的是变量,不是字符串