VueX的基本使用

VueX的基本使用

1、什么是VueX

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

以上是官方文档的介绍,我浅显的理解VueX就是用于存储一些全局配置,可以在不同的组件中使用这些全局配置,根据不同的使用场景提供了异步获取和计算属性式获取的方式。

2、VueX的使用场景

传统传值方式:

VueX传值方式:

从上图我们可以看出,使用VueX后,组件之间值的传递变的异常简单。可以进行统一管理,统一使用。减少了请求后台的次数。没有了繁琐的props和$refs。

但是VueX也有他的弊端,例如刷新页面后state将会变为初始状态,对于频繁变化的结果也不适于存储在VueX内,因此不建议将所有状态都使用VueX来管理,否则你需要在每个使用的页面都调用一次actions(代码分层的方式除外)。

VueX最常见的使用场景就是存储用户的信息、保存token、主题配置等。一种更加高级的用法是使用VueX将和后端交互的axios异步请求进行封装,实现代码的分层,让组件中的代码更加关注与页面交互

3、quickstart

安装vuex

npm install vuex --save

在根目录创建store目录,在目录内创建index.js,在其内部创建

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules: {}
})

在main.js内引用store

import Vue from 'vue'
import App from './App.vue'
import router from './router' //重要
import store from './store'  //重要
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.use(ElementUI)
Vue.config.productionTip = false

new Vue({
  router, 
  store, //重要
  render: h => h(App)
}).$mount('#app')

如果使用脚手架创建项目,且默认选择了vuex,系统会自动为我们创建上述内容。

4、state

4.1、state介绍

VueX中的state就是真正保存数据的地方,它的写法和作用类似于vue对象内的data。

  • state内存放的数据如果发生了修改,依赖这个属性的组件的数据也会发生变化。
  • state内的属性无法直接修改,必须通过mutations修改。
  • state内可以存储一般数据类型,也可以存储对象类型。

4.2、在state内添加一些数据

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    title: 'hello world',
    userInfo: {
      name: 'rayfoo',
      age: 24,
      car: ['Porsche', 'BMW']
    }
  },
  mutations: {},
  actions: {},
  modules: {}
})

4.3、在页面中使用state

// 在.vue内使用
<h1 v-text="$store.state.title"></h1>
{{$store.state.title}}

// 在js中使用
this.$store.state.title

4.4、语法糖...mapState

为了简化state的访问方式,VueX提供了访问语法糖,可以像访问当前项目计算属性一样访问state。

import { mapState } from 'vuex'
export default {
  computed: {
    ...mapState(['title'])
  }
}

页面调用

<h1 v-text="title"></h1>

5、mutations

5.1、mutations介绍

前面我们介绍了,state内的数据是无法直接修改的。如果要修改state内的元素,必须使用mutations。如果你学过java语言,那么可以用setter方法来简单的理解mutations。mutations的存在就是为了修改state内的数据。

由于mutations和我们下面要说的actions使用时非常相似,所以mutations的名称通常用大写加下划线表示

4.2、声明mutations

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    title: 'hello world'
  },
  mutations: {
    SET_TITLE (state, _title) {
      state.title = _title
    }
  },
  actions: {},
  modules: {}
})

我们可以看到,mutations的写法类似于method,它的第一个参数是固定的state,表示要修改的state。后面的参数我们可以自由指定或者省略。

上面的案例中我们通过setTitle将state内的title修改为了新的title,而新的title可以由参数的形式传递进来。

4.3、调用mutations

上面的案例,我们创建了一个mutations,它只是一个声明的过程,如果我们需要调用,则需要使用下面的代码:

this.$store.SET_TITLE("setTitle","hello Vuex")

4.4、语法糖...mapMutations

使用this.$store.commit这种操作形式过于繁琐,mutations提供了语法糖,能更像调用当前页面的methods一样对其调用。

用法如下

<script>
import { mapMutations } from 'vuex'
export default {
  created () {
    // 使用setTitle
    this.SET_TITLE('你好,mapMutations')
  },
  methods: {
    ...mapMutations(['SET_TITLE'])
  }
}
</script>

6、getters

6.1、getters介绍

前面我们用java中的setter比喻了mutations,那getters也可以比喻为java中的getter方法。如果没学过java也没关系,你一定用过Vue中的计算属性computed,getters的用法就类似于computed。

getters可以单纯的获取state中的内容,也可以对结果做过滤、计算、截取等等一系列的操作,来拿到我们需要的结果。

getters默认是不支持参数的,我们可以通过返回一个函数的形式来完成参数的传递。

6.2、声明getters

下面的案例我们通过getter,将所有的title以加上书名号的形式返回:

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    title: 'hello world'
  },
  mutations: {},
  actions: {},
  getters: {
    getFomatTitle (state) {
      return `《${state.title}》`
    }
  },
  modules: {}
})

6.3、使用getters

//.vue页面内
$store.getters.getFomatTitle

// javascript
this.$store.getters.getFomatTitle

6.4、向getters传递参数

下面的案例演示了通过返回函数的形式向getters内传递参数

声明

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    studentList: [
      { id: 1, name: 'zhangsan' },
      { id: 2, name: 'lisi' },
      { id: 3, name: 'wangwu' }
    ]
  },
  mutations: {},
  actions: {},
  getters: {
    getStudentById: state => id => {
      return state.studentList.find(student => student.id == id)
    }
  },
  modules: {}
})

调用

this.$store.getters.getStudentById(1)

4.5、语法糖...mapGetters

和mutations意义,getters也拥有自己的语法糖,下面的案例演示了如何使用getters的语法糖

import { mapGetters } from 'vuex'
export default {
  computed: {
    ...mapGetters(['getFomatTitle', 'getStudentById'])
  }
}

通过...mapGetters引入后,就可以像使用当前组件中的计算属性一样使用VueX的getters了。

页面使用

<h3 v-text="student.name"></h3>
<h3 v-text="getStudentById(2).name"></h3>

7、actions

7.1、actions介绍

actions可以理解为mutations的异步版本,由于修改state内的数据必须使用mutations,所以actions内还是必须使用mutations的。

7.2、声明actions

在vue里最常见的异步操作就是调用axios请求,下面我们通过一个actions调用axios的案例来认识一下它。

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    title: 'hello world'
  },
  mutations: {
    SET_TITLE (state, _title) {
      state.title = _title
    }
  },
  actions: {
    setAsyncTitle (context, title) {
      axios.get(`/api/get-title?title=${title}`).then(resp => {
        context.commit('SET_TITLEX', resp.data)
      })
    }
  },
  getters: {},
  modules: {}
})

7.3、使用actions

this.$store.dispatch('setAsyncTitle', '你好,Axios!!!')

7.4、语法糖...mapActions

import { mapActions } from 'vuex'
export default {
  created () {
    this.setAsyncTitle('你好,Axios!!!')
  },
  methods: {
    ...mapActions(['setAsyncTitle'])
  }
}

8、modules

8.1、modules介绍

前面的案例我们都是在VueX自动生成的./store/index.js中编写的,真实的项目中,VueX中保存的内容会很多,保存在一个文件中会很难管理,通过modules可以更方便的管理VueX。

每个module都可以拥有自己的state、mutations、getters、actions。

在实际开发中,modules中通常不会包含getters,而是单独创建一个js来保存VueX中的所有getters

8.2、modules的使用

简单的使用形式:

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 的状态

8.3、多文件形式引用Modules

多文件的引用形式:以下代码引用自ruoyi-vue

module

import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'

const user = {
  state: {
    token: getToken(),
    name: '',
    avatar: '',
    roles: [],
    permissions: []
  },

  mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token
    },
    SET_NAME: (state, name) => {
      state.name = name
    },
    SET_AVATAR: (state, avatar) => {
      state.avatar = avatar
    },
    SET_ROLES: (state, roles) => {
      state.roles = roles
    },
    SET_PERMISSIONS: (state, permissions) => {
      state.permissions = permissions
    }
  },

  actions: {
    // 登录
    Login({ commit }, userInfo) {
     // 略
    },

    // 获取用户信息
    GetInfo({ commit, state }) {
      // 略
    },

    // 退出系统
    LogOut({ commit, state }) {
      // 略
    },

    // 前端 登出
    FedLogOut({ commit }) {
      return new Promise(resolve => {
        commit('SET_TOKEN', '')
        removeToken()
        resolve()
      })
    }
  }
}

export default user

getters

const getters = {
  sidebar: state => state.app.sidebar,
  size: state => state.app.size,
  device: state => state.app.device,
  visitedViews: state => state.tagsView.visitedViews,
  cachedViews: state => state.tagsView.cachedViews,
  token: state => state.user.token,
  avatar: state => state.user.avatar,
  name: state => state.user.name,
  introduction: state => state.user.introduction,
  roles: state => state.user.roles,
  permissions: state => state.user.permissions,
  permission_routes: state => state.permission.routes,
  topbarRouters:state => state.permission.topbarRouters,
  defaultRoutes:state => state.permission.defaultRoutes,
  sidebarRouters:state => state.permission.sidebarRouters,
}
export default getters

index.js

import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
import user from './modules/user'
import tagsView from './modules/tagsView'
import permission from './modules/permission'
import settings from './modules/settings'
import getters from './getters'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    app,
    user,
    tagsView,
    permission,
    settings
  },
  getters
})

export default store

使用

// state and getters
this.$store.state.模块名.state

// actions
this.$store.dispatch('模块名/actionsName',prams)

// mutations 由于mutations不区分模块,所以注意命名全局唯一性
this.$store.commit("mutationsName", params);

// 因为上面说了 getters没有在每个modules内都写,所以可以直接用
this.getters.gettersName

// 如果使用module内的getters
this.$store.getters["模块名/gettersName"]

8.4、模块中的内容使用...mapXXX

# state
...mapState({
  userName: state => state.user.name,
  userAge: state=> state.user.age
})

# mutations getters actions 没有命名空间的限定,所以要保证全局的唯一性
...mapMutations(['mutationsName'])
...mapGetters(['gettersName'])
...mapActions(['actionsName'])

9、刷新页面数据丢失问题

虽然VueX可以保存全局状态,但页面刷新后。VueX中的状态会被重新初始化。如果当前页面的值恰好是在其他组件内mutations或actions后产生的,那值可能会显示异常,造成数据丢失的感觉。例如如果把获取用户信息的操作放在了登录页面,那么进入系统后刷新就会发生上述情况。

解决办法有如下三种思路:

1、将vuex中的数据保存到浏览器缓存中如(cookie、localStorage和sessionStorage)。

2、在会发生数据丢失的页面重新发送actions、mutations请求。

方法1的缺点是不安全且不适用于大量数据的存储,方法2适用于少量数据且接口访问速度比较快的情况。所以有了方案3,方案3就是方案1和方案2的结合。

3、页面刷新的时候异步请求后台数据,然后动态更新vuex中的数据,其中会有一种情况就是,网络延迟、数据量大的问题。此时还没等vuex获取到后台返回的数据,页面就已经加载完成了,这样就会造成数据丢失。所以该解决方案就是,监听浏览器刷新前事件,在浏览器刷新之前就把vuex里的数据保存至sessionStorage中,刷新成功后如果异步请求的数据还没返回则直接获取sessionStorage里的数据,否则获取vuex里的数据。(只有刷新后还没取到后台数据,才会从sessionStorage里取。确保数据的安全性,就算获取sessionStorage里的数据也是安全的,因为每次刷新都会重新赋值,不必担心数据被篡改问题,其次就是对sessionStorage里的数据做了加密操作)

参考:

VueX官方文档

什么是vuex?怎么使用?在那种场景下使用

如何解决vuex页面刷新数据丢失问题?

posted @ 2021-12-01 23:04  张瑞丰  阅读(1016)  评论(0编辑  收藏  举报