实现原理
- 使用react-navigation对长流程每个节点的画面进行管理
- 每个流程建立独立的配置文件,用于控制整个交易的流程
- 通过统一入口开启流程
- 通过统一的流程管理工具控制流程中的画面跳转、数据流转等等
- 统一流程管理工具提供了画面出口管控、画面跳转、交易数据管理、开启/退出流程控制等等功能
实现过程
- 初始化工程
react-native init rn-tradedemo
- 添加react-navigation、babel插件等相关依赖:
npm i react-navigation@3.11.1 -S
npm i react-native-gesture-handler -S
npm i babel-plugin-root-plugin -D
- 配置babel.config.js,为特定路径命名
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
["babel-plugin-root-import",
{
"paths": [
{
"rootPathPrefix": "$/",
"rootPathSuffix": "./src/"
}
]
}
],
]
};
- 在/src/pages/demo1下创建长流程配置文件tradeflow.json
{
"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"
}
}
}
}
}
- 开发统一流程管理工具tradeflow
5.1 统一流程管理工具用于流程的启动、退出、画面跳转、数据管理等功能
5.2 统一流程管理工具提供的方法
方法名 | 描述 |
---|---|
getTradeCode | 获取流程代码 |
setTradeCode | 设置流程代码 |
setTradeFlow | 设置当前流程配置 |
nextStep | 根据传入的出口信息,跳转下一步 |
back | 回退上一步 |
exitTrade | 退出流程 |
setTradeData | 设置交易数据 |
getTradeData | 获取交易数据 |
getAllTradeData | 获取全部交易数据 |
removeTradeData | 删除交易数据 |
clearTradeData | 清空交易数据 |
startTrade | 开启流程 |
5.3 代码
/**
* 交易流程控制
* @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()}`);
}
}
- 开发合并脚本
6.1 合并脚本的目的是将N个流程的配置文件合并到一个统一的配置文件中,方便工程加载
6.2 代码
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"});
- 开发统一流程入口页面
7.1 统一流程入口的作用是根据路由传参
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 />;
}
}
- 路由
8.1 使用react-navigation用于统一的路由管理
8.2 除正常配置的路由外,还需要将流程配置文件中的路由提取出来添加到统一路由管理中
8.3 代码
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',
});
- 测试
9.1 在主页面中添加跳转demo1流程的方法,测试交易流程的运转。