【第5期】前端Vue使用Proxy+Vuex(store、mutations、actions)跨域调通本地后端接口

本期简介

  • 本期要点
  1. 本地开发前后端如何跨域调用
  2. 全局请求、响应处理拦截器处理
  3. 封装HTTP请求模块
  4. 编写API请求映射到后端API
  5. 数据的状态管理

一、 本地开发前后端如何跨域调用

众所周知,只要前端和后端的域名或端口不一样,就存在跨域访问,例如:前端运行后通过http://localhost:3000访问,后端运行后通过http://localhost:8080访问,就存在跨域,跨域直接http调用会失败,因此要解决跨域访问,需要使用proxy

通过配置proxyTable,就具备了跨域请求,全栈开发就可以直接本地的前端调用本地的后端接口服务,接下来进行全局的请求响应处理

二、全局请求、响应处理拦截器处理

几乎每个项目都会在所有的请求之前做一些公共的配置或操作,也会在所有的响应中做一些统一数据结构的封装或其他操作

1、全局请求拦截器

Vue请求拦截器可以在所有请求发送到后端之前做一些处理,前后端分离常见的比如设置token

创建src/util/request.js

  • 跨域配置导入

    import config from '@/config';

  • 创建axios实例

    const _axios = axios.create(config);

  • 请求拦截器

    _axios.interceptors.request.use(
    config => {
    // 请求前设置token
    let authKey = 'X-Auth-Token';
    let token = localStorage.getItem(authKey);
    if (token) {
    config.headers[authKey] = token;
    }
    return config;
    },
    err => {
    return Promise.reject(err);
    }
    );

2、全局响应拦截器

同样在src/util/request.js中配置全局响应拦截器,这里的逻辑有

    1. 从请求头中获取X-Auth-Token,若有,缓存到localStorage中,便于在请求拦截器中每次携带到请求中,能获取到Token主要是登录的时候会返回,其他接口请求不会返回
    1. 异常捕获时,若响应码是403禁止访问类的,则重定向到登录页,这样无需在其他请求是时单独再做这类处理。
    1. 其他:有的后端接口是不同人开发的,也可能会跨项目进行接口调用,每个接口调用很多时候记不清是什么数据结构,为了前端业务使用统一的结构,一般会在响应中将数据结构进行统一

    _axios.interceptors.response.use(
    res => {
    // 这里处理响应
    let token = res.headers.get('X-Auth-Token');
    if (token) {
    console.log(token);
    localStorage.setItem('X-Auth-Token', token);
    }
    return res;
    },
    err => {
    if (err) {
    console.log(err);
    let title = '错误码: ' + (err.response.status || err.response.data.code);
    let msg = (err.response.data.msg || err.response.statusText);
    if (err.response.data.code === 403) {
    router.push('/Login');
    } else {
    notice.error(title, msg);
    return Promise.reject(err);
    }
    }
    }
    );

三、封装HTTP请求模块

在src/util/request.js中创建GET、POST、PUT等抽象方法,并导出http,在其他地方可以import后使用

1、封装GET请求

const http = {
  get (url, params) {
    return new Promise((resolve, reject) => {
      _axios({
        url,
        params,
        headers: {'Content-Type': 'application/json;charset=UTF-8'},
        method: 'GET'
      }).then(res => {
        resolve(res.data);
        return res;
      }).catch(err => {
        reject(err);
      });
    });
  }
};

export default http;

2、封装POST请求

const http = {
  post (url, body) {
    return new Promise((resolve, reject) => {
      _axios({
        url,
        data: body || {},
        headers: {'Content-Type': 'application/json;charset=UTF-8'},
        method: 'POST'
      }).then(res => {
        resolve(res.data);
        return res;
      }).catch(err => {
        reject(err);
      });
    });
  },
};

export default http;

3、封装PUT请求

const http = {
  put (url, body) {
    return new Promise((resolve, reject) => {
      _axios({
        url,
        data: body || {},
        headers: {'Content-Type': 'application/json;charset=UTF-8'},
        method: 'PUT'
      }).then(res => {
        resolve(res.data);
        return res;
      }).catch(err => {
        reject(err);
      });
    });
  }
};

export default http;

四、编写API请求映射到后端API

前面封装的http各种请求与业务无关,是抽象封装的,接下来创建业务调用模块

1、创建src/api/apis.js业务请求的接口文件

  • 导入封装的http请求文件

    import http from '@/util/request';

  • 以业务功能为单位定义请求模块

    const division = {};
    const login = {};
    const user = {};

2、定义业务请求

division.getFlatCities = () => {
  return http.get(
    '/api/v1/division/flat_cities',
    {}
  );
};

user.register = (data) => {
  return http.post(
    '/api/v1/register',
    data
  );
};

login.login = (data) => {
  return http.post(
    '/login',
    data
  );
};

login.logout = () => {
  return http.post(
    '/api/v1/logout'
  );
};

这样,在其他地方import apis from '@/src/api/apis'后就可以调用业务后端请求了,这样可以统一在apis.js中配置前端到后端的请求映射,单独在vue组件中使用axios也可以直接调用接口,只是这样非常的不友好。

五、数据的状态管理

有时候,我们需要把后端接口请求到的数据缓存起来,在其他各个vue组件中使用,那么这里就会有额外的操作(本地数据缓存),为了统一,我们使用vuex中的actions来封装一下请求后端接口的逻辑,举例:获取行政区划的平铺数据

刚刚apis.js的定义是这样:

division.getFlatCities = () => {
  return http.get(
    '/api/v1/division/flat_cities',
    {}
  );
};

1、store

我们在src/vuex/store.js中添加一个变量,用于记录行政区划的平铺数据:

import Vue from 'vue';
import Vuex from 'vuex';
import * as actions from './actions';
import * as mutations from './mutations';
import * as getters from './getters';

export default new Vuex.Store({
  state: {
    flatCities: {},
  }
}

2、mutations

然后在/src/vuex/mutations.js中创建修改store.state.flatCities的方法,理论上,mutations是唯一能修改store.state的方式,参数一就是store.state,参数二是调用SET_FLAT_CITIES方法的入参

export const SET_FLAT_CITIES = (state, flatCities) => {
  state.flatCities = flatCities;
};

3、actions

继续在/src/vuex/actions.js中定义含缓存操作的业务请求方法

export const loadFlatCities = ({commit, state}) => {
  let cacheData = localStorage.getItem('flatCities');
  if (cacheData) {
    // 有缓存,直接取缓存
    commit('SET_FLAT_CITIES', JSON.parse(cacheData));
  } else {
    apis.division.getFlatCities().then(res => {
      commit('SET_FLAT_CITIES', res.body);
      localStorage.setItem('flatCities', JSON.stringify(res.body));
    });
  }
};

4、...mapActions

vue组件中通过...mapActions映射到loadFlatCities方法

vue组件中从vuex引入mappActions

import {mapState, mapActions} from 'vuex';

通过...mapActions中添加loadFlatCities将其映射进来,这样就可以通过this.loadFlatCities()进行调用,和methos()中定义的方法调用方式一样。

methods: {
    ...mapActions(['logout', 'isLogin', 'loadFlatCities', 'autoLocation', 'changeCurrentLocation']),
    showLocation () {
      this.isShowLocation = true;
    },
    省略

5、$store.state.xxx读取数据状态

当在组件的created()中调用了this.loadFlatCities()时,localStorage和$store.state.flatCities都有后端返回的行政区划平铺,可以直接在组件中进行渲染了。

<div v-for="(flatDivision, fIndex) in $store.state.flatCities" :key="fIndex">
     <Row> 
            <Col span="1">
                  <div class="province-first-letter">{{ flatDivision.letter }}</div>
            </Col>
            <Col span="23">
                <div v-for="(province, index) in flatDivision.provinces" class="province-item" :key="index">
                  <Row>
                        <Col span="1">
                          {{ province.shortName }}
                        </Col>
                        <Col span="23">
                          <span v-for="(ct, index) in province.children" :key="index" class="city-item"
                                @click="changeCity(province, ct)">
                              {{ ct.shortName }}
                        </span>
                        </Col>
                   </Row>
                </div>
             </Col>
     </Row>
</div>

6、行政区划平铺渲染效果

相关推荐
星星会笑滴16 分钟前
vue+node+Express+xlsx+emements-plus实现导入excel,并且将数据保存到数据库
vue.js·excel·express
Backstroke fish38 分钟前
Token刷新机制
前端·javascript·vue.js·typescript·vue
临枫54140 分钟前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
RAY_CHEN.41 分钟前
vue3 pinia 中actions修改状态不生效
vue.js·typescript·npm
酷酷的威朗普41 分钟前
医院绩效考核系统
javascript·css·vue.js·typescript·node.js·echarts·html5
_Legend_King1 小时前
vue3 + elementPlus 日期时间选择器禁用未来及过去时间
javascript·vue.js·elementui
景天科技苑1 小时前
【vue3+vite】新一代vue脚手架工具vite,助力前端开发更快捷更高效
前端·javascript·vue.js·vite·vue项目·脚手架工具
小行星1251 小时前
前端预览pdf文件流
前端·javascript·vue.js
join81 小时前
解决vue-pdf的签章不显示问题
javascript·vue.js·pdf
小行星1251 小时前
前端把dom页面转为pdf文件下载和弹窗预览
前端·javascript·vue.js·pdf