技术发展历程

记录Lucifer的学习历程

0%

GooseSDK Context模块

1. PushWindow

打开新页面

  • 输入参数
参数 类型 是否必输 说明
url string 页面URL,可以是离线包内页面,也可以是在线页面的地址
param Object 相关参数
param.closeCurrentWindow boolean 打开新窗口时,是否关闭当前窗口,默认false
param.isShowTitleBar boolean 新窗口是否展示默认标题栏,默认false
passData Object 传递的参数
  • 输出参数

  • 打开外部链接示例–H5
1
2
3
4
5
6
7
8
9
10
11
12
13

window.GooseSDK.context.pushWindow({
url: 'https://www.taobao.com/'
})

window.GooseSDK.context.pushWindow({
url: 'https://www.baidu.com/'
})

window.GooseSDK.context.pushWindow({
url: 'https://www.youku.com/'
})

  • 打开外部链接示例–React Native
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

import GooseSDK from 'goose-sdk'

GooseSDK.context.pushWindow({
url: 'https://www.taobao.com/'
})

GooseSDK.context.pushWindow({
url: 'https://www.baidu.com/'
})

GooseSDK.context.pushWindow({
url: 'https://www.youku.com/'
})

  • 打开当前离线包其他页面并传递参数–H5
1
2
3
4
5
6
7
8
9
10
11
12
13

import GooseSDK from 'goose-sdk'

let options = {
url: 'index_demo2.html',
passData: {
key1: '传参1',
key2: '传参2'
}
}

window.GooseSDK.context.pushWindow(options)

  • 打开当前离线包其他页面并传递参数–React Native
1
2
3
4
5
6
7
8
9
10
11

let options = {
url: 'index_demo2.html',
passData: {
key1: '传参1',
key2: '传参2'
}
}

GooseSDK.context.pushWindow(options)

2. StartH5App

打开新的离线包

  • 输入参数
参数 类型 是否必输 说明
appId string 当前离线包的appid
param Object 目标页面传参(url除外)
param.url String 需打开离线包页面的url
closeCurrentApp boolean 是否跳转关闭当前离线包。默认false
isShowTitleBar Object 是否显示标题栏
  • 输出参数

  • 打开离线包–H5
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

// 打开离线包
let options = {
appId:'00010002',
param: {
url: '/www/index.html',
}
}
window.GooseSDK.context.startH5App(options)

// 打开离线包并传递参数
let options = {
appId:'00010002',
param: {
url: '/www/index.html',
key1: '传参1',
key2: '传参2'
}
}
window.GooseSDK.context.startH5App(options)

// 打开新的离线包设置是否显示标题栏并关闭当前离线包
let options = {
appId:'00010002',
param: {
url: '/www/index.html',
},
closeCurrentApp: true,
isShowTitleBar: true
}
window.GooseSDK.context.startH5App(options)

  • 打开离线包–React Native
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

import GooseSDK from 'goose-sdk'


// 打开离线包
let options = {
appId:'00010002',
param: {
url: '/www/index.html',
}
}
GooseSDK.context.startH5App(options)

// 打开离线包并传递参数
let options = {
appId:'00010002',
param: {
url: '/www/index.html',
key1: '传参1',
key2: '传参2'
}
}
GooseSDK.context.startH5App(options)

// 打开新的离线包设置是否显示标题栏并关闭当前离线包
let options = {
appId:'00010002',
param: {
url: '/www/index.html',
},
closeCurrentApp: true,
isShowTitleBar: true
}
GooseSDK.context.startH5App(options)

3. GetStartupParams

获取启动参数

  • 输入参数
参数 类型 是否必输 说明
params Object 目标页面传参(url除外)
param.key Array 需要提取的参数key值(上一个页面或者离线包传递的key)
  • 输出参数
参数 类型 是否必输 说明
result Object 提取的所有参数
  • 获取启动参数–H5
1
2
3
4
5
6
7
8
9
10
11

let params = {
key: ['id', 'name'],
}
window.GooseSDK.tools.getStartupParams(params, res => {
if (res) {
console.log(res.id)
console.log(res.name)
}
})

  • 获取启动参数–React Native
1
2
3
4
5
6
7
8
9
10
11
12
13

import GooseSDK from 'goose-sdk'

let params = {
key: ['id', 'name'],
}
GooseSDK.tools.getStartupParams(params , res => {
if (res) {
console.log(res.id)
console.log(res.name)
}
})

4. PopWindow

回退上一页面

  • 输入参数
参数 类型 是否必输 说明
data Object 退回到目标页传参 获取参数使用resume接口获取
  • 输出参数

  • 回退上一页面–H5
1
2
3
4
5

let params = { name: 'agree', age: '2' }

window.GooseSDK.context.popWindow({ data: params });

  • 回退上一页面–React Native
1
2
3
4
5
6
7

import GooseSDK from 'goose-sdk'

let params = { name: 'agree', age: '2' }

GooseSDK.context.popWindow({ data: params });

5. PopTo

回退N层或者回退到指定页面

  • 输入参数
参数 类型 是否必输 说明
index Number 目标界面在会话界面栈中的索引;如果小于零,则将与当前界面的 index 相加
urlPattern Number 目标界面在会话界面栈中的索引;如果小于零,则将与当前界面的 index 相加
index Number 目标界面在会话界面栈中的索引;如果小于零,则将与当前界面的 index 相加
  • 输出参数

  • 回退N层或者回退到指定页面–H5
1
2
3
4
5
6
7
8
9

// 返回N层并传递参数
let params = { name: 'agree', pageName: 'index.html' }

window.GooseSDK.context.popTo({ index: -5, data: params })

// 返回到某个页面
window.GooseSDK.context.popTo({ urlPattern: 'index_index.html' })

  • 回退N层或者回退到指定页面–React Native
1
2
3
4
5
6
7
8
9
10
11

import GooseSDK from 'goose-sdk'

// 返回N层并传递参数
let params = { name: 'agree', pageName: 'index.html' }

GooseSDK.context.popTo({ index: -5, data: params })

// 返回到某个页面
GooseSDK.context.popTo({ urlPattern: 'index_index.html' })

6. popToRoot

退回APP首页

  • 输入参数

  • 输出参数

  • 退回APP首页–H5
1
2
3

window.GooseSDK.context.popToRoot()

  • 退回APP首页–React Native
1
2
3
4
5

import GooseSDK from 'goose-sdk'

GooseSDK.context.popToRoot()

7. exitApp

退出当前离线包

  • 输入参数

  • 输出参数

  • 退出当前离线包–H5
1
2
3

window.GooseSDK.context.exitApp()

  • 退出当前离线包–React Native
1
2
3
4
5

import GooseSDK from 'goose-sdk'

GooseSDK.context.exitApp()

实现原理

  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流程的方法,测试交易流程的运转。

源码链接