多组件状态共享-vuex

Vuex

  1. 什么是Vuex?
    • Vuex是vue的一个状态管理工具,用于存储管理项目中的 state
  2. 什么是状态管理模拟呢?
    • 用 state 控制 视图,这种管理模式就是状态管理
    • 好处:
      • 我们的关注点在 state
  3. 什么是状态 state?
    • 用数据控制视图,这个数据我们称之为 状态
  4. Vuex什么情景下使用?
    • 当你看到或者接到项目就知道了,难就用,简单就不用
    • 当我们的项目中数据操作频繁、数据通信量大,我们需要使用vuex
  5. Vue构成部分
    • state 数据
    • actions 创建动作【用户交互】 ----- 注意: 这一部分可能不写
      • 后端数据交互可以写在actions
    • mutations 修改state的

官方给出的关系图展示
多组件状态共享-vuex_第1张图片

  1. vuex 流程 Flow
    - 单案例:
    - 即将要做项目,里面有功能,提前准备好的
    - 演示
    - 项目中使用流程:
    - 数据分块
    - 数据请求封装
    - 数据请求提前写好的

单案例:

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 公共函数封装库

./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)
        }
    },

项目文件夹根据个人喜好来定
多组件状态共享-vuex_第2张图片

你可能感兴趣的:(Vue,vue)