- 原文地址: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、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。