从零开始搭建前端项目六(axios+mock)

从零开始搭建前端项目六(axios+mock)

从零开始一步一步搭建一个精简的前端项目。

技术栈:Vue3.0 + Vite + TypeScript + Element Plus + Vue Router + axios + Pinia

规范化:Eslint + Airbnb JavaScript Style + husky + lint-staged

包管理:yarn

历史内容

从零开始搭建前端项目一(Vue3+Vite+TS+Eslint+Airbnb+prettier)

从零开始搭建前端项目二(husky+lint-staged)

从零开始搭建前端项目三(Element Plus)

从零开始搭建前端项目四(Vue Router)

从零开始搭建前端项目五(vite.config.ts优化)

本章内容

在项目中加入mock,封装axios,搭建前端网络请求调试环境。

mock

安装

安装依赖。

yarn add mockjs vite-plugin-mock --dev
复制代码

配置

配置 vitePlugins.ts(上一章已经把vite.config.ts中的plugins抽取到vitePlugins.ts中)。

// vitePlugins.ts
import { viteMockServe } from 'vite-plugin-mock';      // ++

export default (env: ConfigEnv) => {
  const isBuild = env.command === 'build';
  const vitePlugins: (Plugin | Plugin[])[] = [
    // ...
  ];
  // ++
  if (!isBuild) {
    vitePlugins.push(
      viteMockServe({
        ignore: /^\_/,
        mockPath: 'mock',
        localEnabled: !isBuild,
        // 实际开发关闭
        prodEnabled: false,
        injectCode: `
            import { setupProdMockServer } from '../mock/_createProductionServer';
            setupProdMockServer();
        `,
      })
    );
  }
  return vitePlugins;
};
复制代码

在项目根目录下创建mock文件夹,在mock文件夹下创建_createProdMockServer.ts文件,批量加载mock文件夹下的所有接口。

// _createProdMockServer.ts
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
// 批量加载
const modules = import.meta.globEager('./mock/*.ts');

const mockModules: Array<string> = [];
Object.keys(modules).forEach((key) => {
  if (key.includes('/_')) {
    return;
  }
  mockModules.push(...modules[key].default);
});

export default function setupProdMockServer() {
  createProdMockServer(mockModules);
}
复制代码

测试

在mock文件夹下创建user.ts,加入模拟登录接口。

import { MockMethod } from 'vite-plugin-mock';

export default [
  {
    url: '/textMock',
    timeout: 200,
    method: 'POST',
    response: () => {
      return {
        code: 0,
        data: {
          token: 'eca4bb1529bb5b4dcd3c9aa68e9e185d',
          expire: 1200,
          onLine: true,
        },
        msg: 'success',
      };
    },
  },
] as MockMethod[];
复制代码

运行,访问该api。
ts16.png
ts17.png

axios封装

安装

yarn add axios
复制代码

封装

src文件夹下创建utils->http文件夹,在http文件夹下创建baseAxios.ts文件。

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import qs from 'qs';
import isPlainObject from 'lodash/isPlainObject';
import router from '@router/index';
import { IResponse, RequestOptions } from '@models/axios/axios';

// 如果请求话费了超过 `timeout` 的时间,请求将被中断
axios.defaults.timeout = 1000 * 30;
// 表示跨域请求时是否需要使用凭证
axios.defaults.withCredentials = false;
// 允许跨域
axios.defaults.headers.post['Access-Control-Allow-Origin-Type'] = '*';
// 返回支持的状态码
axios.defaults.validateStatus = function (status: number) {
  return status >= 200 && status <= 500;
};

const axiosInstance: AxiosInstance = axios.create({
  baseURL: `${import.meta.env.BASE_URL}`,
});

// axios实例拦截响应
axiosInstance.interceptors.response.use(
  (response: AxiosResponse) => {
    return response;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// axios实例拦截请求
axiosInstance.interceptors.request.use(
  (config: AxiosRequestConfig) => {
    config.headers['Accept-Language'] = 'zh-CN';
    // 防止get请求缓存
    if (config.method === 'get') {
      config.params = {
        ...config.params,
        ...{ _t: new Date().getTime() },
      };
    }

    if (isPlainObject(config.data)) {
      // eslint-disable-next-line no-param-reassign
      config.data = {
        ...config.data,
      };
      if (config.headers?.['content-type']) {
        const contentType: string = config.headers['content-type'] + '';
        if (/^application\/x-www-form-urlencoded/.test(contentType)) {
          // form形式编码
          config.data = qs.stringify(config.data);
        }
      }
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

const request = <T = any>(
  config: AxiosRequestConfig,
  options?: RequestOptions
): Promise<T> => {
  const conf = config;
  return new Promise((resolve, reject) => {
    axiosInstance
      .request<any, AxiosResponse<IResponse>>(conf)
      .then((res: AxiosResponse<IResponse>) => {
        const data: any = res;
        resolve(data as T);
      })
      .catch((error) => reject(error));
  });
};

// 封装get
export function get<T = any>(
  config: AxiosRequestConfig,
  options?: RequestOptions
): Promise<T> {
  return request({ ...config, method: 'GET' }, options);
}
// 封装post
export function post<T = any>(
  config: AxiosRequestConfig,
  options?: RequestOptions
): Promise<T> {
  return request({ ...config, method: 'POST' }, options);
}

export default request;
export type { AxiosInstance, AxiosResponse };
复制代码

接口

src文件下创建api文件夹,在api文件夹下创建各个接口定义,如创建userLogin.ts文件。

import { post } from '@utils/http/baseAxios';
import { IResponse } from '@models/axios/axios';
import { LoginData } from '@models/user/user';

/**
 * @description: 用户登录
 * @params {LoginData} params
 * @return {Promise}
 */
export const login = (params: LoginData): Promise<IResponse> => {
  return post({ url: 'user/login', params }).then((res) => res.data);
};
复制代码

网络请求使用实例

在使用 async…await 方法时,经常采用 try…catch 捕获异常,如果有多个异步操作,需要每一次书写 try…catch。这样代码的简洁性较差,为了使代码更加的优雅,我们通过使用 await-to-js js 库来处理异常。

安装await-to-js

yarn add await-to-js
复制代码

使用

<template>
  <el-button @click="sendLogin">sendLogin</el-button>
</template><script setup lang="ts" name="Login">
import to from 'await-to-js';
import { login as userLogin} from '@api/userLogin';
import { IResponse } from '@models/axios/axios';
import { LoginData } from '@models/user/user';

const $router = useRouter();
const sendLogin = async () => {
  const params = {
    username: 'admin',
    password: '123456',
  } as LoginData;
  const [err, res] = await to<IResponse>(userLogin(params));
  if (err) {
    // 错误处理
    console.log(err);
  }
  const { code, data, msg } = res;
  console.log(code, data, msg);
};
</script>
复制代码

小结

本篇介绍了在开发过程中如何使用mock模拟网络请求,封装axios,使用await-to-js更优雅地处理网络请求。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享