1.state->actions->mutations - 存在actions的情况下
./src/store/index.js - 状态管理文件
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex) //激活插件
//! 打造仓库 -> 实例对象
const store=new Vuex.Store({
state:{
count:0 // 初始化数据
},
actions:{
// 用于创建动作的方法
increment({commit}){ //这个参数就是store,对他进行解构
// 动作的创建
const action={
type:'INCREMENT'// 动作的类型,一般用常量来表示
}
commit(action) //将动作发给mutations
},
decrement({commit}){
const action={
type:'DECREMENT'
}
commit(action)
},
},
mutations:{
// 用于修改数据的方法
INCREMENT(state,action){//动作的类型作为方法名
// state就是 数据
// action就是actions中发过来的动作
state.count++
},
DECREMENT(state,action){
state.count--
},
}
})
export default store
main.js - 入口文件
import store from './store'
new Vue({
store,// 入口文件中,通过store选项注册store实例,这样每一个组件就会得到一个 $store的属性
render: h => h(App),
}).$mount('#app')
App.vue
<template>
<div>
<!-- <p>{{this.$store.state.count}}</p> -->
<p>{{n}}</p>
<!-- <button @click="add">+</button> -->
<button @click='increment'>+</button>
<button @click='decrement'>-</button>
</div>
</template>
<script>
//方案一:通过辅助工具来连接actions
import {mapActions,mapState} from 'vuex'
//mapActions是一个函数,接受一个数组作为参数,它的返回值是一个对象
export default{
name:'App',
//为什么数据放在computed中而不是data中,因为data中放的是初始化数据
computed:{
...mapState({
n:state=>state.count
})
},
methods:{
...mapActions(['increment','decrement']),
//先说下为什么下面的方法不好,数据和方法都给store管,为什么自己还要自定义一个方法,多此一举
add(){ // 方案二:不使用辅助工具用法
this.$store.dispatch('increment')//用dispatch来连接组件->actions
// console.log(this.$store)
}
},
mounted(){
console.log('this',this)
}
}
</script>
上述App.vue中两种方式来连接数据:1)有辅助工具 ; 2)不用辅助工具
推荐使用辅助工具的用法
2.State -> mutations 跳过actions的情况
区别文件 App.vue
<template>
<div>
<p>{{n}}</p>
<button @click="INCREMENT">+</button>
<button @click="DECREMENT">-</button>
</div>
</template>
<script>
import {mapState,mapMutations} from 'vuex'
export default {
name:'App',
computed:{
...mapState({
n:state=>state.count
})
},
methods:{
...mapMutations(['INCREMENT','DECREMENT'])
}
}
</script>
分析:如果我们直接将项目中的所有state/actions/mutations全放在一起
- 坏处:
1. 杂乱,文件也比较大
2. 维护,更新不方便
- 解决:
1. 如果一个类型的数据做成一个数据包,那就好
2. 数据分块
一、数据分块
./store/index.js
import Vuex from 'vuex'
import Vue from 'vue'
import homeStore from '../pages/home/store'
import userStore from '../pages/user/store'
Vue.use(Vuex)//激活插件
export default new Vuex.Store({
//数据分块
modules:{
//虽然分了不同类别的对象,但是还是在同一个文件夹里
//将对象放到外面去,做成一个单独的模块,将对象放在store.js里,一个文件夹一个组件+一个状态管理
//在模块化里,单个文件具备单个功能
homeStore,
userStore,
}
})
./pages/home/store.js - 状态管理文件
export default{
namespaced:'homeStore',//命名空间,给数据包起了一个名字
state:{
count:0//初始化数据
},
actions:{
increment({commit},playload){
//playload我们称之为'负载',其实就是参数
const action={
type:'INCREMENT',
playload
}
commit(action)
}
},
mutations:{
INCREMENT(state,action){
//这里如何拿到组件里的数据? - 传参
state.count+=Number(action.playload)
}
}
}
./pages/home/index.vue - Home组件
<template>
<div>
<input type="number" name="" id="" v-model='num'>
<!-- 传参 -->
<button @click='increment(num)'> + </button>
<p>{{n}}</p>
</div>
</template>
<script>
import {mapState,mapActions} from 'vuex'
export default {
name:'Home',
data(){
return {
num:0
}
},
computed:{
// ...mapState(命名kongjian,{})
...mapState('homeStore',{
n:state=>state.count
})
},
methods:{
...mapActions('homeStore',['increment'])
}
}
</script>
App.vue
<template>
<div>
<Home/>
<!-- User/> -->
</div>
</template>
<script>
import Home from './pages/home/index'
import User from './pages/user/index'
export default {
name:'App',
components:{//注册
Home
}
}
</script>
二、带请求的数据分块
./pages/user/store.js - 状态管理文件
import axios from 'axios'
export default{
namespaced:'userStore',//命名空间,给数据包起了一个名字
state:{
userInfo:{}
},
actions:{
//请求在actions里发
//这里的this指的是这个对象,所以不能使用$http
getUserInfo({commit}){
axios.get('http://59.110.226.77:3000/api/user/getInfo',{
params:{
userId:'5e980ee6771cdf43999456b1',
token:'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InlhbnlhYmluZyIsInBhc3N3b3JkIjoiMTIzIiwiY3RpbWUiOjE1OTY0NDQwNTA4NTgsImlhdCI6MTU5NjQ0NDA1MH0.GfW6NnskSszrIw_8DlARvJRpWRWILO1gEhjl0OCDA1k'
}
}).then(res=>{
// console.log('res',res)
const action={
type:'GET_USER_INFO',
playload:res
}
commit(action)
}).catch(error=>Promise.reject(error))
}
},
mutations:{//修改数据
GET_USER_INFO(state,action){
state.userInfo=action.playload.data.data
}
}
}
./pages/user/index.vue - User组件
<template>
<div>
<p>username:{{userInfo.username}}</p>
<p>telphone:{{userInfo.phone}}</p>
</div>
</template>
<script>
//引入辅助工具来链接state和actions
import {mapState,mapActions} from 'vuex'
export default {
name:'User',
computed:{
...mapState('userStore',{
userInfo:state=>state.userInfo
})
},
methods:{
...mapActions('userStore',['getUserInfo'])
},
mounted(){//页面一加载完就发请求
this.getUserInfo()
}
}
</script>
App.vue同上(一、数据分块)
这里我们考虑的东西就比较多了,我们写好的代码不是只为了出结果即可,要做好性能优化和方便后期维护的
思考:
store.js状态管理文件中,请求的方式需要换怎么办?
请求的路径需要换怎么办?
Axios.get里回调太深,get是回调,then是回调 , commit是回掉,容易回调地狱?
这些都不利于后期维护
------------ 继续操作 -------------
1.封装数据请求
注:将数据请求统一一处封装,便于将来的管理和更新
- utils/request || utils/http || utils/ajax || utils/api
./utils/request.js
import axios from 'axios'
import {getCookie} from './cookie'
import {baseURL} from '../services/baseURL'
//1.创建axios 自定义实例
const ins=axios.create({
baseURL,//这个值是多样的,因为会在很多环境下进行操作测试
timeout:4000,//连接超时
})
//2.设置拦截器
ins.interceptors.request.use(function(config){//配置项
//1.loading
//2.token 统一携带
//token从cookie中取
//这句话使用是有条件的,看接口文档是要统一携带,还是单个
// config.Headers.authToken=getCookie('token')//令牌一般都携带在请求头处,但是这里的携带在请求的参数中
return config
},function(error){
return Promise.reject(error)
})
ins.interceptors.response.use(function(res){//配置项
return res
},function(error){
return Promise.reject(error)
})
//3.发请求
export default function request(options){
//对参数进行解构
const {//设置默认参数
url,
method='get',
data,
headers={
"Content-Type":"application/x-www-form-urlencoded",
},
}=options;
switch(method.toUpperCase()){//判断请求方式
case 'GET':
return ins.get(url,{params:data});
break;
case 'POST':
switch(headers['Content-Type']){
case 'application/json':
return ins.post(url,data,{headers});
break;
case 'application/x-www-form-urlencoded':
const p=new URLSearchParams();
for(let key in data){
p.append(key,data[key])
}
return ins.post(url,p,{headers})
break;
case 'mutilpart/form-data':
const param=new FormData();
for(let key in data){
param.append(key,data[key])
}
return ins.post(url,param,{headers})
break;
}
break;
case 'PATCH':
return ins.patch(url,data);
break;
case 'PUT':
return ins.put(url,data);
break;
case 'DELETE':
return ins.delete(url,{data});//配置项
break;
default:
return ins(options);
break;
}
}
提取token需要从cookie中提取,封装cookie.js
./utils.cookie.js
export function getCookie(key){
if (document.cookie) { // 判断是否有cookie
let arr = document.cookie.split('; '); // 拆分所有cookie
for (let i = 0; i < arr.length; i++) {
let item = arr[i].split('='); // 将cookie数据拆分成 key value
// 通过key 查找value
if (item[0] === key) return item[1]; // 找到key 返回value
}
return ''; // 如果循环结束 都没有 则返回空字符串
}
}
export function setCookie(key, value, day){
if (typeof day === 'number') {
let d = new Date();
d.setDate(d.getDate() + day);
document.cookie = `${key}=${value};expires=${d};path=/`;
} else {
document.cookie = `${key}=${value};path=/`;
}
}
./services/baseURL.js - 将来可能用到的请求路径都写在这个模块中
//项目使用环境: 开发 测试 生产 Bata RC Online
export const baseURL='http://59.110.226.77:3000';//开发
// export const baseURL = 'http://59.110.226.77:8000' // 生产
// export const baseURL = 'http://43.100.35.99:8000' // test
// export const baseURL = 'http://59.110.226.77:8000' // Bata
// export const baseURL = 'http://59.110.226.77:8000' // RC
// export const baseURL = 'http://59.110.226.77:8000' // Online
./services/index.js - 目的:为了防止回调太深,提前先将请求写好
// user相关
import request from '../utils/request'
import {getCookie} from '../utils/cookie'
// 获取用户信息接口
export default function getUserInfoReq(){
// retrurn返回值就是promise
//因为request返回的ins===axios返回的是一个promise
return request(({
url:'/api/user/getInfo',
data:{
userId:getCookie('userId'),
token:getCookie('token')
}
}))
}
./user/store.js - 优化后的写法
actions部分进行修改
actions:{
// ! 这里进行数据请求封装方法
//优化过后的写法
async getUserInfo({commit}){
const action={
type:'GET_USER_INFO',
playload:await getUserInfoReq()//请求得到的结果,是个异步
};
commit(action)
}
},