- 原文地址:Create your first Ethereum dAPP with Web3 and Vue.JS (Part 3)
- 原文做者:Alt Street
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:sakila1012
- 校对者:allenlongbaobao,talisk
你们好,欢迎来到本系列的最后一部分。若是你还没进入情况,那么我告诉你,咱们将为以太坊区块链建立一个简单的去中心化应用程序。您能够随时查看第 1 和第 2 部分!css
到目前为止,咱们的应用程序可以从 metamask 获取并显示账户数据。可是,在更改账户时,若是不从新加载页面,则不会更新数据。这并非最优的,咱们但愿可以确保响应式地更新数据。html
咱们的方法与简单地初始化 web3 实例略有不一样。Metamask 还不支持 websockets,所以咱们将不得不每隔一段时间就去轮询数据是否有修改。咱们不但愿在没有更改的状况下调度操做,所以只有在知足某个条件(特定更改)时,咱们的操做才会与它们各自的有效负载一块儿被调度。前端
也许上述方法并非诸多解决方案中的最优解,可是它在严格模式的约束下工做,因此还算不错。在 util 文件夹中建立一个名为 pollWeb3.js 的新文件。下面是咱们要作的:vue
import Web3 from 'web3'
import {store} from '../store/'
let pollWeb3 = function (state) {
let web3 = window.web3
web3 = new Web3(web3.currentProvider)
setInterval(() => {
if (web3 && store.state.web3.web3Instance) {
if (web3.eth.coinbase !== store.state.web3.coinbase) {
let newCoinbase = web3.eth.coinbase
web3.eth.getBalance(web3.eth.coinbase, function (err, newBalance) {
if (err) {
console.log(err)
} else {
store.dispatch('pollWeb3', {
coinbase: newCoinbase,
balance: parseInt(newBalance, 10)
})
}
})
} else {
web3.eth.getBalance(store.state.web3.coinbase, (err, polledBalance) => {
if (err) {
console.log(err)
} else if (parseInt(polledBalance, 10) !== store.state.web3.balance) {
store.dispatch('pollWeb3', {
coinbase: store.state.web3.coinbase,
balance: polledBalance
})
}
})
}
}
}, 500)
}
export default pollWeb3
复制代码
如今,一旦咱们的 web3 实例被初始化,咱们就要开始轮询更新。因此,打开 Store/index.js ,导入 pollWeb3.js 文件,并将其添加到咱们的 regierWeb3Instance() 方法的底部,以便在状态更改后执行。android
import pollWeb3 from '../util/pollWeb3'
registerWeb3Instance (state, payload) {
console.log('registerWeb3instance Mutation being executed', payload)
let result = payload
let web3Copy = state.web3
web3Copy.coinbase = result.coinbase
web3Copy.networkId = result.networkId
web3Copy.balance = parseInt(result.balance, 10)
web3Copy.isInjected = result.injectedWeb3
web3Copy.web3Instance = result.web3
state.web3 = web3Copy
pollWeb3()
}
复制代码
因为咱们正在调度操做,因此须要将其添加到 store 中,并进行变异以提交更改。咱们能够直接提交更改,但为了保持模式一致性,咱们不这么作。咱们将添加一些控制台日志,以便您能够在控制台中观看精彩的过程。在 actions 对象中添加:ios
pollWeb3 ({commit}, payload) {
console.log('pollWeb3 action being executed')
commit('pollWeb3Instance', payload)
}
复制代码
如今咱们只须要对传入的两个变量进行更改git
pollWeb3Instance (state, payload) {
console.log('pollWeb3Instance mutation being executed', payload)
state.web3.coinbase = payload.coinbase
state.web3.balance = parseInt(payload.balance, 10)
}
复制代码
搞定了!若是咱们如今改变 Metamask 的地址,或者余额发生变化,咱们将看到在咱们的应用程序无需从新加载页面更新。当咱们更改网络时,页面将从新加载,咱们将从新注册一个新实例。可是,在生产中,咱们但愿显示一个警告,要求更改到部署协约的正确网络。github
我知道这是一个漫长的道路。但在下一节,咱们将最终深刻到咱们的智能协议链接到咱们的应用程序。与咱们已经作过的相比,这实际上至关容易了。web
首先,咱们将编写代码,而后部署协议并将 ABI 和 Address 插入到应用程序中。为了建立咱们期待已久的 casino 组件,须要执行如下操做:vuex
可是,首先,咱们须要咱们的应用程序可以与咱们的智能协议交互。咱们将用已经作过的一样的方法来处理该问题。在 util 文件夹中建立一个名为 getContract.js 的新文件。
import Web3 from ‘web3’
import {address, ABI} from ‘./constants/casinoContract’
let getContract = new Promise(function (resolve, reject) {
let web3 = new Web3(window.web3.currentProvider)
let casinoContract = web3.eth.contract(ABI)
let casinoContractInstance = casinoContract.at(address)
// casinoContractInstance = () => casinoContractInstance
resolve(casinoContractInstance)
})
export default getContract
复制代码
首先要注意的是,咱们正在导入一个尚不存在的文件,稍后咱们将在部署协议时修复该文件。
首先,咱们经过将 ABI(咱们将回到)传递到 web3.eth.Contact() 方法中,为稳固性协议建立一个协议对象。而后,咱们能够在一地址上初始化该对象。在这个实例中,咱们能够调用咱们的方法和事件。
然而,若是没有 action 和变体,这将是不完整的。所以,在 casino-component.vue 的脚本标记中添加如下内容。
export default {
name: ‘casino’,
mounted () {
console.log(‘dispatching getContractInstance’)
this.$store.dispatch(‘getContractInstance’)
}
}
复制代码
如今 action 和变体在 store 中。首先导入 getContract.js 文件,我相信您如今已经知道如何作到这一点了。而后在咱们建立的过程当中,调用它:
getContractInstance ({commit}) {
getContract.then(result => {
commit(‘registerContractInstance’, result)
}).catch(e => console.log(e))
}
复制代码
把结果传给咱们的变体:
registerContractInstance (state, payload) {
console.log(‘Casino contract instance: ‘, payload)
state.contractInstance = () => payload
}
复制代码
这将把咱们的协议实例存储在 store 中,以便咱们在组件中使用。
首先,咱们将添加一个数据属性(在导出中)到咱们的 casino 组件中,这样咱们就能够拥有具备响应式属性的变量。这些值将是 winEvent、amount 和 Pending。
data () {
return {
amount: null,
pending: false,
winEvent: null
}
}
复制代码
咱们将建立一个 onclick 函数来监听用户点击数字事件。这将触发协议上的 bet() 函数,显示微调器,当它接收到事件时,隐藏微调器并显示事件参数。在 data 属性下,添加一个名为 methods 的属性,该属性接收一个对象,咱们将在其中放置咱们的函数。
methods: {
clickNumber (event) {
console.log(event.target.innerHTML, this.amount)
this.winEvent = null
this.pending = true
this.$store.state.contractInstance().bet(event.target.innerHTML, {
gas: 300000,
value: this.$store.state.web3.web3Instance().toWei(this.amount, 'ether'),
from: this.$store.state.web3.coinbase
}, (err, result) => {
if (err) {
console.log(err)
this.pending = false
} else {
let Won = this.$store.state.contractInstance().Won()
Won.watch((err, result) => {
if (err) {
console.log('could not get event Won()')
} else {
this.winEvent = result.args
this.pending = false
}
})
}
})
}
}
复制代码
bet() 函数的第一个参数是在协议中定义的参数 u Number.Event.Target.innerHTML ,接下来,引用咱们将在列表标记中建立的数字。而后是一个定义事务参数的对象,这是咱们输入用户下注金额的地方。第三个参数是回调函数。完成后,咱们将监听这一事件。
如今,咱们将为组件建立 html 和 CSS。只是复制粘贴它,我认为它已经很浅显了。在此以后,咱们将部署协议,并得到 ABI 和 Address。
<template>
<div class=”casino”>
<h1>Welcome to the Casino</h1>
<h4>Please pick a number between 1 and 10</h4>
Amount to bet: <input v-model=”amount” placeholder=”0 Ether”>
<ul>
<li v-on:click=”clickNumber”>1</li>
<li v-on:click=”clickNumber”>2</li>
<li v-on:click=”clickNumber”>3</li>
<li v-on:click=”clickNumber”>4</li>
<li v-on:click=”clickNumber”>5</li>
<li v-on:click=”clickNumber”>6</li>
<li v-on:click=”clickNumber”>7</li>
<li v-on:click=”clickNumber”>8</li>
<li v-on:click=”clickNumber”>9</li>
<li v-on:click=”clickNumber”>10</li>
</ul>
<img v-if=”pending” id=”loader” src=”https://loading.io/spinners/double-ring/lg.double-ring-spinner.gif”>
<div class=”event” v-if=”winEvent”>
Won: {{ winEvent._status }}
Amount: {{ winEvent._amount }} Wei
</div>
</div>
</template>
<style scoped>
.casino {
margin-top: 50px;
text-align:center;
}
#loader {
width:150px;
}
ul {
margin: 25px;
list-style-type: none;
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-column-gap:25px;
grid-row-gap:25px;
}
li{
padding: 20px;
margin-right: 5px;
border-radius: 50%;
cursor: pointer;
background-color:#fff;
border: -2px solid #bf0d9b;
color: #bf0d9b;
box-shadow:3px 5px #bf0d9b;
}
li:hover{
background-color:#bf0d9b;
color:white;
box-shadow:0px 0px #bf0d9b;
}
li:active{
opacity: 0.7;
}
*{
color: #444444;
}
</style>
复制代码
若是您不熟悉 metamask 或以太坊网络,请不要担忧。
当全部的事情都熟悉了并作完以后,您的 Metamask 应该以下所示:
再打开 remix,咱们的协议应该还在。若是不是,请转到此要点并复制粘贴。在 ReMix 的 rop 右边,确保咱们的环境被设置为「InsistedWeb 3(Ropsten)」,而且选择了咱们的地址。
部署与第1部分中的部署相同。咱们在 Value 字段中输入几个参数来预装协议,输入构造函数参数,而后单击 Create。这一次,metamask 将提示接受/拒绝事务(约定部署)。单击「接受」并等待事务完成。
当 TX 完成后点击它,这将带你到那个 TX 的萎缩块链浏览器。咱们能够在「to」字段下找到协议的地址。你的协议虽然不一样,但看起来很类似。
咱们的协议地址在「to」字段中。
这就给了咱们地址,如今是 ABI。回到 remix 并切换到「编译」选项卡(右上角)。在协议名称旁边,咱们将看到一个名为「Details」的按钮,单击它。第四个领域是咱们的 ABI。
不错,如今咱们只须要建立前一节还不存在的一个文件。所以,在 util/constents 文件夹中建立一个名为 casinoContract.js 的新文件。建立两个变量,粘贴必要的内容并导出变量,这样咱们从上面导入的内容就能够访问它们。
const address = ‘0x…………..’
const ABI = […]
export {address, ABI}
复制代码
如今,咱们能够经过在终端中运行 npm start ,并在浏览器中运行 localhost:8080 来测试咱们的应用程序。输入金额并单击一个数字。Metamask 将提示您接受事务,旋转器将启动。在 30 秒到 1 分钟以后,咱们获得第一次确认,所以也获得了事件的确认。咱们的余额发生了变化,因此 pollweb 3 触发它的 action 来更新余额:
最终结果(左)和生命周期(右)。
若是你能在这个系列中走到这一步,我会为您鼓掌。我不是一个专业的做家,因此有时阅读起来并不容易。咱们的应用程序在主干网上已经设置好了,咱们只须要让它更漂亮一些,更友好一些。咱们将在下一节中这样作,尽管这是可选的。
咱们很快就会讲完的。它将只是一些 html、css 和 vue 条件语句,带有 v-if/v-Else。
**在 App.vue **中,将容器类添加到咱们的 div 元素中,在 CSS 中定义该类:
.container {
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
@media (min-width: 768px) {
.container {
width: 750px;
}
}
复制代码
**在 main.js 中,**导入咱们已经安装的 font-awesome 的库(我知道,这不是咱们须要的两个图标的最佳方式):
import ‘font-awesome/css/font-awesome.css’
复制代码
在 Hello-metanask.vue 中,咱们将作一些更改。咱们将在咱们的 Computed 属性中使用 mapState 助手,而不是当前函数。咱们还将使用 v-if 检查 isInjected ,并在此基础上显示不一样的 HTML。最后的组件以下所示:
<template>
<div class='metamask-info'>
<p v-if="isInjected" id="has-metamask"><i aria-hidden="true" class="fa fa-check"></i> Metamask installed</p>
<p v-else id="no-metamask"><i aria-hidden="true" class="fa fa-times"></i> Metamask not found</p>
<p>Network: {{ network }}</p>
<p>Account: {{ coinbase }}</p>
<p>Balance: {{ balance }} Wei </p>
</div>
</template>
<script>
import {NETWORKS} from '../util/constants/networks'
import {mapState} from 'vuex'
export default {
name: 'hello-metamask',
computed: mapState({
isInjected: state => state.web3.isInjected,
network: state => NETWORKS[state.web3.networkId],
coinbase: state => state.web3.coinbase,
balance: state => state.web3.balance
})
}
</script>
<style scoped>
#has-metamask {
color: green;
}
#no-metamask {
color:red;
}</style>
复制代码
咱们将执行相同的 v-if/v-else 方法来设计咱们的事件,该事件将在赌场内部返回 -Component.vue:
<div class=”event” v-if=”winEvent”>
<p v-if=”winEvent._status” id=”has-won”><i aria-hidden=”true” class=”fa fa-check”></i> Congragulations, you have won {{winEvent._amount}} wei</p>
<p v-else id=”has-lost”><i aria-hidden=”true” class=”fa fa-check”></i> Sorry you lost, please try again.</p>
</div>
#has-won {
color: green;
}
#has-lost {
color:red;
}
复制代码
最后,在咱们的 clickNumber() 函数中,在 this.winEvent=Result.args :下面添加一行:
this.winEvent._amount = parseInt(result.args._amount, 10)
复制代码
首先,项目的完整代码能够在主分支下得到:https://github.com/kyriediculous/dapp-tutorial/tree/master !
输掉赌注后的最后申请:
在咱们的应用程序中仍然有一些警告。咱们没有在任何地方正确地处理错误,咱们不须要全部的控制台日志语句,它不是一个很是完美的应用程序(我不是一个设计人员),等等。然而,这款应用程序作得很好。
但愿本教程系列可以帮助您构建更多、更好的去中心化应用程序。我真诚地但愿你和我同样喜欢读这篇文章。
我不是一个有 20 多年经验的软件工程师。所以,若是您有任何建议或改进,请随时发表意见。我喜欢学习新事物,在力所能及的范围内提升本身。谢谢。
更新:增长以太坊平衡显示
欢迎在Twitter上关注咱们,访问咱们的网站,若是您喜欢本教程,请留下提示!
TIPJAR: ETH — 0x6d31cb338b5590adafec46462a1b095ebdc37d50
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。