技术发展历程

记录Lucifer的学习历程

0%

react-native-tradeflow

实现原理

  1. 使用react-navigation对长流程每个节点的画面进行管理
  2. 每个流程建立独立的配置文件,用于控制整个交易的流程
  3. 通过统一入口开启流程
  4. 通过统一的流程管理工具控制流程中的画面跳转、数据流转等等
  5. 统一流程管理工具提供了画面出口管控、画面跳转、交易数据管理、开启/退出流程控制等等功能

实现过程

  1. 初始化工程
    1
    react-native init rn-tradedemo
  2. 添加react-navigation、babel插件等相关依赖:
    1
    2
    3
    npm i react-navigation@3.11.1 -S
    npm i react-native-gesture-handler -S
    npm i babel-plugin-root-plugin -D
  3. 配置babel.config.js,为特定路径命名
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    module.exports = {
    presets: ['module:metro-react-native-babel-preset'],
    plugins: [
    ["babel-plugin-root-import",
    {
    "paths": [
    {
    "rootPathPrefix": "$/",
    "rootPathSuffix": "./src/"
    }
    ]
    }
    ],
    ]
    };
  4. 在/src/pages/demo1下创建长流程配置文件tradeflow.json
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    {
    "tradecode": "demo1",
    "pages": [
    {
    "title": "page1",
    "path": "$/pages/demo1/tradepages/page1",
    "componentName": "page1",
    "navigationOptions": {
    "header": null
    }
    },
    {
    "title": "page21",
    "path": "$/pages/demo1/tradepages/page2.1",
    "componentName": "page21",
    "navigationOptions": {
    "header": null
    }
    },
    {
    "title": "page22",
    "path": "$/pages/demo1/tradepages/page2.2",
    "componentName": "page22",
    "navigationOptions": {
    "header": null
    }
    },
    {
    "title": "page3",
    "path": "$/pages/demo1/tradepages/page3",
    "componentName": "page3",
    "navigationOptions": {
    "header": null
    }
    },
    {
    "title": "page4",
    "path": "$/pages/demo1/tradepages/page4",
    "componentName": "page4",
    "navigationOptions": {
    "header": null
    }
    },
    {
    "title": "page5",
    "path": "$/pages/demo1/tradepages/page5",
    "componentName": "page5",
    "navigationOptions": {
    "header": null
    }
    }
    ],
    "tradeflow": {
    "title": "page1",
    "default": {
    "title": "page21",
    "default": {
    "title": "page3",
    "default": {
    "title": "page5"
    }
    }
    },
    "other": {
    "title": "page22",
    "default": {
    "title": "page4",
    "default": {
    "title": "page5"
    }
    }
    }
    }
    }
  5. 开发统一流程管理工具tradeflow
  6. 1 统一流程管理工具用于流程的启动、退出、画面跳转、数据管理等功能
  7. 2 统一流程管理工具提供的方法
方法名 描述
getTradeCode 获取流程代码
setTradeCode 设置流程代码
setTradeFlow 设置当前流程配置
nextStep 根据传入的出口信息,跳转下一步
back 回退上一步
exitTrade 退出流程
setTradeData 设置交易数据
getTradeData 获取交易数据
getAllTradeData 获取全部交易数据
removeTradeData 删除交易数据
clearTradeData 清空交易数据
startTrade 开启流程

5.3 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/**
* 交易流程控制
* @author Lucifer
*/

// 设置交易流程的全局参数
// 交易码
if (!window.tradeCode) {
window.tradeCode = null;
}

// 交易流程
if (!window.tradeFlow) {
window.tradeFlow = null;
}

// 当前交易节点
if (!window.currentTradeStep) {
window.currentTradeStep = [];
}

// 当前交易的数据
if (!window.currentTradeData) {
window.currentTradeData = {};
}

/**
* 获取下一个节点的名称
* @author Lucifer
*/
function getBackStep(flow, step) {
if (flow.title === step) {
return flow;
} else {
for (let key in flow) {
if (key !== "title" && key !== "back") {
let backStep = getBackStep(flow[key], step);
if (backStep) {
return backStep;
}
}
}
}
}

function getCurrentTradeStep() {
return window.currentTradeStep[window.currentTradeStep.length - 1];
}
function getCurrentTradeStepName() {
return getCurrentTradeStep().title;
}

export default {
/**
* 获取当前的交易码
* @author Lucifer
*/
getTradeCode() {
return window.tradeCode;
},

/**
* 设置当前交易码
* @author Lucifer
* @param tradecode 交易码
*/
setTradeCode(tradecode) {
window.tradeCode = tradecode;
},

/**
* 设置当前交易流程
* @author Lucifer
* @param tradeflow 当前交易流程
*/
setTradeFlow(tradeflow) {
window.tradeFlow = tradeflow;
window.currentTradeStep.push(tradeflow);
},

/**
* 进入交易的下一个节点
* @author Lucifer
* @param tradepage 当前交易页面对象
* @param out 出口名称(可不传,默认为default)
*/
nextStep(tradepage, out) {
if (!out) {
out = "default";
}

window.currentTradeStep.push(getCurrentTradeStep()[out]);
const routerName = `${window.tradeCode}-${getCurrentTradeStepName()}`;

tradepage.props.navigation.replace(routerName);
},

/**
* 退回到交易的上一个节点
* @author Lucifer
* @param tradepage 当前交易页面对象
*/
back(tradepage) {
if (window.currentTradeStep.length > 0) {
window.currentTradeStep.pop();
const routerName = `${window.tradeCode}-${getCurrentTradeStepName()}`;

tradepage.props.navigation.replace(routerName);
}
},

/**
* 退出当前交易
* @author Lucifer
* @param tradepage 当前交易页面对象
*/
exitTrade(tradepage) {
// 清空交易相关数据
window.tradeCode = null;
window.tradeFlow = null;
window.currentTradeStep = null;

tradepage.props.navigation.goBack();
},

/**
* 设置交易数据
* @author Lucifer
* @param key 交易数据Key
* @param value 交易数据Value
*/
setTradeData(key, value) {
if (!window.currentTradeData) {
window.currentTradeData = {};
}

window.currentTradeData[key] = value;
},

/**
* 获取交易数据
* @author Lucifer
* @param key 交易数据Key
*/
getTradeData(key) {
if (!window.currentTradeData) {
window.currentTradeData = {};
}

return window.currentTradeData[key];
},

/**
* 获取交易全部数据
* @author Lucifer
*/
getAllTradeData() {
if (!window.currentTradeData) {
window.currentTradeData = {};
}

return window.currentTradeData;
},

/**
* 根据Key删除交易数据
* @author Lucifer
* @param key 交易数据Key
*/
removeTradeData(key) {
if (!window.currentTradeData) {
window.currentTradeData = {};
}

delete window.currentTradeData[key];
},

/**
* 清空交易数据
* @author Lucifer
*/
clearTradeData() {
window.currentTradeData = {};
},

/**
* 开始交易
* @author Lucifer
* @param tradepage 交易画面
*/
startTrade(tradepage) {
tradepage.props.navigation.replace(`${window.tradeCode}-${getCurrentTradeStepName()}`);
}
}
  1. 开发合并脚本

  2. 1 合并脚本的目的是将N个流程的配置文件合并到一个统一的配置文件中,方便工程加载

  3. 2 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    const fs = require("fs");
    const path = require("path");
    const process = require("process");
    const chalk = require("chalk");
    const glob = require("glob");

    const basePath = path.resolve(__dirname, "../src");
    const destPath = `${basePath}/router/tradeRouters.js`;

    let tradeCodes = [];

    // 生成目标文件的内容
    let destContent = `module.exports = {`;

    glob.sync(`${basePath}/pages/**/tradeflow.json`).forEach(filepath => {
    const fileContentStr = fs.readFileSync(filepath, { encoding: "UTF-8" });

    const configJson = JSON.parse(fileContentStr);

    const tradecode = configJson.tradecode;

    if (tradeCodes.indexOf(tradecode) !== -1) {
    console.log(chalk.red("交易码有重复,请调整后再试"));
    process.exit(-1);
    } else {
    tradeCodes.push(tradecode);
    destContent += `"${tradecode}": {"pages": [`;

    const pages = configJson.pages;

    pages.map(node => {
    destContent += `{"title": "${node.title}", "screen": require("${node.path}"), "componentName": "${node.componentName}", "navigationOptions": ${JSON.stringify(node.navigationOptions)}},`;
    });

    destContent += `],
    "tradeflow": ${JSON.stringify(configJson.tradeflow)}`

    destContent += `},`;
    }
    });

    destContent += `}`;

    fs.writeFileSync(destPath, destContent, {encoding: "UTF-8"});

  4. 开发统一流程入口页面

  5. 1 统一流程入口的作用是根据路由传参

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import React, { Component } from "react";
    import { StyleSheet, View, Text } from "react-native";

    import tradeRouters from "$/router/tradeRouters";

    import TradeUtil from "$/trade-control";

    module.exports = class TradeTemplate extends Component {
    constructor(props) {
    super(props);

    // 交易码
    const tradecode = this.props.navigation.getParam("tradecode");
    TradeUtil.setTradeCode(tradecode);
    // 根据交易码找到交易对应的流程配置信息
    const currentTradeFlow = tradeRouters[tradecode];
    TradeUtil.setTradeFlow(currentTradeFlow.tradeflow);

    TradeUtil.startTrade(this);
    }
    render() {
    return <View />;
    }
    }
  6. 路由

  7. 1 使用react-navigation用于统一的路由管理

  8. 2 除正常配置的路由外,还需要将流程配置文件中的路由提取出来添加到统一路由管理中

  9. 3 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    import {createStackNavigator} from 'react-navigation';

    const tradeRouters = require('./tradeRouters');

    // 根据交易路由生成对应的路由配置
    function buildTradeRouters() {
    let retList = {};
    for (let trade in tradeRouters) {
    const config = tradeRouters[trade];
    const tradeSteps = config.pages;

    console.log(trade);
    tradeSteps.map((node, index) => {
    let currentTradeRoute = {};
    currentTradeRoute.screen = node.screen;
    currentTradeRoute.componentName = `${trade}-${node.componentName}`;
    currentTradeRoute.navigationOptions = node.navigationOptions
    ? node.navigationOptions
    : {header: null};

    retList[`${trade}-${node.componentName}`] = currentTradeRoute;
    });
    }

    return retList;
    }

    let routerList = Object.assign(
    {
    homepage: {
    screen: require('$/pages/home/homepage'),
    componentName: 'homepage',
    navigationOptions: {header: null},
    },
    tradetemplate: {
    screen: require('$/pages/templates/tradetemplate'),
    componentName: 'tradetemplate',
    navigationOptions: {header: null},
    },
    },
    buildTradeRouters(),
    );

    module.exports = createStackNavigator(routerList, {
    initialRouteName: 'homepage',
    });

  10. 测试

  11. 1 在主页面中添加跳转demo1流程的方法,测试交易流程的运转。

源码链接