Vuex 是什么? | Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
状态传值
父组件给子组件传值
子组件给父组件传值
非父子组件(兄弟组件)传值
子组件直接使用父组件的数据和方法
父组件直接使用子组件的数据和方法
跨组件传值
状态管理器
多个视图依赖于同一状态。
来自不同视图的行为需要变更同一状态。
- 北京
- 搜索框
- 登录
- 我的
....
异步操作在组件,组件中直接提交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: { } })
登录时保存状态
// 修改状态管理器中的代码 --- 代码不要看图,只看图中的代码位置,实际代码替换如下 this.$store.commit('changeLoginState', true)
底部组件获取登录状态
请将屏幕竖向浏览
// src/views/home/index.vue
- 北京
- 搜索框
我的 登录 ....
退出登录
user header
总结:登录时,异步的登录操作放到了 登录组件中
登录时,异步的登录操作放到了 状态管理器中,通过组件触发状态管理器执行 异步操作
登录的代码拷贝到了 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: { } })
输入至少6位,包含至少一个大写字母,1个小写字母,1个数字
发送短信验证码
- 短信验证码登录 账号密码登录
手机快速注册 其他登录方式 微信
苹果
异步操作在组件,组件中通过
this.$store.commit()
改变数据异步操作在状态管理器,组件中通过
this.$store.dispatch()
触发,通过其内部的 context.commit() 改变数据组件中通过this.$store.state获取状态,可以配合计算属性使用
之前的案例是 通过 计算属性 计算得到 状态,通过 this.$store.state获取得到
mapState的是一个对象或者数组
// App.vue
请将屏幕竖向浏览
// src/views/home/index.vue
....
// src/views/user/index.vue
user header
这个辅助函数使用场景: 组件中去改变 mutation
// views/user/index.vue
user header
组件触发 action,aciton提交mutation
// views/login/index.vue
输入至少6位,包含至少一个大写字母,1个小写字母,1个数字
发送短信验证码
- 短信验证码登录 账号密码登录
手机快速注册 其他登录方式 微信
苹果
// 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
user header {{ listLen }}
便于团队的开发协作
vue的实例中 可以 使用 computed 计算属性
vuex 中可以 使用 getters 计算属性
// 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
输入至少6位,包含至少一个大写字母,1个小写字母,1个数字
发送短信验证码
- 短信验证码登录 账号密码登录
手机快速注册 其他登录方式 微信
苹果
15.4 退出时改变状态
s r c/views/user/idnex.vue
user header {{ listLen }}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 详情页面底部的数量
{{ brand }} {{ category }}{{ proname }}
{{ originprice }} 如果删掉本地存储的数据,当用户在首页后者详情页面重新刷新页面时,自动跳转到了 登录页面, ------ 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)