前端業務程式碼中的配置化
阿新 • • 發佈:2020-06-24
## 業務程式碼中的配置化
工作中有許多邏輯冗雜、迭代頻繁的業務程式碼,隨著迭代將越來越難以維護,一些場景適合通過配置化的方式來處理便於維護。
### 一、什麼是業務程式碼配置化?
根據業務場景使用配置化的 `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);
};
```
### 四、結語
所以,業務程式碼配置化很簡單,大家也都一直在用,只是如果在一些業務場景中都形成配置化的習慣或者共識,可能更好維護吧。
---
鄙人拙見,大佬