VueDemo-14-16状态管理器

14.状态管理器

 

Vuex 是什么? | Vuex

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

它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

状态传值

  • 父组件给子组件传值

  • 子组件给父组件传值

  • 非父子组件(兄弟组件)传值

  • 子组件直接使用父组件的数据和方法

  • 父组件直接使用子组件的数据和方法

  • 跨组件传值

  • 状态管理器

  • 多个视图依赖于同一状态。

  • 来自不同视图的行为需要变更同一状态。

VueDemo-14-16状态管理器_第1张图片

 VueDemo-14-16状态管理器_第2张图片

14.0 构建首页的头部组件


14.1 第一版登录状态

异步操作在组件,组件中直接提交mutations

定义状态管理器

// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
​
Vue.use(Vuex)
​
export default new Vuex.Store({
  state: {
    // ? 为什么要从本地存储设置 状态管理器的初始值
    // 因为每次刷新页面都会使 状态管理器 重置
    loginState: localStorage.getItem('loginState') === 'true'
  },
  mutations: {
    changeLoginState (state, payload) { // state 所有的状态  payload 参数
      state.loginState = payload
    }
  },
  actions: {
  },
  modules: {
  }
})
​

登录时保存状态

VueDemo-14-16状态管理器_第3张图片

// 修改状态管理器中的代码 --- 代码不要看图,只看图中的代码位置,实际代码替换如下
this.$store.commit('changeLoginState', true)

底部组件获取登录状态


// src/views/home/index.vue


​
​

退出登录


总结:登录时,异步的登录操作放到了 登录组件中

14.2 第二版登录状态

登录时,异步的登录操作放到了 状态管理器中,通过组件触发状态管理器执行 异步操作

登录的代码拷贝到了 store/index.js中的actions内

import Vue from 'vue'
import Vuex from 'vuex'
import router from '../router'
import { Dialog, Toast } from 'vant'
import { login } from './../api/user'
Vue.use(Vuex)
​
export default new Vuex.Store({
  state: {
    // ? 为什么要从本地存储设置 状态管理器的初始值
    // 因为每次刷新页面都会使 状态管理器 重置
    loginState: localStorage.getItem('loginState') === 'true'
  },
  mutations: {
    changeLoginState (state, payload) { // state 所有的状态  payload 参数
      state.loginState = payload
    }
  },
  actions: {
    // context 上下文对象 ---  store
    // params 参数
    loginAction (context, parmas) {
      login({
        loginname: parmas.loginname,
        password: parmas.password
      }).then(res => {
        if (res.data.code === '10010') {
          // 账户不存在,提醒用户是否要立即注册
          Dialog.confirm({
            message: '该用户还未注册,是否立即注册',
            confirmButtonText: '立即注册',
            confirmButtonColor: '#ff6666',
            cancelButtonText: '取消',
            cancelButtonColor: '#999'
          })
            .then(() => {
              router.push('/register/step1')
            })
            .catch(() => {
              // on cancel
            })
        } else if (res.data.code === '10011') {
          // 提醒用户密码错误, 视情况而定是否需要清空密码输入框 this.password = ''
          Toast('密码错误')
        } else {
          // 登录成功
          Toast('登录成功')
          localStorage.setItem('userid', res.data.data.userid) // 知道是谁
          localStorage.setItem('token', res.data.data.token) // 后端验证用户的登录状态
          localStorage.setItem('loginState', true) // 前端自检登录状态
​
          // 使用状态管理器修改状态
          context.commit('changeLoginState', true) // 修改状态管理器中的loginState的值为true
          // 返回上一页
          router.back()
        }
      })
    }
  },
  modules: {
  }
})
​

异步操作在组件,组件中通过

this.$store.commit()改变数据

异步操作在状态管理器,组件中通过

this.$store.dispatch() 触发,通过其内部的 context.commit() 改变数据

组件中通过this.$store.state获取状态,可以配合计算属性使用

14.3 使用辅助函数mapState获取状态

之前的案例是 通过 计算属性 计算得到 状态,通过 this.$store.state获取得到

mapState的是一个对象或者数组

// App.vue


// src/views/home/index.vue


// src/views/user/index.vue


14.4 使用辅助函数mapMutations 改变状态

这个辅助函数使用场景: 组件中去改变 mutation

// views/user/index.vue


14.5 使用辅助函数 mapActions 触发 acitons

组件触发 action,aciton提交mutation

// views/login/index.vue


14.6 getters以及ma pGetters

// src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import router from '../router'
import { Dialog, Toast } from 'vant'
import { login } from './../api/user'
Vue.use(Vuex)
​
export default new Vuex.Store({
  state: {
    list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5],
    // ? 为什么要从本地存储设置 状态管理器的初始值
    // 因为每次刷新页面都会使 状态管理器 重置
    loginState: localStorage.getItem('loginState') === 'true'
  },
  getters: { // 可以看作是state的计算属性,类似于computed
    len: state => state.list.length
  },
  mutations: {
    changeLoginState (state, payload) { // state 所有的状态  payload 参数
      state.loginState = payload
    }
  },
  actions: {
    // context 上下文对象 ---  store
    // params 参数
    loginAction (context, parmas) {
      login({
        loginname: parmas.loginname,
        password: parmas.password
      }).then(res => {
        if (res.data.code === '10010') {
          // 账户不存在,提醒用户是否要立即注册
          Dialog.confirm({
            message: '该用户还未注册,是否立即注册',
            confirmButtonText: '立即注册',
            confirmButtonColor: '#ff6666',
            cancelButtonText: '取消',
            cancelButtonColor: '#999'
          })
            .then(() => {
              router.push('/register/step1')
            })
            .catch(() => {
              // on cancel
            })
        } else if (res.data.code === '10011') {
          // 提醒用户密码错误, 视情况而定是否需要清空密码输入框 this.password = ''
          Toast('密码错误')
        } else {
          // 登录成功
          Toast('登录成功')
          localStorage.setItem('userid', res.data.data.userid) // 知道是谁
          localStorage.setItem('token', res.data.data.token) // 后端验证用户的登录状态
          localStorage.setItem('loginState', true) // 前端自检登录状态
​
          // 使用状态管理器修改状态
          context.commit('changeLoginState', true) // 修改状态管理器中的loginState的值为true
          // 返回上一页
          router.back()
        }
      })
    }
  },
  modules: {
  }
})
​

// src/views/user/index.vue


15 分模块的状态管理器

便于团队的开发协作

vue的实例中 可以 使用 computed 计算属性

vuex 中可以 使用 getters 计算属性

15.1 构建用户的相关的模块

// store/modules/user.js
import router from '../../router'
import { Dialog, Toast } from 'vant'
import { login } from './../../api/user'
export default {
  namespaced: true, // 命名空间,作用很大,用来标识是哪一个模块
  state: {
    list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5],
    // ? 为什么要从本地存储设置 状态管理器的初始值
    // 因为每次刷新页面都会使 状态管理器 重置
    loginState: localStorage.getItem('loginState') === 'true'
  },
​
  mutations: {
    changeLoginState (state, payload) { // state 所有的状态  payload 参数
      state.loginState = payload
    }
  },
  actions: {
    // context 上下文对象 ---  store
    // params 参数
    loginAction (context, parmas) {
      login({
        loginname: parmas.loginname,
        password: parmas.password
      }).then(res => {
        if (res.data.code === '10010') {
          // 账户不存在,提醒用户是否要立即注册
          Dialog.confirm({
            message: '该用户还未注册,是否立即注册',
            confirmButtonText: '立即注册',
            confirmButtonColor: '#ff6666',
            cancelButtonText: '取消',
            cancelButtonColor: '#999'
          })
            .then(() => {
              router.push('/register/step1')
            })
            .catch(() => {
              // on cancel
            })
        } else if (res.data.code === '10011') {
          // 提醒用户密码错误, 视情况而定是否需要清空密码输入框 this.password = ''
          Toast('密码错误')
        } else {
          // 登录成功
          Toast('登录成功')
          localStorage.setItem('userid', res.data.data.userid) // 知道是谁
          localStorage.setItem('token', res.data.data.token) // 后端验证用户的登录状态
          localStorage.setItem('loginState', true) // 前端自检登录状态
​
          // 使用状态管理器修改状态
          context.commit('changeLoginState', true) // 修改状态管理器中的loginState的值为true
          // 返回上一页
          router.back()
        }
      })
    }
  }
}
​
​
​
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
Vue.use(Vuex)
​
export default new Vuex.Store({
  getters: { // 可以看作是state的计算属性,类似于computed
    len: state => state.user.list.length
  },
  modules: { // 整合模块
    user
  }
})
​

Src/App.vue


// src/views/home/index.vue


15.2 状态管理器中使用模块

15.3 登录时改变状态

Src/views/login/index.vue


15.4 退出时改变状态

s r c/views/user/idnex.vue


16.使用状态管理器管理购物车的数据 - 作业

16.1 添加模块

s r c/store/cart.js

import { getCartList } from './../../api/cart'
export default {
  namespaced: true,
  state: {
    cartList: []
  },
  mutations: {
    changeCartList (state, data) {
      state.cartList = data
    }
  },
  actions: {
    getCartListAction (context, params) {
      return new Promise(resolve => {
        // 后续的操作交给组件
        getCartList(params).then(res => {
          resolve(res)
        })
      })
    }
  }
}
​

16.2 注册模块

// src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import cart from './modules/cart'
Vue.use(Vuex)
​
export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  getters: { // 可以看作状状态管理器的计算属性
    showNum (state) { // 控制底部选项卡中数量显示还是不显示
      // state 这个参数其实就是 所有的状态
      return state.user.isLogin
    },
   totalNum (state) { // 判断很关键
      return state.cart.cartList && state.cart.cartList.reduce((sum, item) => {
        return item.flag ? sum + item.num : sum + 0
      }, 0)
    },
    totalPrice (state) {
      return state.cart.cartList && state.cart.cartList.reduce((sum, item) => {
        return item.flag ? sum + item.originprice * item.num : sum + 0
      }, 0) * 100
    }
  },
  modules: {
    user,
    cart
  }
})
​

16.3 组件使用状态

s r c/views/cart/index.vue



16.4 添加底部的数量标签

s r c/app.vue


16.5 详情页面底部的数量


如果删掉本地存储的数据,当用户在首页后者详情页面重新刷新页面时,自动跳转到了 登录页面, ------ qq 微信

但是我们希望 首页,分类,详情,活动,即使用户不登录也可以查看 ---- 修改axios的配置

// http://www.axios-js.com/zh-cn/docs/
import axios from 'axios'
import router from './../router'
import store from './../store' // **************************重中之重****************************************
// 开发环境 yarn serve
// 生产环境 yarn build
// development  production
const isDev = process.env.NODE_ENV === 'development'
​
// 创建axios实例
// http://www.axios-js.com/zh-cn/docs/#axios-create-config
const request = axios.create({
  // baseUrl 实际请求的地址是 baseURL + 请求地址
  // http://121.89.205.189/api/banner/list  ===》 baseURL + '/banner/list'
  // baseURL: 'http://121.89.205.189/api',
  // 项目上线时无需修改baseURL地址 ---- 需要提前知道线上接口的地址
  // baseURL: isDev ? 'http://121.89.205.189/api' : 'http://121.89.205.189/api',
  // 如果使用了 代理 解决跨域问题
  baseURL: isDev ? '' : 'http://121.89.205.189/api',
  timeout: 6000 // 网络超时时间
})
​
// axios 的拦截器
// http://www.axios-js.com/zh-cn/docs/#%E6%8B%A6%E6%88%AA%E5%99%A8
// 请求拦截器 ---- 所有的数据在请求之前都会执行的代码 --- 显示loading的动画效果/给接口添加token
request.interceptors.request.use((config) => {
  // 在请求之前做些什么
  // 给所有的请求都传递token信息
  config.headers.common.token = localStorage.getItem('token') || ''
  return config
}, (error) => {
  return Promise.reject(error)
})
// 响应拦截器 ---- 在拿到接口的数据之前都会执行的代码 --- 隐藏loading的动画效果/验证token
request.interceptors.response.use((response) => {
  // 在响应时做些什么
  // token 没有传递  token 传递了只不过是错误的  token 传递了 失效了
  if (response.data.code === '10119') {
    console.log(111111111)
    // 引入路由器,跳转到登录页面 ****************************重中之重****************************************
    if (store.state.user.isLogin) {
      router.push('/login')
    }
  }
  return response
}, (error) => {
  return Promise.reject(error)
})
​
// 暴露自定义的axios
export default request
​

16.6 总结

  • 选择是否需要分模块(不管项目大小都可分 - 参照16点,但是一般小项目部分模块 - 参照 15点)

  • 分模块步骤

    • 1.先写模块(state, mutations, actions, namespaced)

    namespaced 是关键,使用 mutations 和 actions 时需要 指明模块的名称

    • 2.注册模块 (state, getters, mutations, actions, modules)

    modules是关键,它的key就是模块的名称

    getters 是计算属性

    state 可以是 所有组件都需要的模块的状态

    • 3.组件中使用辅助函数执行业务(mapState, mapGetters,mapMutations, mapActions)

你可能感兴趣的:(html,javascript,前端)