Vue预习课:统一状态管理 - Vuex
vuex的数据只是在内存(页面)中临时保存,一旦页面刷新则数据丢失
Vuex
Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式 。它采用集中式存储管理应用的所有组件的状
态,并以相应的规则保证状态以 可预测的方式发生变化 。
集中式存储为了保证数据的同步,因为多个组件都会使用到这个数据。方便组件使用同一个数据
如果使用其他的方式进行数据一层层传递,如果数据出现不同步问题,则不方便排查是那个组件出现问题
下面就是标准的数据流(数据的流向可以预测)
安装
在vue-cli的环境下安装vuex
vue add vuex
起始
State —- 存储状态
将应用全局状态定义在state中
state: {isLogin: false}
Mutation —- 变更状态
修改state只能通过mutation
mutations: {login (state) {state.isLogin = true},logout (state) {state.isLogin = false}},
获取和修改状态
使用store.state获取状态
<button @click="login" v-if="!$store.state.isLogin">登录</button><button @click="logout" v-else>登出</button>
修改状态只能通过store.commit(mutation)
this.$store.commit('login')this.$store.commit('logout')
vuex只是在当前内存中临时缓存,刷新页面,则vuex的状态丢失
Action —- 异步
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
// 参数1是vuex传递的上下文context:{commit, dispatch, state}login ({ commit }, username) {// 模拟请求return new Promise((resolve, reject) => {setTimeout(() => {if (username === 'admin') {commit('login')resolve()} else {reject()}}, 1000);})}
派发动作触发actions
// 派发动作触发actions admin是传递的参数this.$store.dispatch('login', 'admin').then(() => {this.$router.push(this.$route.query.redirect)}).catch(() => { // 登录失败alert('用户名或密码错误')})
最佳实践
模块化
程序很大得情况下:
使用modules定义多个子模块利于组件复杂状态
import user from './user'export default new Vuex.Store({modules: {user,}})
移动先前登录状态相关代码到user.js
export default {namespaced: true, // 设置独立的命名空间,避免命名冲突// ...}
访问方式响应变化
// 访问数据的时候需要添加命名空间// Login.vue<button @click="login" v-if="!$store.state.user.isLogin">登录</button>this.$store.dispatch('user/login', 'admin').then(() => {const redirect = this.$route.query.redirect || '/'this.$router.push(redirect)}).catch(() => {alert('用户名或密码错误')})this.$store.commit('user/login')
// router/index.jsstore.state.user.isLogin
mapState()/mapMutation()/mapAction() —-通过映射使访问名称简短
通过这些映射方法可以让大家少敲几个字,避免对$store直接访问。减少耦合
通过映射方法,可以映射到当前组件的实例上。加上一些方法,或加上一些计算属性
state相关修改,Login.vue
import { mapState } from 'vuex'computed: {...mapState('user', ['isLogin']) // 这个调用写成isLogin...mapState(['user/isLogin']) // 这个写法也可以,但是调用不方便,调用需要写成这样user/isLogin// mapState返回的是一个对象,是一个键值对的形式,所以需要进行展开}
<button @click="login" v-if="!isLogin">登录</button>
action相关修改
import { mapActions } from 'vuex'methods: {login () {// this.$store.dispatch('user/getUserInfo', 'admin')this['user/login']('admin').then(...)},...mapActions(['user/login', 'user/logout'])// ...mapActions('user', ['login', 'logout']) 因为和computed中命名冲突,则使用上面的写法},
Getter
getters是派生状态。一旦和派生状态相关的状态发生改变,则派生的状态也会跟着发生变化(其实是计算属性在vuex中的迁移/实现)
可以使用getters从store的state中派生出一些状态
export default {namespaced: true,state: {isLogin: false,username: '' // 用户名},mutations: {setUsername (state, username) {state.username = username}},getters: { // 派生出欢迎信息welcome: state => {return state.username + ',欢迎回来';}},actions: {login ({ commit }, username) {return new Promise((resolve, reject) => {setTimeout(() => {if (username === 'admin') {// 登录成功,设置用户名commit('setUsername', username)resolve()} else {reject()}}, 1000);})}},}
{{welcome}}import { mapGetters, mapState } from 'vuex'computed: {...mapState('user', ['isLogin']),...mapGetters('user', ['welcome'])}
严格模式
严格模式是为了防止用户不通过vuex提供的方式改状态
严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有
的状态变更都能被调试工具跟踪到。开启严格模式strict: true
const store = new Vuex.Store({// ...strict: true})
插件
vuex插件的使用场景:就是在编写和状态无关的操作代码的时候使用。如果都写在vuex中则,页面代码难以维护
Vuex 的 store 接受 plugins 选项,这个选项暴露出每次 mutation 的钩子。Vuex 插件就是一个函
数,它接收 store 作为唯一参数:
// 插件 store实例const myPlugin = store => {// 当 store 初始化后调用}
注册插件:
const store = new Vuex.Store({// ...// 注册插件,可以注册多个plugins: [myPlugin]})
范例:实现登录状态持久化,store/plugins/persist.js
// vuex的插件// 存储vuex的值export default store => {// 在store初始化的时候,将存储在localStorage中的状态还原// 初始化时从localStorage获取数据if (localStorage) {const user = JSON.parse(localStorage.getItem('user'))if(user) {store.commit('user/login');store.commit('user/setUsername', user.userName);}}// 如果用户的状态发生变化,则自动存入localStorage// 这个API可以订阅mutation的变化,只要发生mutation,就会执行下面的回调函数// 用户状态发生变化时缓存之(订阅所有的mutation,只要mutation更改,就会监听到)store.subscribe((mutation, state) => {// type的类型// {type: 'user/login'}// {type: 'user/logout'}// {type: cart/card}if (mutation.type.startsWith('user/login')) {// 思考题目:因为路由是在登陆之后动态添加的,所以这个会导致需要能访问的路由没有访问到// 存入localStoragelocalStorage.setItem('user', JSON.stringify(state.user))}else if(mutation.type === 'user/logout') {// 注销的处理localStorage.removeItem('user')}})}
