Skip to content

✍️ 项目实践分享

  • 如何让编写的代码更加优雅、简洁、可读性高、并且易于维护和扩展;在日常工作中要多总结与思考,特别是关于全局性的设计、架构、代码规范的思考。
  • 以下是项目实践分享的示例代码

全局路由封装

文档描述

  • 路由入口src/router/index.js;
  • 路由守卫 src/router/guard.js
  • 报错修复 src/router/error-handling.js
  • 认证登录 src/router/auth.js
  • 配置合并 src/router/routes.js
  • 子路由src/router/modules;
  • 通过 webpack 的 require.context引入, 后续新增路由,直接在子路由路径增加 xxx.js即可
js
/**
 * 路由主入口 index.js
 */
import Vue from 'vue';
import Router from 'vue-router';
import { routes } from './routes';
import { fixNavigationDuplication } from './error-handling';
import { setupBeforeEachGuard, setupAfterEachGuard } from './guards';

Vue.use(Router);

// 修复路由导航重复问题
fixNavigationDuplication();

// 创建路由实例
const router = new Router({
  linkActiveClass: 'linkActive',
  base: __dirname,
  routes,
});
// 设置路由守卫
setupBeforeEachGuard(router);
setupAfterEachGuard(router);

export default router;

示例代码

展开
  1. routes.js
    js
    // 动态导入子路由模块
    const requireModule = require.context('./modules', false, /\.js$/);
    const moduleRoutes = requireModule
      .keys()
      .map((file) => requireModule(file).default)
      .flat();
      //...省略代码...
    export default routes;
  2. guard.js
    js
     /**
     * 路由前置守卫
     * @param {Object} router - 路由实例
     */
       export const setupBeforeEachGuard = (router) => {
         router.beforeEach((to, from, next) => {// 路由守卫逻辑
           // 路由变化时取消所有待处理的请求
           cancelAllPendingRequests('路由已变化,请求已取消');
           // 已登录时访问根路径或登录页,重定向到首页
           if (loggedIn && (to.path === '/' || to.path === '/login')) {
             next({ path: '/welCome' });
             return;
           }
           //...省略代码...
           // 已登录且路由存在,正常导航
           if (loggedIn && to.matched.length) {
             next();
             return;
           }
           // 路由不存在,导航到404页面
           if (to.matched.length === 0) {
             next({ path: '/notfound' });
             return;
           }
           // 默认情况,正常导航
           next();
         });
       };
       export default {
         setupBeforeEachGuard,
         setupAfterEachGuard,
       };

全局axios封装

文档描述

  • HTTP处理入口src/fetch/index.js;
  • 基本设置 src/fetch/config.js
  • 报错提示 src/fetch/error-handling.js
  • 超时提示 src/fetch/loading.js
  • 取消处理 src/fetch/cancel.js
  • axios拦截 src/fetch/interceptors.js
  • 请求&响应处理 src/fetch/request.js
  • 清理登录信息 src/fetch/clearAuth.js
  • api动态注册 src/fetch/api-registry.js
  • 各模块apisrc/fetch/modules;
  • 通过 webpack 的 require.context引入, 后续新增api,直接在modules下 xxx.js即可
js
/**
 * HTTP 请求模块主入口
 * from OPS @lixiaopeng used by @henson 2025年7月16日
 */
import { initAxiosConfig } from './config';
import { setupRequestInterceptor, setupResponseInterceptor } from './interceptors';
import { buildApiObject } from './api-registry';

// 初始化 axios 配置
initAxiosConfig();

// 设置拦截器
setupRequestInterceptor();
setupResponseInterceptor();

// 构建 API 对象
const APIS = buildApiObject();

export default APIS;

示例代码

展开
  1. api-registry.js
    js
      /**
      * API 注册与检测模块
      * from OPS @lixiaopeng used by @henson 2025年7月16日
      */
      import { requestMethods } from './request';
    
      // 动态导入API模块
      const requireModule = require.context('./modules', false, /\.js$/);
      const modules = requireModule.keys().map((file) => requireModule(file).default);
      /**
      * 检查重复API名称
      */
      export const detectDuplicateApis = () => {
        const urlNameMap = new Map();
    
        apiList.forEach((api) => {
          const { name } = api;
          const apis = urlNameMap.get(name) || [];
          apis.push(api);
          urlNameMap.set(name, apis);
        });
    
        // 两个或以上同名API
        const duplicates = Array.from(urlNameMap.values())
          .filter((apis) => apis.length > 1)
          .reduce((acc, apis) => acc.concat(apis), []);
    
        if (duplicates.length > 0) {
          console.log('🚨 发现了相同名称的API请及时处理 ==>', { duplicates });
        }
      };
    
      /**
      * 构建API对象
      * @returns {Object} API对象
      */
      export const buildApiObject = () => {
        // 检测重复API
        detectDuplicateApis();
    
        // 构建API对象
        return apiList.reduce((apis, api) => {
          const { name: urlName, type = 'post', url, resType = 'json', timeout = '', repeat = 1 } = api;
          const requestMethod = requestMethods[type];
          //skipCancel用于是否需要跳过取消校验
          apis[urlName] = (data, config = { skipCancel: api.skipCancel || false }) =>
            requestMethod(url, data, { repeat, resType, timeout, ...config });
          return apis;
        }, {});
      };
    
      export default {
        apiList,
        detectDuplicateApis,
        buildApiObject,
      };
  2. request.js
    js
     /**
     * POST请求封装
     * @param {string} url - 请求URL
     * @param {Object} data - 请求参数
     * @param {Object} config - 请求配置
     * @returns {Promise} - 请求Promise
     */
     export const fetchPost = (url, data, config = {}) => {
       const { timeout, resType } = config;
       // 默认请求超过1.5秒显示 loading,长请求使用较长的延迟
       const loadingDelay = timeout > DEFAULT_DELAY ? timeout : DEFAULT_DELAY;
       LoadingInstance.start({}, loadingDelay);
    
       // 设置超时时间
       axios.defaults.timeout = timeout || axios.defaults.timeout;
       // 设置响应类型
       axios.defaults.responseType = resType || 'json';
    
       return axios.post(url, filterDirty(data), config).then(handleResponse).catch(handleResponseError);
       //...省略代码...
       /**
       * 处理响应数据
       * @param {Object} response - 响应对象
       */
       const handleResponse = (response) => {
         LoadingInstance.close();
    
         if (!response) return;
    
         const { data } = response;
    
         // 处理相关状态码
         if (data?.code == 2001) {
           const currentHash = window.location.hash;
           const redirectPath = currentHash.startsWith('#/')
             ? currentHash.substring(2, currentHash.indexOf('?') > -1 ? currentHash.indexOf('?') : undefined)
             : ''; // 获取当前路径
           redirectToLogin(redirectPath); // 重定向到登录页面
         } else if (data?.code == 8003) {
           // 清除用户信息
           clearLoginInfo();
           Message({ type: 'warning', message: i18n.t('fetch_request_1195') });
           redirectToLogin(); // 重定向到登录页面
         } else if (data?.code == 8004) {
           setTimeout(() => {
             console.log('团队切换');
             location.reload();
           }, 2000);
           // return Promise.reject(new Error('团队切换'));
         }
    
         return response;
       };        
       export default {
         fetchPost,
         fetchGet,
         requestMethods,
       };        
     };

结语

  • 学无止境,写出优雅的代码,在日常工作中不断提炼和进步的,希望我们能迈向更高的境界

树字标品MES 让智造简单些!