前端开发调试-管理端react脚手架

173 阅读6分钟

背景

项目伊始阶段或者在接手新项目,了解前端基本架构了解前端脚手架需要具备什么能力很重要。一个好的脚手架会提供完备的能力,助力开发者日常codeing。

介绍

以下介绍是在多年开发中,总结下来关于管理端较为完备的脚手架能力清单。基于antd pro现有脚手架进行描述、增加我们日常开发可能需要用的功能。对于脚手架更底层的组成,需要自己去一一了解、翻查。想要学习到更多知识的前端初学者更需要去了解现成脚手架各个底层能力才能更好地驾驭、使用、完善属于自己的业务脚手架

脚手架搭建介绍

  • 现成脚手架 (react)

    react 脚手架选型:react + antd pro + ts

    使用以下命令:官网

    npm i @ant-design/pro-cli -g
    pro create myapp
    

    根据提示选择对应的模块 如无法使用antd pro cli 工具,可以直接clone github仓库代码: github.com/ant-design/…

  • 环境变量

    react脚手架:antd pro 环境变量定义 官网介绍

    环境变量(local、dev、test、uat、prod)

    image.png

    Pro 脚手架默认使用 Umi 作为底层框架,在 Umi 内可通过指定 UMI_ENV 环境变量来区分不同环境的配置文件,UMI_ENV 需要在 package.json 内配置。

    scripts: {
        "start": "cross-env REACT_APP_ENV=dev UMI_ENV=dev umi dev",
        "start:dev": "cross-env REACT_APP_ENV=dev UMI_ENV=dev MOCK=none umi dev",
        "start:no-mock": "cross-env REACT_APP_ENV=dev UMI_ENV=dev MOCK=none umi dev",
        "start:no-ui": "cross-env REACT_APP_ENV=dev UMI_ENV=dev UMI_UI=none umi dev",
        "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=pre MOCK=none umi dev",
        "start:test": "cross-env REACT_APP_ENV=test UMI_ENV=test MOCK=none umi dev"
    }
  • 全局变量

react 教授架在变量的配置方式都一样,在加载环境文件中定义变量。

antd pro 环境变量文件配置例子(config.[mode].ts)

const ENV = 'dev';
const envs = {
  dev: {
    define: {
      // 全局变量
      API_URL: '/api',
    },
    proxy: {
      '/api': {
        // 要代理的地址
        target: 'http://localhost:8080',
        changeOrigin: true,
        pathRewrite: {
          '^/api': '',
        },
      },
    },
  },
};

/**
 * 导出的多环境变量命名约定:一律大写且采用下划线分割单词
 * 注意:在添加变量后,需要在src/typing.d.ts内添加该变量的声明,否则在使用变量时IDE会报错。
 */
export default defineConfig({
  define: {
    ...envs[ENV].define,
  },
});
export const proxy = envs[ENV].proxy;
  • 全局状态

    antd pro 脚手架提供了 useModel 数据流,基于 hooks 范式的轻量级数据管理方案,可以在 Umi 项目中管理全局的共享数据。

    注意:状态可以分为全局状态、页面状态、组件状态,划分好状态层级,并将状态在合适的层级维护

-页面状态缓存

umi 提供了 插件 umi-plugin-keep-alive,该插件是由 react-activation 二次封装。通过KeepAlive组件包裹,实现页面缓存功能。

路由页面缓存

{
    name: 'demo',
    path: '/demo',
    component: './demo',
    wrappers: ['@/components/KeepAlive'],
    access: 'normalRouteFilter',
 }

通过封装KeepAlive组件,在antd路由文件中配置wrappers控制页面缓存

  • 逻辑封装

    anrd pro 脚手架请求封装 (@umijs/max)

    @umijs/max 内置了插件方案。它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。脚手架request配置文件 src/requestErrorConfig.ts

    统一错误处理

        // 错误处理: umi@3 的错误处理方案。
      errorConfig: {
        // 自定义错误,错误抛出
        errorThrower: (res) => {
          const { success, data, errorCode, errorMessage, showType } =
            res as unknown as ResponseStructure;
          if (!success) {
            // 根据业务定制错误
            const error: any = new Error(errorMessage);
            error.name = 'BizError';
            error.info = { errorCode, errorMessage, showType, data };
            throw error; // 抛出自制的错误
          }
        },
        // 错误接收及处理
        errorHandler: (error: any, opts: any) => {
          if (opts?.skipErrorHandler) throw error;
          // 自定义 errorThrower 抛出的错误。
          if (error.name === 'BizError') {
           // 业务处理包括: 未登录与登录失效的重定向、后端接口异常码映射与错误描述、
          } else if (error.response) {
            // Axios 的错误处理
            // 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
          } else if (error.request) {
            // 请求已经成功发起,但没有收到响应
            // \`error.request\` 在浏览器中是 XMLHttpRequest 的实例,
            // 而在node.js中是 http.ClientRequest 的实例
          } else {
            // 发送请求时出了点问题
          }
        },
      },
    

    响应拦截错误处理处理业务错误报错包括且不限于: 未登录、登录失效、后端异常、接口404、接口失败约定等。

    请求拦截

        // 请求拦截器
      requestInterceptors: [
        (config: RequestOptions) => {
          // 拦截请求配置,进行个性化处理。
          const url = config?.url?.concat('?token = 123');
          return { ...config, url };
        },
      ],
    

    请求拦截统一处理请求中的必须传参、headers。如:默认接口拼接参数、请求请求头(headers) Content-Type 、统一鉴权token、全局loading等处理

    响应拦截

        // 响应拦截器
      responseInterceptors: [
        (response) => {
          // 拦截响应数据,进行个性化处理
          const { data } = response as unknown as ResponseStructure;
    
          if (data?.success === false) {
            message.error('请求失败!');
          }
          return response;
        },
      ],
    

    响应拦截统一处理非异常情况后端接口返回正常,但是业务需要特殊处理部分逻辑。如:统一处理后端返回数据流,处理图片流,音频流等

  • 组件封装

    脚手架提供统一业务组件路径:src/components。组件封装包含且不限于:布局组件、菜单组件、头部栏组件、页面缓存标签组件、页面内容组件、表单组件、查询条件组件、表格组件、编辑表格组件、带查询条件表格组件、 内容描述组件等

  • 业务封装 react: hooks

    统一封装业务流程包含且不限于:登录流程、客户端缓存(storage、IndexdDB、cookies)、页面keepAlive声明周期与系统中定制的业务功能等

  • module 封装 (实验)

    以页面模块为封装模型统一封装接口模型,统一接口模型有一下优势:

    1、抽取service层,分层管理

    2、提高接口复用性、拓展性、可维护

    3、service模块可按需注入页面

    4、模板化接口层,代码生成

    const useOptionsMemberInfo = () => {
      const [memberList, setMemberList] = useState({});
      const [memberDetail,setMemberDetail] = useState({})
      const getMemberInfoList = async (payload) => {
          try {
            const { result, code } = await service({
              url: API.getMemberByPage,
              method: 'GET',
              payload,
            });
            if(code === 0){
              setMemberList(result)
            }
          } catch (error) {
            console.log(error)
          }
      };
      const getMemberDetail = async (payload) => {
        try {
          const { result, code } = await service({
            url: API.getMemberDetail,
            method: 'GET',
            payload,
          });
          if(code === 0){
            setMemberDetail(result)
          }
        } catch (error) {
          console.log(error)
        }
    };
      const delMember = async (payload) => {
        try {
          const { result, code } = await service({
            url: API.delMember,
            method: 'POST',
            payload,
          });
          if(code === 0){
            // message 删除成功
          }
        } catch (error) {
          console.log(error)
        }
      };
      const addMember = async (payload) => {
        try {
          const { result, code } = await service({
            url: API.addMember,
            method: 'POST',
            payload,
          });
          if(code === 0){
            // message 新增成功
          }
        } catch (error) {
          console.log(error)
        }
      };
      const upadteMember = async (payload) => {
        try {
          const { result, code } = await service({
            url: API.upadteMember,
            method: 'POST',
            payload,
          });
          if(code === 0){
            // message 新增成功
          }
        } catch (error) {
          console.log(error)
        }
      };
      return {
        memberList,
        setMemberList,
        memberDetail,
        setMemberDetail,
        getMemberInfoList,
        delMember,
        addMember,
        upadteMember,
        getMemberDetail
      };
    };
    
  • utils功能单元封装

    统一封装业务流程包含且不限于:字符加密解密工具、处理js精度问题的加减乘除计算工具、url处理工具、图片base64文件处理工具、输入表单校验正则工具、时间处理工具

  • 文件格式规范

    参考文章 前端代码规范