一、 开始前的说明
本文从最开始一步一步搭建一个react-native + redux-saga + websocket + protobuf的项目。项目完全是按照规范的应用开发目录构建。
- 你将学到的知识
1.1 如何创建一个 react-native项目,react-native项目工程目录搭建。
1.2 如何集成 redux 。
1.3 如何集成 redux-saga。
1.4 如何使用 websocket。
1.5 如何使用protobufjs。
1.1 创建一个react-native项目
(创建项目非常简单,如果你不会或者还没有搭建环境 请参考 点击前往)
在cmd控制台运行 react-native init (项目名)sagaandprotobuf
创建成功后会看到如图1
看到这个图说明已经创建成功了!
1.2 集成redux-saga
关于 redux 和 redux-saga 的介绍和功能就不详细说明了,网上很多
1.2.1 添加 redux 和 redux-saga
(1) 集成redux-saga 前我们先要集成redux。首先看一下当前生成的项目目录。如下图2
图2.png
(2)添加在package.json中添加rudux 相关包 , 运行 npm install 安装
"dependencies": {
"react-redux": "^6.0.0",
"redux": "^4.0.1",
"redux-logger": "^3.0.6",
"redux-saga": "^1.0.0"
},
(3) 安装完成后 创建redux 和 redux-saga 的相关目录文件夹和文件:
根目录下创建app文件夹,用来存放我们要写的代码。
[1] 在app文件夹下新建三个文件夹:actions(用来存放以后所有的action),pages(用来存放页面),reducers(用来存放reducer),sagas(用来存放各模块的saga文件)。因为在项目开发中需要将各个模块的action 和 raducer分开开发,所以或有很多这类文件。
[2] 在app文件夹下新建actionsTypes.js,rootReducers.js,rootsagas.js,store.js 文件文件。 在pages文件夹下新建 home.js。
[3] 在action 文件夹里面新建 WebsocketAction.js,reducers 文件夹下新建 WebsocketReducer.js,sagas文件夹下新建 WebsocketSaga.js。
至此我们还没有写一行代码。只是将框架搭建起来。目录结构如下图3。
图3.png
(4)按照我上面的步骤建立好文件后,只需要跟着下面粘贴代码就可以了,开始复制粘贴:
[1] actionsTypes.js 代码如下
/**
* create by sxf on 2019/2/14.
* 功能: 事件类型统一分配
*/
export const CONNECTSUCCESS = 'CONNECTSUCCESS';
export const CONNECT = 'CONNECT';
export const CONNECTFALL = 'CONNECTFALL';
export const SENDMSG = 'SENDMSG';
export const CONNECTCLOSE = 'CONNECTCLOSE';
export const RETURNMSG = 'RETURNMSG';
[2] rootReducers.js 代码如下
import {combineReducers} from 'redux'
import WebsocketReducer from './reducers/WebsocketReducer'
//这里面必须要有初始数据 - 否则报错
const rootReducer = combineReducers({
WebsocketReducer
});
export default rootReducer;
[3] store.js 代码如下:将redux-saga和redux结合起来
import {createStore, applyMiddleware, compose} from 'redux'
import createSagaMiddleware , { END } from 'redux-saga'
import {createLogger} from 'redux-logger'
import rootReducer from './rootReducers'
import sagas from './rootsagas'
const configureStore = preloadedState => {
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
rootReducer,
preloadedState,
compose (
applyMiddleware(sagaMiddleware, createLogger())
)
)
sagaMiddleware.run(sagas);
store.close = () => store.dispatch(END)
return store;
}
const store = configureStore();
export default store;
[4] WebsocketAction.js 代码如下
import {CONNECT, CONNECTCLOSE, CONNECTFALL, CONNECTSUCCESS,SENDMSG,RETURNMSG } from './../actionsTypes'
const wsconnect = (connectobj) => ({ type : CONNECT,connectobj:connectobj});
const wsconnectclose = () => ({ type : CONNECTCLOSE});
const connectsuccess = () => ({ type : CONNECTSUCCESS});
const connectfall = () => ({ type : CONNECTFALL});
const sendmsg = (sendmsg) => ({ type : SENDMSG,sendmsg:sendmsg});
const wsmsgres = (msgstr) => ({ type : RETURNMSG,msgstr:msgstr});
export {
connectsuccess,
connectfall,
wsconnect,
wsconnectclose,
sendmsg,
wsmsgres
}
[5] WebsocketReducer.js 代码如下
import * as types from './../actionsTypes'
const initwsState ={
status:'未连接',
isSuccess:false,
ws:null,
msg:""
}
export default function webSockerfun(state=initwsState,action) {
switch (action.type){
case types.CONNECTSUCCESS:
return{
...state,
status:"连接成功",
}
break;
case types.CONNECTFALL:
return{
...state,
status:"未连接",
msg:""
}
break;
case types.RETURNMSG:
return{
...state,
msg:action.msgstr
}
break;
default:
return state;
}
}
[6] 修改App.js 内容为下代码。使得默认页面指向 home.js,并将redux 和react-native结合起来。
import React, {Component} from 'react';
import { Provider } from 'react-redux'
import store from './app/store'
import Home from './app/pages/home'
type Props = {};
export default class App extends Component<Props> {
render() {
return (
<Provider store={store}>
<Home/>
</Provider>
);
}
}
[7] 你可能发现怎么没写rootsagas 和 WebsocketSaga的代码呢?因为集成protobufjs是在WebsocketSaga当中。所以WebsocketSaga这部分要特别注意(重点和坑点 后面会讲)。现在的WebsocketSaga代码是一个无法运行的空的方法。
WebsocketSaga.js 代码如下
import { END} from 'redux-saga'
import { put , take, fork ,cancel ,cancelled,delay,call} from 'redux-saga/effects'
import {CONNECT,CONNECTCLOSE,SENDMSG} from './../actionsTypes'
import {connectsuccess,connectfall,wsmsgres} from './../actions/WebsocketAction'
export function* watchWebsocket() {
// 这里面未来会逻辑代码 现在增加这个方法主要是为了 rootsaga.js 不报错
}
rootsagas.js 代码如下
import {fork} from "redux-saga/effects";
import {watchWebsocket} from './sagas/WebsocketSaga'
export default function* rootSaga() {
yield fork(watchWebsocket);
}
[7] home.js 页面的代码如下
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity
} from 'react-native';
import { connect } from 'react-redux';
import {wsconnect,wsconnectclose,sendmsg} from './../actions/WebsocketAction'
import WebsocketReducer from "../reducers/WebsocketReducer";
class Home extends Component {
_onConnect(){
this.props.dispatch(wsconnect({mydispatch: this.props.dispatch}));
}
_onConnectclose(){
this.props.dispatch(wsconnectclose());
}
_onsendmsg(){
// 随便发送点数据
this.props.dispatch(sendmsg({msgtext:"protoBuf发送数:"+ Math.round(Math.random()*100)}));
}
render() {
return (
<View style={styles.container}>
<Text style={styles.counter}>{this.props.WebsocketReducer.status}</Text>
<TouchableOpacity style={styles.reset} onPress={()=>this._onConnect()}>
<Text>连接websocket</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.start} onPress={()=>this._onConnectclose()}>
<Text>断开连接</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.stop} onPress={()=>this._onsendmsg()}>
<Text>发送消息</Text>
</TouchableOpacity>
<Text style={styles.counter}>{this.props.WebsocketReducer.msg}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column'
},
counter: {
fontSize: 50,
marginBottom: 70
},
reset: {
margin: 10,
backgroundColor: 'yellow'
},
start: {
margin: 10,
backgroundColor: 'yellow'
},
stop: {
margin: 10,
backgroundColor: 'yellow'
}
})
const mapStateToProps = state => ({
WebsocketReducer:state.WebsocketReducer
})
export default connect(mapStateToProps)(Home);
好了 截止到目前 redux-saga 和 redux 都集成成功了。
1.3 集成protobufjs
说明: 关于protobuf 的知识网上有很多 推荐看 点击前往这片文章。这个讲的比较好。不过这个里面有一个坑和官方git是一样的后面会说。
(1) 首先将 protobufjs 包下载下来, 添加包到package.json中 运行 npm install 安装
"dependencies": {
...
"protobufjs": "^6.8.8"
},
(2) 一般情况会和后端定义一个.proto文件,用来定义proto格式。这里我们在sagas文件夹下新建一个文件awesome.proto 这个文件的内容如下(这个文件的内容规范 请看官网或者是啊上面的推荐网站)
// awesome.proto
package awesomepackage;
syntax = "proto3";
message AwesomeMessage {
string awesome_field = 1; // becomes awesomeField
}
(3) 将这个文件用pbjs 命令转成 json文件。(这个地方很坑,官网和其他地方都没有提到,如果不转的话在rudux-saga中是无法使用的。因为protobuf.load()方法采用的是回调函数的异步机制,违背了saga的书写规范。)
如何使用这个命令?
[1] 在\node_modules\protobufjs\bin目录下找到 pbjs 文件 这个就是命令文件。
[2] 控制台命令进入这个目录 cd node_modules\protobufjs\bin
[3] 运行 node ,这里面可能会运行不成功 卡在installing espree@^3.5.4 这个地方。我试了下 好像是npm 下载这个包会卡住, 这里手动 yarn add espree这个包吧(没有yarn?那你需要好好补补课了)
node pbjs -t json E:\2019stude\react-native-reduxsaga-protobuf-websocket\mystudy\app\sagas\awesome.proto > E:\2019stude\react-native-reduxsaga-protobuf-websocket\mystudy\app\sagas\awesome.json
运行完以后再项目的sagas文件夹下可以看到awsome.json文件
[4] 回头将WebsocketSaga代码改成下面的
import { END} from 'redux-saga'
import { put , take, fork ,cancel ,cancelled,delay,call} from 'redux-saga/effects'
import {CONNECT,CONNECTCLOSE,SENDMSG} from './../actionsTypes'
import {connectsuccess,connectfall,wsmsgres} from './../actions/WebsocketAction'
var protobuf = require("protobufjs");
var ws = null; // 缓存 websocket连接
var _mydispatch = null; // 这个变量是因为saga无法支持callback 只能变通处理(这也是个坑点)
var protpfile = null; // 缓存proto文件
export function* watchWebsocket() {
while (true){
const action = yield take(CONNECT);
if(_mydispatch == null){
_mydispatch = action.connectobj.mydispatch;
}
yield fork(connectWebsocket,_mydispatch);
var sendmsgTask = yield fork(sendmsg);
yield take(CONNECTCLOSE);
yield fork(connectcolseWebsocket);
yield cancel(sendmsgTask);
}
}
function* sendmsg(){
try{
while (true){
const sendaction = yield take(SENDMSG);
yield fork(decodeencodewithproto,sendaction.sendmsg.msgtext);
}
}finally {
if(yield cancelled()){
console.log("取消了监听发送任务");
}
}
}
function* decodeencodewithproto(sendstr) {
let restroot ;
if(protpfile == null){
// 缓存proto 对象
restroot = yield call(protobuffun);
protpfile = restroot;
}else{
restroot = protpfile;
}
var AwesomeMessage = restroot.lookupType("awesomepackage.AwesomeMessage");
var payload = { awesomeField: sendstr };
var errMsg = AwesomeMessage.verify(payload);
if (errMsg)
throw Error(errMsg);
var message = AwesomeMessage.create(payload); // or use .fromObject if conversion is necessary
var buffer = AwesomeMessage.encode(message).finish();
ws.send(buffer);
}
function protobuffun() {
return new Promise(resolve => {
//之所以要转成json 就是因为这个地方无法使用reload方法 只能用require方法
var jsonDescriptor = require("./awesome.json"); // exemplary for node
var root = protobuf.Root.fromJSON(jsonDescriptor);
resolve(root);
})
}
function* connectcolseWebsocket() {
ws.close();
}
function* connectWebsocket(mydispatch) {
ws = new WebSocket("ws://echo.websocket.org");
ws.onopen = () => {
mydispatch(connectsuccess())
};
ws.onerror = e => {
mydispatch(connectfall())
};
ws.onmessage = e => {
console.log(e.data)
var buf = new Uint8Array(e.data);
var _AwesomeMessage = protpfile.lookupType("awesomepackage.AwesomeMessage");
var message = _AwesomeMessage.decode(buf).awesomeField;
console.log(message);
mydispatch(wsmsgres(message))
};
ws.onclose = e => {
// connection closed
mydispatch(connectfall())
};
}
OK 大工搞成了。跑起来看下效果吧!!
图4.png
项目的源代码在我的git上面 地址是 https://github.com/chen735623058/react-native-reduxsaga-protobuf-websocket。 如果帮到您了辛苦给颗星呗。
网友评论