1. 程式人生 > >前端業務程式碼配置化處理條件判斷邏輯

前端業務程式碼配置化處理條件判斷邏輯

## 業務程式碼中的配置化 工作中有許多邏輯冗雜、迭代頻繁的業務程式碼,隨著迭代將越來越難以維護,一些場景適合通過配置化的方式來處理便於維護。 ### 一、什麼是業務程式碼配置化? 根據業務場景使用配置化的 `Object|Array|Map` 處理條件判斷邏輯,通常需要配置檔案 `CONFIG.js`,若邏輯複雜需新增 `getConfig` 的處理函式 - `tool.js` - 本質上 if/else 邏輯是一種狀態匹配 - 表驅動法,使用表資料,儲存對應的狀態處理 - 可讀性好,減少了繁雜巢狀的 `if-else`,讀取配置,邏輯更清晰 - 可維護性高,邏輯分支的增刪只是 `CONFIG` 的增刪 ### 二、如何在業務場景中進行程式碼配置化? #### 1. 簡單的狀態對映 - 按需使用 `Object|Map` 配置 ##### 單一條件 - Object 形式: ```javascript // 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 形式: ```javascript // CONFIG.JS export const WORK_MAP = new Map([ [0, "學生"], [1, "老師"], [2, "碼農"], ]); // index.js this.setData({ work: WORK_MAP.get(status), }); ``` ##### 多重條件 ```javascript 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` 配置 ```javascript // 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` 配置 - 通過配置函式並傳參注入介面資料可滿足定製化需求 ```javascript // 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 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](https://img.suv666.com/test/20200624150811_GGvX7iGu.png) #### index.xml - 資料驅動,減少模板中的判斷邏輯 ```xml ``` #### CONFIG.js ```javascript 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 ```javascript 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); }; ``` ### 四、結語 所以,業務程式碼配置化很簡單,大家也都一直在用,只是如果在一些業務場景中都形成配置化的習慣或者共識,可能更好維護吧。 --- 鄙人拙見,大佬