转载地址
前言
最近DAPP的开发貌似很火,学习了区块链的一些知识之后,相信有很多人和我一样,也想了解开发一个DAPP是一个怎样的流程。
下面将通过一个简单的栗子来初识一下DAPP的开发流程,届时,我们也将开发出第一个DAPP应用–《永存的留言》。
在线体验(Ludis):。
项目介绍
《永存的留言》是一个基于以太坊的在线留言平台。它的功能十分简单–用户可以在平台上进行留言,平台每10s随机的展示留言内容。
但是它的特点在于,利用区块链的特性,保证了数据的真实性、完整性和安全性。- 使用Solidity开发后端方法
- 使用Truffle框架
- 基于
unbox react
- 部署在以太坊测试网络上
Ropoetn Test Network
- 使用MetaMask钱包插件发布交易
开发步骤
下载react项目模板
确保本地已经准备好Truffle所需的环境,准备以下命令,下载react项目模板。
$ mkdir a && cd a
truffle unbox react
当看到 Unbox successful. Sweet!
提示时,表明下载成功。 编写智能合约
这是我们唯一的实现合约,包含的发送留言和读取留言的方法。
pragma solidity ^0.4.19;contract SimpleStorage { // 留言结构体 struct Message { string word; // 留言 address from; // 留言者地址 string timestamp ; // 留言unix时间戳 } Message[] private wordArr; /** * 写入留言的方法 */ function setWord(string s, string t) public { wordArr.push(Message({ word: s, from: msg.sender, timestamp: t })); } /** * 获取随机留言的方法 */ function getRandomWord(uint random) public view returns (uint, string, address, string) { if(wordArr.length==0){ return (0, "", msg.sender, ""); }else{ Message storage result = wordArr[random]; return (wordArr.length, result.word, result.from, result.timestamp); } }}
编译、部署合约
修改发布的脚本。
var SimpleStorage = artifacts.require("./SimpleStorage.sol");//var Words = artifacts.require("Words");module.exports = function(deployer) { deployer.deploy(SimpleStorage); //deployer.deploy(Words);};
执行truffle compile
进行合约的编译。
获取合约地址。
- 这里我们打开MetaMask钱包插件,左上角选择
Ropoetn Test Network
网络. - 利用Remix编译,发布合约到以太坊测试环境。
- 通过MetaMask的交易hash查询,获取已经部署到以太坊测试网络中的合约地址。
编写前端页面
这个部分主要是编写前端的展示效果和与合约交互的逻辑,这一部分最难编写,也最耗时间。
- 主要逻辑代码
const contractAddress = "0x39e5196750dcddb1aaf6dda7d6e8dbb633482905" // 合约地址(以太坊测试网络)var simpleStorageInstance // 合约实例class App extends Component { // 初始化构造 constructor(props) { super(props) this.state = { word: null, from: null, timestamp: null, random: 0, count: 0, input: '', web3: null, emptyTip: "还没有留言,快来创建全世界第一条留言吧~", firstTimeLoad: true, loading: false, loadingTip: "留言正在写入,请耐心等待~", waitingTip: "留言正在写入,请耐心等待~", successTip: "留言成功", animate: "", in: css(styles.in), out: css(styles.out) } } // 获取Web3实例 componentWillMount() { // Get network provider and web3 instance. getWeb3 .then(results => { this.setState({ web3: results.web3 }) // Instantiate contract once web3 provided. this.instantiateContract() }) .catch(() => { console.log('Error finding web3.') }) } // 获取合约实例 instantiateContract() { /* * SMART CONTRACT EXAMPLE * * Normally these functions would be called in the context of a * state management library, but for convenience I've placed them here. */ const contract = require('truffle-contract') const simpleStorage = contract(SimpleStorageContract) simpleStorage.setProvider(this.state.web3.currentProvider) // Get accounts. this.state.web3.eth.getAccounts((error, accounts) => { simpleStorage.at(contractAddress).then(instance => { simpleStorageInstance = instance /*simpleStorage.deployed().then((instance) => { simpleStorageInstance = instance // 部署本地Ganache*/ console.log("合约实例获取成功") }) .then(result => { return simpleStorageInstance.getRandomWord(this.state.random) }) .then(result => { console.log("读取成功", result) if(result[1]!=this.setState.word){ this.setState({ animate: this.state.out }) setTimeout(() => { this.setState({ count: result[0].c[0], word: result[1], from: result[2], timestamp: result[3], animate: this.state.in, firstTimeLoad: false }) }, 2000) }else{ this.setState({ firstTimeLoad: false }) } this.randerWord() }) }) } // 循环从区块上随机读取留言 randerWord() { setInterval(() => { let random_num = Math.random() * (this.state.count? this.state.count: 0) this.setState({ random: parseInt(random_num) }) console.log("setInterval读取", this.state.random) simpleStorageInstance.getRandomWord(this.state.random) .then(result => { console.log("setInterval读取成功", result) if(result[1]!=this.setState.word){ this.setState({ animate: this.state.out }) setTimeout(() => { this.setState({ count: result[0].c[0], word: result[1], from: result[2], timestamp: result[3], animate: this.state.in }) }, 2000) } }) }, 10000) } // 写入区块链 setWord(){ if(!this.state.input) return this.setState({ loading: true }) let timestamp = new Date().getTime() simpleStorageInstance.setWord(this.state.input, String(timestamp), {from: this.state.web3.eth.accounts[0]}) .then(result => { this.setState({ loadingTip: this.state.successTip }) setTimeout(() => { this.setState({ loading: false, input: '', loadingTip: this.state.waitingTip }) }, 1500) }) .catch(e => { // 拒绝支付 this.setState({ loading: false }) }) } // 时间戳转义 formatTime(timestamp) { let date = new Date(Number(timestamp)) let year = date.getFullYear() let month = date.getMonth() + 1 let day = date.getDate() let hour = date.getHours() let minute = date.getMinutes() let second = date.getSeconds() let fDate = [year, month, day, ].map(this.formatNumber) return fDate[0] + '年' + fDate[1] + '月' + fDate[2] + '日' + ' ' + [hour, minute, second].map(this.formatNumber).join(':') } /** 小于10的数字前面加0 */ formatNumber(n) { n = n.toString() return n[1] ? n : '0' + n }}
运行项目
使用npm start
启动项目,浏览器的3000端口运行。
Ropoetn Test Network
网络才可以)。 总结
这样我们就开发出了我们的第一个DAPP,体会了开发的基本流程。
- 明确项目需求
- 确定开发环境
- 编写、测试、部署合约
- 设计前端页面,使前后端交互
- 发布测试