
🌈个人主页:前端青山
🔥系列专栏:Vue篇
🔖人终将被年少不可得之物困其一生
依旧青山,本期给大家带来vue篇专栏内容:vue-vuex详解
目录
4.4为什么 Vuex 的 mutation 中不能做异步操作?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
其具有以下优势:
能够在vuex中集中管理共享的数据,便于开发和后期进行维护
能够高效的实现组件之间的数据共享,提高开发效率(代码量)
存储在vuex中的数据是响应式的,当数据发生改变时,页面中的数据也会同步更新
什么样的数据适合存储在Vuex中?
一般情况下,只有组件之间共享的数据才有必要存储到vuex中,对于组件中私有的数据依旧存储在组件自身的data中即可。
注意事项:由于vue项目一刷新就会重新编译,因此每次刷新都会重新初始化vuex中的数据,会导致之前的数据丢失,因此vuex中一般不保存非常重要的数据。【放在vuex中的数据一般是刷新后也不影响业务的数据】
vuex的核心组成:
state:状态,用于初始化仓库中的数据,在这里声明项目中全局使用的数据
mutations:修改state数据的方法,存放用于修改state数据的方法
只存放同于同步修改数据的方法
每个方法接收俩个形参,依次是:
state:object,指的是仓库中的数据
payload:any类型,用于修改数据的数据源(可选)
函数不需要return,所有操作都是基于state直接更改
actions:修改state数据的方法,存放用于修改state数据的方法
只存放同于异步修改数据的方法
每个方法接收俩个形参,依次是:
context:object,指的是仓库对象(上下文对象)
payload:any类型,用于修改数据的数据源(可选)
函数不需要return,所有操作都是基于state直接更改
这里的方法本身自己不直接该数据,而是通过context对象调用mutations中的方法去修改数据
getters:获取并修饰数据(对数据进行格式处理),存储用于修饰数据的方法
里面的方法有一个形参
state:object,指的是仓库中的数据
每个函数必须有返回值,返回修饰完的数据
modules:模块化,存放模块化后的仓库模块,这里存放导入进来的模块变量
vuex语法,vuex支持对象属性形式、辅助函数形式去操作store中数据,所以每个操作都具备俩个语法:
获取state数据
对象属性:this.$store.state.属性名
辅助函数:mapState
作用:将指定的state中的数据映射为组件自身的计算属性
语法:
写在computed中
...mapState([属性名1,属性名2,....])
同步修改数据
对象属性:this.$store.commit(方法名,载荷数据)
辅助函数:mapMutations
作用:将指定的mutations中的方法映射为组件自身的方法
语法:
写在methods中
...mapMutations([方法名1,方法名2,...])
异步修改数据
对象属性:this.$store.dispatch(方法名,载荷数据)
辅助函数:mapActions
作用:将指定的Actions中的方法映射为组件自身的方法
语法:
写在methods中
...mapActions([方法名1,方法名2,...])
获取修饰数据
对象属性:this.$store.getters.属性名
辅助函数:mapGetters
作用:将指定的getters中的数据映射为组件自身的计算属性
语法:
写在computed中
...mapGetters([属性名1,属性名2,....])
为什么需要模块化?
团队协作开发需要
方便后期维护管理
怎么模块化?
将除modules对象中的内容按照指定的拆分标准进行按文件分离
在index.js中导入并在modules对象中注册
有什么需要注意的?
因为模块化,后续的语法需要有所调整,具体见下
命名冲突时会执行自动合并策略
state,名字相同也不会冲突
mutations、actioms里同名方法会被合并成数组,都执行(index.js中的是最先执行的)
getters如果同名,无法合并,直接报错
通过命名空间来避免冲突(给每个模块开启命名空间)
设置模块的namespaced属性为true即可
vuex模块化后的语法:
获取state数据
对象属性:this.$store.state.模块名.属性名
辅助函数:mapState
作用:将指定的state中的数据映射为组件自身的计算属性
语法:
写在computed中
...mapState(模块名,[属性名1,属性名2,....])
同步修改数据
对象属性:this.$store.commit(模块名/方法名,载荷数据)
辅助函数:mapMutations
作用:将指定的mutations中的方法映射为组件自身的方法
语法:
写在methods中
...mapMutations(模块名,[方法名1,方法名2,...])
异步修改数据
对象属性:this.$store.dispatch(模块名/方法名,载荷数据)
辅助函数:mapActions
作用:将指定的Actions中的方法映射为组件自身的方法
语法:
写在methods中
...mapActions(模块名,[方法名1,方法名2,...])
获取修饰数据
对象属性:this.$store.getters["模块名/属性名"]
辅助函数:mapGetters
作用:将指定的getters中的数据映射为组件自身的计算属性
语法:
写在computed中
...mapGetters(模块名,[属性名1,属性名2,....])
mutation中的操作是一系列的同步函数,用于修改state中的变量的的状态。当使用vuex时需要通过commit来提交需要操作的内容。mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是实际进行状态更改的地方,并且它会接受 state 作为第一个参数:
- const store = new Vuex.Store({
- state: {
- count: 1
- },
- mutations: {
- increment (state) {
- state.count++ // 变更状态
- }
- }
- })
当触发一个类型为 increment 的 mutation 时,需要调用此函数:
store.commit('increment')
而Action类似于mutation,不同点在于:
Action 可以包含任意异步操作。
Action 提交的是 mutation,而不是直接变更状态。
- const store = new Vuex.Store({
- state: {
- count: 0
- },
- mutations: {
- increment (state) {
- state.count++
- }
- },
- actions: {
- increment (context) {
- context.commit('increment')
- }
- }
- })
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用
context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。 所以,两者的不同点如下:
Mutation专注于修改State,理论上是修改State的唯一途径;Action业务代码、异步请求。
Mutation:必须同步执行;Action:可以异步,但不能直接操作State。
在视图更新时,先触发actions,actions再触发mutation
mutation的参数是state,它包含store中的数据;store的参数是context,它是 state 的父级,包含 state、getters
(1)最重要的区别
vuex存储在内存中
localstorage 则以文件的方式存储在本地,只能存储字符串类型的数据,存储对象需要 JSON的stringify和parse方法进行处理。 读取内存比读取硬盘速度要快
(2)应用场景
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。vuex用于组件之间的传值。
localstorage是本地存储,是将数据存储到浏览器的方法,一般是在跨页面传递数据时使用 。
Vuex能做到数据的响应式,localstorage不能
(3)永久性
刷新页面时vuex存储的值会丢失,localstorage不会。
注意: 对于不变的数据确实可以用localstorage可以代替vuex,但是当两个组件共用一个数据源(对象或数组)时,如果其中一个组件改变了该数据源,希望另一个组件响应该变化时,localstorage无法做到,原因就是区别1。
有五种,分别是 State、 Getter、Mutation 、Action、 Module
state => 基本数据(数据源存放地)
getters => 从基本数据派生出来的数据
mutations => 提交更改数据的方法,同步
actions => 像一个装饰器,包裹mutations,使之可以异步。
modules => 模块化Vuex
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解我们的应用。
Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过 Action 来提交 mutation实现,这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解我们的应用。
每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现 time-travel 了。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。
在严格模式下,无论何时发生了状态变更且不是由mutation函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
在Vuex.Store 构造器选项中开启,如下
- const store = new Vuex.Store({
- strict:true,
- })
使用mapGetters辅助函数, 利用对象展开运算符将getter混入computed 对象中
- import {mapGetters} from 'vuex'
- export default{
- computed:{
- ...mapGetters(['total','discountTotal'])
- }
- }
使用mapMutations辅助函数,在组件中这么使用
- import { mapMutations } from 'vuex'
- methods:{
- ...mapMutations({
- setNumber:'SET_NUMBER',
- })
- }
然后调用this.setNumber(10)相当调用this.$store.commit('SET_NUMBER',10)
- import { createStore } from 'vuex'
- import router from '@/router'
- import qs from 'qs'
-
- export default createStore({
- state: {
- // token: '',
- // menuList:[],
- hasRoutes: false,
- editableTabsValue:'/index',
- editableTabs:[
- {
- title: '首页',
- name: '/index'
- }
- ]
- },
- getters: {
- GET_TOKEN: state => {
- return sessionStorage.getItem("token")
- },
- GET_MENULIST:state => {
- return JSON.parse(sessionStorage.getItem("menuList"))
- },
- GET_PERMS:state=>{
- return JSON.parse(sessionStorage.getItem("perms"))
- },
- GET_USERINFO:state=>{
- // console.log("GET_USERINFO="+sessionStorage.getItem("userInfo"))
- return JSON.parse(sessionStorage.getItem("userInfo"))
- }
- },
- mutations: {
- SET_TOKEN: (state, token) => {
- // state.token = token
- sessionStorage.setItem("token", token)
- },
- RESET_TOKEN:(state)=>{
- //state.token=''
- sessionStorage.setItem("token", "")
- },
- SET_MENULIST:(state,menuList)=>{
- //state.menuList=menuList;
- sessionStorage.setItem("menuList", JSON.stringify(menuList))
- },
- SET_PERMS:(state,perms)=>{
- sessionStorage.setItem("perms", JSON.stringify(perms))
- },
- SET_USERINFO:(state,userInfo)=>{
- sessionStorage.setItem("userInfo", JSON.stringify(userInfo))
- },
- //动态添加tab
- ADD_TABS:(state,tab)=>{
- // debugger
- // console.log("进入条件"+state.editableTabs.findIndex(e=>e.name));
- if(state.editableTabs.findIndex(e=>e.name===tab.path)===-1){
- // if(tab.name===undefined){
- // tab.name="操作日志"
- // }
- state.editableTabs.push({
- title: tab.name,
- name:tab.path
- });
- }
- state.editableTabsValue=tab.path
- // console.log("路由列表:"+JSON.stringify(state.editableTabs))
- // console.log("路由值:"+JSON.stringify(state.editableTabsValue))
- },
- RESET_TABS:(state)=>{
- state.editableTabsValue='/index';
- state.editableTabs=[
- {
- title: '首页',
- name: '/index'
- }
- ]
- },
- SET_ROUTES_STATE:(state,hasRoutes)=>{
- state.hasRoutes=hasRoutes
- }
- },
- actions: {
- // 安全退出
- logout(){
- window.sessionStorage.clear();
- this.state.hasRoutes=false
- router.replace('/login')
- }
- },
- modules: {
- }
- })
-