zoukankan      html  css  js  c++  java
  • 前端业务代码中的配置化

    业务代码中的配置化

    工作中有许多逻辑冗杂、迭代频繁的业务代码,随着迭代将越来越难以维护,一些场景适合通过配置化的方式来处理便于维护。

    一、什么是业务代码配置化?

    根据业务场景使用配置化的 Object|Array|Map 处理条件判断逻辑,通常需要配置文件 CONFIG.js,若逻辑复杂需添加 getConfig 的处理函数 - tool.js

    • 本质上 if/else 逻辑是一种状态匹配

    • 表驱动法,使用表数据,存储对应的状态处理

    • 可读性好,减少了繁杂嵌套的 if-else,读取配置,逻辑更清晰

    • 可维护性高,逻辑分支的增删只是 CONFIG 的增删

    二、如何在业务场景中进行代码配置化?

    1. 简单的状态映射

    • 按需使用 Object|Map 配置
    单一条件
    • Object 形式:
    // CONFIG.JS
      export const STATUS = {
        STUDENT: 0,
        TEACHER: 1,
        MA_NONG: 2,
      };
      export const WORK_MAP = {
        STATUS.STUDENT: '学生',
        STATUS.TEACHER: '老师',
        STATUS.MA_NONG: '码农',
      };
    
    // index.js
      this.setData({
        work: WORK_MAP[status],
      });
    
      axios.post(url, { status: STATUS.MA_NONG });
    
    • Map 形式:
    // CONFIG.JS
    export const WORK_MAP = new Map([
      [0, "学生"],
      [1, "老師"],
      [2, "码农"],
    ]);
    // index.js
    this.setData({
      work: WORK_MAP.get(status),
    });
    
    多重条件
    const config = new Map([
      [
        (condition0, condition1, condition2) =>
          condition0 && condition1 && condition2,
        () => {
          console.log("map0");
        },
      ],
      [
        (condition0, condition1, condition2) =>
          condition0 || condition1 || condition2,
        () => {
          console.log("map1");
        },
      ],
    ]);
    config.forEach((action, _if) => _if(0, 1, 0) && action());
    

    2. 每个状态有多种属性

    • 多个属性
    • 使用 Array 配置
    // CONFIG.JS
      export const CONFIG = [
        {
          status: STATUS.STUDENT,
          name: '学生',
          action: '谈恋爱',
        },
        {
          status: STATUS.TEACHER,
          name: '老师',
          action: '教书',
        },
        {
          status: STATUS.MA_NONG,
          name: '码农',
          action: '写bug',
        },
      ];
    
    // index.js
      <!-- 根据状态不同的行为 -->
      function action(status) {
        const { name, work } = CONFIG.find(i => i.status === status);
        console.log(`${name}在${action}`);
      }
    

    3. 每个状态有多种属性且参数定制化

    • 参数高度定制化,不同状态需要适配接口不同的字段
    • 使用 Array 配置
    • 通过配置函数并传参注入接口数据可满足定制化需求
    // CONFIG.JS
      export const CONFIG = [
        {
          status: STATUS.STUDENT,
          name: '学生',
          action: () => {
            console.log('学生的工作是谈恋爱');
          },
        },
        {
          status: STATUS.TEACHER,
          name: '老师',
          action: (info) => {
            alert(`老师${info.age}岁,每天${info.action}`);
          },
        },
        {
          status: STATUS.MA_NONG,
          name: '码农',
          action: (info) => {
            toast(`码农工作${info.workTime}年了,头发仅剩${info.hair}根了`);
          },
        },
      ];
    
    // index.js
      <!-- 根据接口状态action -->
      function action(res) {
        const { action, info } = CONFIG.find(i => i.status === res.status);
        action && action(info); // 传参定制化
      }
    

    三、实例

    大首页瀑布流 item 样式

    • 根据 list 接口下发的 item 的类型(type)&样式(layout)字段取 item 中的封面、标题、标签、头像...,字段各不相同
    • 十几种 item 类型,有的还有不同的 layout,item 数据下发方式不同
    • 公共组件,需要适配其他模块的接口数据作展示

    瀑布流item

    index.xml

    • 数据驱动,减少模板中的判断逻辑
    <view class="panel" bind:tap="goDetail">
      <!-- 封面 -->
      <image  wx:if="{{panel.cover}}" class="panel__cover" src="{{panel.cover.image}}">
          <view class="panel__tag {{panel.tagClass}}" wx:if="{{panel.tagClass}}">{{panel.tag}}</view>
      </image>
      <!-- 标题 -->
      <view class="panel__titl" wx:if="{{panel.title}}">{{panel.title}}</view>
      <!-- footer -->
      <view class="panel__footer" wx:if="{{panel.showFooter}}">
        <image class="panel__footer-icon" wx:if="{{panel.user.icon}}" src="{{panel.user.icon}}"></image>
        <text class="panel__footer-name" wx:if="{{panel.user.nickname}}">{{panel.user.nickname}}</text>
        <text class="panel__footer-comment" wx:if="{{panel.commentCount}}">{{panel.commentCount}}评论</text>
      </view>
    </view>
    

    CONFIG.js

    import { Layout, NewsType, Redirect } from 'src/constant';
    import { formatImage, formatUser } from './tool';
    
    /**
     * 配置项
     * @param {String} title 标题
     * @param {String} cover 封面
     * @param {String} tag 标签
     * @param {Object} user 用户信息
     * @param {Boolean} showFooter 是否显示footer
     * @param {Boolean} isAd 是否广告
     * @param {Function} itemWrap 兼容接口数据函数,数据可能以ref_xxx下发,比如帖子:ref_post
     * ......
     */
    
    <!-- 默认配置项 -->
    export const DEFAULT = {
      title: ({ title = '' }) => title,
      cover: ({ image_list = [], article_images = [] }) =>
        formatImage(image_list[0]) || formatImage(article_images[0]),
      showFooter: true,
      user: ({ user, user_account, article_source_tx = '' }) =>
        user
          ? formatUser(user)
          : user_account
          ? formatUser(user_account)
          : {
              icon: '',
              nickname: article_source_tx,
            },
    };
    
    export const CONFIG = [
      {
        type: NewsType.NEWS,
        ...DEFAULT,
        tag: '资讯',
        tagClass: 'news',
      },
      {
        type: NewsType.VIDEO,
        ...DEFAULT,
        tag: '',
        tagClass: 'video',
        cover: ({ image_gif_list = [], image_list = [] }) => formatImage(image_gif_list[0] || image_list[0]),
        video: ({ video_url = '', tencent_vid = '', image_gif_list = [] }) => ({
          hasVideo: true,
          src: tencent_vid || video_url,
          video_url,
          tencent_vid,
          gifCover: formatImage(image_gif_list[0]),
        }),
      },
      {
        type: Redirect.EVAL_DETAIL,
        layouts: [
          {
            layout: Layout.EVALUATION,
            ...DEFAULT,
            tag: '口碑',
            tagClass: 'praise',
          },
          {
            layout: Layout.PRAISE_COMPONENT,
            ...DEFAULT,
            tag: '口碑',
            tagClass: 'praise',
            itemWrap: ({ ref_car_score = {} }) => ref_car_score,
            title: ({ chosen_topic = '' }) => chosen_topic,
            commentCount: ({ comment_count = null }) => comment_count,
            cover: ({ images = [], recommend_images = [] }) =>
              formatImage(images[0]) ||
              formatImage(getCoverFromRecommendImages(recommend_images)),
          },
          {
            layout: Layout.NORMAL,
            ...DEFAULT,
          },
        ],
      },
      ......
    ];
    

    tool.js

    import { CONFIG, DEFAULT, AD_CONFIG } from "./CONFIG";
    // 获取瀑布流item数据
    export const getPanelData = (item) => {
      const getConfigByTypeAndLayout = () => {
        let config = CONFIG.find((i) => i.type == item.type);
        if (item.isAd) {
          config = AD_CONFIG;
        }
        if (config && config.layouts) {
          config = config.layouts.find(
            (i) => i.layout === item.layout_type || i.layout === item.display_style
          );
        }
        if (!config) {
          config = DEFAULT;
          console.log("no-config", item.type, item.layout_type, item);
        }
        return config;
      };
      const getPanelDataByConfig = (c) => {
        const panel = {};
        let _item = item;
        if (c.itemWrap) {
          _item = c.itemWrap(item);
        }
        Object.keys(c).forEach((key) => {
          if (typeof c[key] === "function") {
            panel[key] = c[key](_item);
          } else {
            panel[key] = c[key];
          }
        });
        return panel;
      };
      // 根据item的类型、样式获取配置
      const config = getConfigByTypeAndLayout(item);
      // 根据配置获取瀑布流item信息
      return getPanelDataByConfig(config);
    };
    

    四、结语

    所以,业务代码配置化很简单,大家也都一直在用,只是如果在一些业务场景中都形成配置化的习惯或者共识,可能更好维护吧。


    鄙人拙见,大佬赐教~

  • 相关阅读:
    基于UML和ASP.NET实现三层B/S结构系统开发
    通过避免下列 10 个常见 ASP.NET 缺陷使网站平稳运行(转)
    GOOGLE浏览器(已经可以下载啦)
    虽然很老,但是还是挺有用,而且经常要看。META
    ASP.NET保持用户状态的九种选择
    设计ASP.NET应用程序的七大绝招
    将更智能的 ASP.NET 文件下载体验内置到您的 Web 应用程序中 (MSDN)
    据说是民间最准确的算命法则
    ASP.NET是否是微软继MFC之后最无用的框架?
    宁波单身公寓出租
  • 原文地址:https://www.cnblogs.com/lizhhmac/p/13187188.html
Copyright © 2011-2022 走看看