区块链
挖矿,比特币,EOS,以太坊

eos开发教程,基于eos的Dapp开发–元素战争(三)

我们在前面的章节中先后介绍了一个基于EOS的Dapp中主要包含有哪些内容以及智能合约的编写过程和规范,今天我们来谈谈一个Dapp开发中另一个不可或缺的内容,即前端是如何开发的。

在本次课程之前需要指出:在本课程中将涉及到private-key的操作,由于这仅仅是个教程所以在这里故意将private-key的使用简单化了,在我们自己进行DAPP的开发过程中是不可取的,一定要注意保护好用户的隐私以及自己Dapp智能合约的账户安全。

上一节中我们在智能合约中实现了一个名为login的ation,用户通过前端进行登录,然后使用一个名为eosjs的Javascript的库提交请求到智能合约,在本节中我们还将使用另外一个JavaScript库Redux来处理React app的状态信息,Redux并不仅仅是为了React而设计的,因此我们要使用一个react-redux模块来实现这些。首先通过以下命令来安装相应的库:

1npm install --save redux
2npm install --save react-redux
3npm install --save eosjs

我们来看Login.jsp文件,其中包含了用户名输入框,private-key输入框,提交按钮三个部分,当然你现在点击这个按钮是不会有任何反应的,button是react的一个组件,我们可以在src/components/Button看到这些内容,button类封装了我们整个web app的按钮的绘制和样式,通过复用这个组件,我们可以避免大规模的使用CSS等来构建前端页面。

 1import React, { Component } from 'react';
 2import { connect } from 'react-redux';
 3// button组件
 4import { Button } from 'components';
 5// 服务组件
 6import { UserAction } from 'actions';
 7import { ApiService } from 'services';
 8
 9class Login extends Component {
10
11  constructor(props) {
12    super(props);
13    // 构造函数来存储数据的状态和错误信息
14    this.state = {
15      form: {
16        username: '',
17        key: '',
18        error: '',
19      },
20    }
21    // 绑定消息函数
22    this.handleChange = this.handleChange.bind(this);
23    this.handleSubmit = this.handleSubmit.bind(this);
24  }
25
26  // 响应键盘上的动作
27  handleChange(event) {
28    const { name, value } = event.target;
29    const { form } = this.state;
30
31    this.setState({
32      form: {
33        ...form,
34        [name]: value,
35        error: '',
36      },
37    });
38  }
39
40  // 用户点击提交按钮的相应
41  handleSubmit(event) {
42    event.preventDefault();
43    const { form } = this.state;
44    const { setUser } = this.props;
45    //通过apiService发送登录的trx到智能合约,如果登录成功了保存用户名到redux,如果失败了向用户展示错误信息
46    return ApiService.login(form)
47      .then(() => {
48        setUser({ name: form.username });
49      })
50      .catch(err => {
51        this.setState({ error: err.toString() });
52      });
53  }

我们首先要把登录框放到main app中,为此我们来编辑下这个文件src/components/App/App.jsx。接下来我们将在登录框中构建并绑定一些响应函数,需要存储登录的数据然后提交用户的登录信息到智能合约中去,然后通过一个构造器和两个函数来实现这些。

  • 构造函数–用来初始化一些信息同时绑定两个响应函数,这样我们就可以方便的查询组件的状态。
  • handleChange函数–当用户更新用户名或者密码的时候就会被触发,然后更新组件的状态。
  • handleSubmit函数–发送用户的登录请求到智能合约。

上面说了这么多,其实前端和智能合约之间并没有产生交互,接下来我们来看如何实现交互。在frontend文件夹中我们可以看到.env文件,它用来存储一些变量的地方如,类似于环境变量:

  • REACT_APP_EOS_HTTP_ENDPOINT–接口的地址
  • REACT_APP_EOS_CONTRACT_NAME–合约的名字

接下来让我们尝试创建一个服务端组件,命名为ApiService,这个服务组件将会让前端和智能合约联系起来。在创建服务组件的时候我们使用了takeAction方法,该方法将发送transaction信息到智能合约。在这里我们使用了eosjs中的三个库:

  • RPC
  • SignatureProvider
  • Api

关于这三个库的相关信息也可以参考下eosjs的文档。

在takeAction中我们将向智能合约发送两部分内容即:action和dataValue。为了trx处理的方便,我们将使用api.transact() 将发送的内容转为JSON格式。我们从代码中可以看到JSON中主要包含有三部分,账户、action的名字、权限。接下来定义login中的内容:用户名、key。这些信息已经保存在本地了,可以拿来直接使用,现在我们可以用ApiService.login()触发登录操作了。

登录功能实现之后,我们需要通知组件,以方便在登录过程中的调用。我们可以通过把登录消息存储在Redux中来实现,首先让我们来创建三个组件:

  • action
  • reducer
  • store

Action 是把数据从应用传到 store 的有效载荷,它是 store 数据的唯一来源。一般来说你会通过 store.dispatch() 将 action 传到 store。action一般都是存储在Redux中的一个普通的JavaScript对象,在本教程中我们只需定义一个action,我们称之为SET_USER,对应到我们上一节内容中的多索引表中存储的数据,在frontend/src/actions/UserAction.js可以找到:

 1import { ActionTypes } from 'const';
 2
 3class UserAction {
 4
 5  static setUser({ name, win_count, lost_count, game }) {
 6    return {
 7      type: ActionTypes.SET_USER,
 8      name,      // 用户名
 9      win_count, // 用户胜利的次数
10      lost_count,// 用户失败的次数
11      game,      // 当前游戏信息
12    }
13  }
14
15}
16
17export default UserAction;

同样的在frontend/src/reducers/UserReducer.js我们可以找到Reducer的相关信息,Action 只是描述了有事情发生了这一事实,并没有指明应用如何更新 state。而这正是 reducer 要做的事情。在 Redux 应用中,所有的 state 都被保存在一个单一对象中。建议在写代码前先想一下这个对象的结构。如何才能以最简的形式把应用的 state 用对象描述出来。在本文中我们在reducer中定义了一个初始化状态和一个状态声明相关内容。当SET_USER action被检测到的时候,我们会用Object.assign()通过创建一个副本的方式去更新初始化的状态。这个函数将会针对store中的每一个用户生成一个新的对象,开发者尽量不要直接修改Redux的store。

 1import { ActionTypes } from 'const';
 2//初始化一些信息
 3const initialState = {
 4  name: "",
 5  win_count: 0,
 6  lost_count: 0,
 7  game: null,
 8};
 9
10export default function (state = initialState, action) {
11  switch (action.type) {
12    case ActionTypes.SET_USER: {
13      return Object.assign({}, state, {
14        name: typeof action.name === "undefined" ? state.name : action.name,
15        win_count: action.win_count || initialState.win_count,
16        lost_count: action.lost_count || initialState.lost_count,
17        game: action.game || initialState.game,
18      });
19    }
20    default:
21      return state;
22  }
23}

本代码中将会使用Redux utility工具combinedReducers导出UserReducer,在frontend/src/reducers/index.js.可以找到,当然我们也可以在以后的开发过程中扩展添加更多的reducer

Store 就是把action和reducer联系到一起的对象。Store 有以下职责:

  • 维持应用的 state;
  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 通过 subscribe(listener) 注册监听器。

再次强调一下 Redux 应用只有一个单一的 store。当需要拆分处理数据的逻辑时,使用 reducer 组合 而不是创建多个 store。在本文中store的路径为frontend/src/store/index.js。

 1import { createStore, compose } from 'redux';
 2import rootReducer from 'reducers';
 3
 4const initialState = {};
 5const enhancers = [];
 6
 7// DevTools Extension for debugging in Chrome
 8if (process.env.NODE_ENV === 'development') {
 9  const devToolsExtension = window.devToolsExtension;
10
11  if (typeof devToolsExtension === 'function') {
12    enhancers.push(devToolsExtension());
13  }
14}
15
16const composedEnhancers = compose(
17  ...enhancers
18)
19
20const store = createStore(
21  rootReducer,
22  initialState,
23  composedEnhancers
24)
25
26export default store;

上面介绍了三个组件:action,reducer,store,但并未将三者如何融合的作出说明,当用户点击确认按钮的时候会通过handleSubmit()调用服务组件里的ApiService.login(),然后通过该方法调用智能合约里面的ation。调用智能合约里面的action分为两种情况:

  1. 调用成功:SET_USER这个ation被执行且UserReducer会接收到相应的action,Redux store中将会更新用户名相应的属性值,其他信息不变。
  2. 调用失败:将会记录相应的失败信息到Login登录界面。

为了连接store和web app我们还需要使用connect函数将两者关联起来,可以参看以下代码:

1// 将所有的状态信息和组件的属性值放到map表里
2const mapStateToProps = state => state;
3
4// 将以下的action和组件的属性值放到map表里
5const mapDispatchToProps = {
6  setUser: UserAction.setUser,
7};

登录界面写好了,我们可以开始写第二个界面即游戏界面了,游戏界面的相关代码不再粘贴在这里,感兴趣的可以仔细阅读Game.jsx。

本文至此,大致介绍了元素战争游戏中是使用什么来开发前端页面的,开发过程中使用到了哪些组件,如何去实现一个service服务,并通过这个服务使前端和智能合约关联起来。具体的代码可以参看源码,也可以看我稍微注释的内容。官方给出的总结如下:

笔者未有过前端开发经验,写作过程中难免有误,还请各位朋友多多指正。

赞(0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址