Vuex 基本概念

参照官网整理总结vuex语法。

计划日期:
Vuex基础部分:2022年2月20日——2022年2月28日
Vuex源码相关实践:待定
Vuex拓展:待定
写完后,会发到仓库地址:待定

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式

  • state,驱动应用的数据源;
  • view,以声明方式将 state 映射到视图;
  • actions,响应在 view 上的用户输入导致的状态变化。
vuex-16454369395901.png

store——包含以上概念的容器

view——以声明方式将 state 映射到视图

state 状态、数据

getters:当state的数据变更时,getters的值也发生变化(相当于vuex中state的计算属性)

mutations 更改状态的函数

actions 异步操作

Modules 模块。为了将store的状态进行分割,每个模块都有属于自己的state、mutation、action、getter

Vuex V3.0

基础代码:

import Vue from 'vue'
import Vuex from 'vuex'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
Vue.use(Vuex)//使用vuex
// 将 store 实例作为插件安装
const store = new Vuex.Store({
  /*状态、数据 存储 */
  state: {
    count: 0
  },
  /* 行为,更改状态的函数  */
  mutations: {
    increment(state) {
      state.count++
    }
  }
})
new Vue({
  router,
  store,// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
  render: function (h) {
    return h(App)
  }
}).$mount('#app')

state——状态、数据

状态、数据 **单一状态树 **作为一个“唯一数据源 (SSOT (opens new window))”而存在。

操作:

读取state中数据
this.$store.state.count
变更state中的数据
//使用commit调用mutations内的increment方法
this.$store.commit("increment")

mapState辅助函数

计算属性显示数据

作用:当获取多个状态减少申明多个状态的重复和冗余,使用辅助函数来生成计算属性。

main.js

import Vue from 'vue'
import Vuex from 'vuex'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
// 将 store 实例作为插件安装
Vue.use(Vuex)
const store = new Vuex.Store({
  /*状态、数据 存储 */
  state: {
    count: 0,
    c: 1,
  },
  /* 行为,更改状态的函数  */
  mutations: {
    increment(state) {
      state.count++
    }
  }
})
new Vue({
  router,
  store,
  render: function (h) {
    return h(App)
  }
}).$mount('#app')

home.vue

<template>
  <div>
    首页
    <p>{{ count }}</p>
    <p>{{ countPlusLocalState }}</p>
    <p>{{ countAlias }}</p>
  </div>
</template>
<script>
import { mapState } from "vuex";
export default {
  //import引入的组件需要注入到对象中才能使用
  components: {},
  data() {
    //这里存放数据
    return {
      localCount: 123,
    };
  },
  //监听属性 类似于data概念
  computed: mapState({
    // 箭头函数可使代码更简练
    count: (state) => state.count,
    // 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: "count",
    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState(state) {
      return state.count + this.localCount;
    },
  }),
  // computed: mapState(["count"]),
  //监控data中的数据变化
  watch: {},
  //方法集合
  methods: {
    increment() {
      /* 获取state内的数据值 */
      this.$store.commit("increment");
      console.log(this.$store.state.count);
    },
  },
};
</script>
image-20220220161503081.png
简写形式

当计算属性名称和state的数据名称相同可以简写成数组形式

computed: mapState(["count"]),

页面内调用:

{{count}}
image-20220220161704874.png
展开mapstate对象方便调用
<template>
  <div>
    首页
    <p>{{ count }}</p>
    <p>{{ c }}</p>
    <!-- <p>{{ countPlusLocalState }}</p> -->
    <!-- <p>{{ countAlias }}</p> -->
  </div>
</template>
<script>
computed: {
    //写法1
    ...mapState(["count", "c"]),
    //写法2
    ...mapState({
      count: "count",
      c: "c",
    }),
},
</script>

Getter——store内的计算属性

当state内的数据变化后,getter内的数据也随之变化

属性访问

Getter(state,getters)

state——(必填)返回state状态数据内的值

示例:`state.count++`

getters——(可选)调用getters内的方法

示例:`getters.todolist.length`

完整示例:

vuex书写:

const store = new Vuex.Store({
  /*状态、数据 存储 */
  state: {
    /* state练习 */
    count: 0,
    c: 1,
    /* getters练习 */
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  /* 行为,更改状态的函数  */
  mutations: {
    /* state练习 */
    increment(state) {
      state.count++
    }
  },
  getters: {
    doneTodos: (state) => {
      // 返回todo.done为true的列表项
      return state.todos.filter(todo => todo.done)
    },
    // 返回完整的列表
    todolist: (state) => {
      return state.todos;
    },
    // 接受其他getters作为第2个参数
    doneTodosCount(state, getters) {
      console.log(getters.todolist)
      return getters.todolist.length
    }
  }
})

vue页面显示数据

<template>
  <div>
    首页
    <p>为true的列表:{{ doneTodos }}</p>
    <p>完整的列表项:{{ todolist }}</p>
    <p>完整的列表长度:{{ doneTodosCount }}</p>
  </div>
</template>
<script>
    export default {
      computed: {
        // 返回todos列表
        doneTodos() {
          return this.$store.getters.doneTodos;
        },
        todolist() {
          return this.$store.getters.todolist;
        },
        // 返回列表长度
        doneTodosCount() {
          return this.$store.getters.doneTodosCount;
        },
      },
    }
</script>

结果显示:

image-20220221171937666.png

方法访问

通过给getters方法传递参数,从而实现返回想要的数据内容

vuex

  /*状态、数据 存储 */
state: {
    //...
    /* getters练习 */
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
},  
getters: {
    //...
    //给getters方法传递参数
    getTodoById: (state) => (id) => {
      return state.todos.find(todo => todo.id === id)
    }
}

调用

<template>
  <div>
    <h3>通过方法访问</h3>
    <p>返回指定id的列表项:{{ gettodo(2) }}</p>
  </div>
</template>
<script>
    //需要写在methods方法内,前面写在computed计算属性内
  methods: {
    /* 返回指定id的列表项 */
    gettodo(id) {
      return this.$store.getters.getTodoById(id);
    },
    //...
  },
</script>

输出:

image-20220221172922503.png

mapGetters辅助函数

注:只能映射到计算属性中,无法映射到方法中

写法与mapState类似

原本写法:

  computed: {
    // 返回todos列表
    doneTodos() {
      return this.$store.getters.doneTodos;
    },
    todolist() {
      return this.$store.getters.todolist;
    },
    // 返回列表长度
    doneTodosCount() {
      return this.$store.getters.doneTodosCount;
    },
  },

使用mapGetter写法:

写法1

import { mapGetter } from "vuex";
  computed: {
    ...mapGetters(["doneTodos", "todolist", "doneTodosCount"]),
  },

写法2

import { mapGetters } from "vuex";
  computed: {
    ...mapGetters({
      doneTodos: "doneTodos",
      todolist: "todolist",
      doneTodosCount: "doneTodosCount",
    }),
  },

输出结果:

image-20220221173855234.png

mutations——更改state状态的函数

Vuex 的 store 中的状态的唯一方法是提交 mutation

基本格式:

type(state,mutation)

  • type:(事件类型)——命名在mutations中的函数名称

    示例:

      increment (state, n) {
        state.count += n
      }
    

    increment 为事件类型

  • state:【必选】(数据)——store中存储的数据

  • mutation:【可选】(荷载)——调用事件类型时传递给事件的参数。

    示例:store.commit('increment', 10) 最后的10为参数事件类型为increment

完整示例:

store

const store = new Vuex.Store({
  /*状态、数据 存储 */
  state: {
    //...
    /* mutation练习 */
    mut: 1
  },
  /* 行为,更改状态的函数  */
  mutations: {
    //...
    /* mutation练习 */
    incrementdata(state, n) {
      state.mut += n.sum
    }
  },
  getters: {
  }
})

mutation.vue

<template>
  <div class="">mutation获取值:{{ mudata }}</div>
</template>
<script>
export default {
  components: {},
  data() {
    return {
      mudata: "",
    };
  },
  methods: {
    runmutation() {
      this.$store.commit({
        type: "incrementdata",
        sum: 1,
      });
      console.log(this.$store.state);
      this.mudata = this.$store.state.mut;
    },
  },
  mounted() {
    this.runmutation();
  },
};
</script>

结果:


image-20220222225315304.png

Mutation 必须是同步函数,异步的话导致事件无法追踪(原因在于mutation直接变更的是State(状态)内的数据)

mapMutations 辅助函数

mapGettersmapState类似

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`

      // `mapMutations` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
    ]),
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
}

使用常量替代 Mutation 事件类型

const SOME_MUTATION = 'SOME_MUTATION'
import { createStore } from 'vuex'

const store = createStore({
  state: { ... },
  mutations: {
    // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
    [SOME_MUTATION] (state) {
      // 修改 state
    }
  }
})

示例:

const CHANGE_NAME = 'CHANGE_NAME'
mutations: {
    [CHANGE_NAME](state, data) {
        state.name = data
    }
},

Action 行为

参数概要

目的是为了处理mutation中的异步操作,原因在于,action提交的是mutation,而mutation直接变更的是state(状态数据)内的值

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

接收1个参数

第一个参数 返回store内所有的对象(所以可以用解构赋值的方式获取指定store内的对象)

打印increment (context) context的数据结果

image-20220223153726896.png

写法1.

  actions: {
    increment (context) {
        setTimeout(() => {
          context.commit('increment')
        }, 1000)
    }
  }

写法2.

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

调用action

store.dispatch('increment')

异步调用action

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

载荷形式分发(同mutation)

// 以载荷形式分发
store.dispatch('incrementAsync', {
  amount: 10
})

// 以对象形式分发
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

mapActions调用方式

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)

state: {
    //...
    /* action练习 */
    act: 1,
},
mutations: {
    //...
    /* action练习 */
    action(state) {
        console.log(state.act)
        state.act++
        console.log('返回存储的值:' + state.act)
    }
},  
actions: {
    //...
    action(context) {
      console.log(context);
      context.commit('action')
    },
    /* action异步分发 */
    actionA(con) {
      return new Promise(
        (resolve, reject) => {
          setTimeout(function () {
            con.commit('action')
            console.log('actionA:' + con.state.act)
            resolve()
          }, 1000)
        }
      )
    },
    actionB(con) {
      // 异步的形式调用2次action
      return con.dispatch('actionA').then(() => {
        con.commit('action')
        console.log('actionB:' + con.state.act)
      })
    },
    /* async await包裹异步函数写法 */
    async actionA(con) {
      // commit触发mutations中的action行为导致act+1,
      //dispatch调用action中的action,再次触发了mutations中的action行为导致act+1,
      con.commit('action', await con.dispatch('action'))//相当于异步+2
      console.log('actionB调用=>A:' + con.state.act);//3
    },
    async actionB(con) {
      await con.dispatch('actionA');//3
    }
  }

界面内调用:

this.$store.dispatch("actionB");
image-20220223171202724.png

用async await 写成同步形式

    /* async await包裹异步函数写法 */
    async actionA(con) {
      // commit触发mutations中的action行为导致act+1,
      //dispatch调用action中的action,再次触发了mutations中的action行为导致act+1,
      con.commit('action', await con.dispatch('action'))//相当于异步+2
      console.log('actionB调用=>A:' + con.state.act);//3
    },
    async actionB(con) {
      await con.dispatch('actionA');//3
    }
image-20220223171237219.png

module 模块分割

Vuex 允许我们将 store 分割成模块(module)

多模块注册及基本使用

思路分别写2个goods和users2个不同的模块,最后注册引入到实例中

完整的store对象【全部代码】

/* 用module之后 */
const store = new Vuex.Store({
  /* 全局状态 */
  state: {
    name: 'ccc'
  },
  /* 全局状态变更 */
  mutations: {},
  /* 全局行为 */
  actions: {},
  /* 全局状态返回类似计算属性 */
  getters: {},
  modules: {
    goods,
    // users,//注册模块的方式进行注册
    /* 多模块公用 */
    goodslist: {
      namespaced: true,//开启命名空间
      /* 公用相同的模型 */
      modules: {
        goods1: goods,
        goods2: goods,
      }
    }
  }
})

goods模型

const goods = {
    namespaced: true,
    // 普通的纯对象形式申明,这个状态对象会通过引用被共享,则当多模型共用的时候数据也会发生改变【模型重用】
    // state: {
    //     name: '默认goods',
    // },
    // 函数形式每个模型都是单独不会同时改变【模型重用】
    state() {
        return {
            name: '默认goods',
        }
    },
    // 更改状态
    mutations: {
        GOODS(state, data) {
            state.name = data
        }
    },
    // 行为
    actions: {
        change_name(state, data) {
            state.commit('GOODS', data)
        }
    },
    // 计算属性
    getters: {
        getname: (state) => {
            return state.name + ' ' + state.name
        }
    },

}
export default goods;

users模型

const users = {
    namespaced: true,
    state: {
        name: '默认users',
    },
    // 更改状态
    mutations: {
        [CHANGE_NAME](state, data) {
            state.name = data
        }
    },
    // 行为
    actions: {
        change_name(state, data) {
            state.dispatch('CHANGE_NAME', data)
        }
    },
    // 计算属性
    getters: {
        getname: (state) => {
            return state.name
        }
    },

}
export default users;

界面调用module.vue

<template>
  <div class="">
    <h2>模型1:goods</h2>
    <p>当前名称:{{ goods }}</p>
    <p>getter获取的当前名称:{{ getname }}</p>
    商品名称:<input type="text" v-model="goodsname" />
    <input type="button" @click="changegoodsname" value="修改商品名称" />
    <h2>多模型共用</h2>
    <p>goods模型1 name值:{{ goodslist1 }}</p>
    <p>goods模型2 name值:{{ goodslist2 }}</p>
    <h3>注册嵌套模块</h3>
    <p>goods模型3 name值:{{ goodslist3 }}</p>
    <h2>模型2:users</h2>
    <p>当前的名称:{{ users }}</p>
  </div>
</template>
<script>
import { mapGetters, mapState, mapActions, mapMutations } from "vuex";
export default {
  //import引入的组件需要注入到对象中才能使用
  components: {},
  data() {
    //这里存放数据
    return { goodsname: "123" };
  },
  //监听属性 类似于data概念
  computed: {
    ...mapState({
      goods: (state) => {
        return state.goods.name;
      },
      users: (state) => {
        return state.users.name;
      },
      goodslist1: (state) => {
        return state.goodslist.goods1.name;
      },
      goodslist2: (state) => {
        return state.goodslist.goods2.name;
      },
      goodslist3: (state) => {
        return state.goodslist.goods2.name;
      },
    }),
    ...mapGetters("goods", {
      getname: "getname",
    }),
  },
  //监控data中的数据变化
  watch: {},
  //方法集合
  methods: {
    /* 用mutation同步修改值 */
    // ...mapMutations("goods", {
    //   changegoods: "GOODS",
    // }),
    /* 用action异步修改值 */
    ...mapActions("goods", {
      actionchangegoods: "change_name",
    }),
    changegoodsname() {
      // this.changegoods(this.goodsname);
      this.actionchangegoods(this.goodsname);
    },
  },
  //生命周期 - 创建完成(可以访问当前this实例)
  created() {},
  //生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {
  },
};
</script>

模块动态注册【保留state】

// 注册模块 `myModule`
//{ preserveState: true }使模块中的state无法被覆写,保持原样【保留原始state】,注册的模块state无法写入
store.registerModule('users', users, { preserveState: false })
// 注册嵌套模块 `goodslist/myModule`(前面的goodslist必须存在的情况下才能进行嵌套)
store.registerModule(['goodslist', 'goods3'], goods, { preserveState: false })

模块重用

前面完整代码中的一部分goods1和goods2模块重用的方式写入

goods3注册模块的方式写入。用的也是同一个模块

/* 用module之后 */
const store = new Vuex.Store({
  //...
  modules: {
    goods,
    // users,//注册模块的方式进行注册
    /* 多模块公用 */
    goodslist: {
      namespaced: true,//开启命名空间
      /* 公用相同的模型 */
      modules: {
        goods1: goods,
        goods2: goods,
      }
    }
  }
  //...
})
// 注册嵌套模块 `goodslist/myModule`(前面的goodslist必须存在的情况下才能进行嵌套)
store.registerModule(['goodslist', 'goods3'], goods, { preserveState: false })

结果展示

点击按钮,修改上方商品名称内容

image-20220225105654561.png

在线链接【需翻墙】https://codesandbox.io/s/wonderful-night-ew7uj2?file=/src/views/module1.vue

Vuex V4.0

State(不变)

Getter(不变)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容

  • 一、什么时候使用Vuex 如果应用比较简单,就不需要使用Vuex,直接使用父子组件传值及及它传值方式即可,使用Vu...
    懒懒猫阅读 316评论 0 0
  • 本文为转载,原文:Vue学习笔记进阶篇——vuex核心概念 前言 本文将继续上一篇 vuex文章 ,来详细解读一下...
    ChainZhang阅读 1,649评论 0 13
  • State 单一状态树 Vuex使用单一状态树——用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据...
    oWSQo阅读 1,091评论 0 0
  • 1.State Vuex 使用单一状态树,每个应用将仅仅包含一个 store 实例。 在 Vue 组件中获得 Vu...
    angelwgh阅读 398评论 0 0
  • 介绍: 本文主要介绍vuex 中五个核心概念的基本用法,帮助对于不太看懂官方文档的同学 前提: 1. 熟悉vu...
    拉面的无聊时光阅读 652评论 0 1