- 原文地址:Create your first Ethereum dAPP with Web3 and Vue.JS (Part 2)
- 原文做者:Alt Street
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:L9m
- 校對者:allen, 玉兒
歡迎回到這個很棒的系列教程的第二部分,在個教程中咱們要親身實踐,建立咱們的第一個去中心化應用(decentralized application)。在第二部分中,咱們將介紹 VueJS 和 VueX 的核心概念以及 web3js 與 metamask 的交互。前端
若是你錯過了第一部分,你能夠在下面找到,也請在 Twitter 上關注咱們。vue
VueJS 是一個用於構建用戶界面的 JavaScript 框架。初看起來,它相似傳統的 mustache(譯者注:原文爲 moustache)模板,但其實 Vue 在後面作了不少工做。android
<div id=」app」>
{{ message }}
</div>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
複製代碼
這是一個很基本的 Vue 應用的結構。數據對象中的 message 屬性會被渲染到屏幕上 id 爲「app」的元素中,當咱們改變 message 時,屏幕上的值也會實時更新。你能夠去這個 jsfiddle 上查看(開啓自動運行):jsfiddle.net/tn1mfxwr/2/。ios
VueJS 的另外一個重要特徵是組件。組件是小的、可複用的而且可嵌套的小段代碼。本質上,一個 Web 應用是由較小組件組成的組件樹構成的。當咱們着手編寫咱們前端應用時,咱們會越發清楚。git
這個頁面示例是由組件構成的。頁面由三個組件組成的,其中的兩個有子組件。github
咱們使用 Vuex 管理應用的狀態。相似於 Redux,Vuex 實現了一個對於咱們應用數據「單一數據源」的容器。Vuex 容許咱們使用一種可預見的方法操做和提供應用程序使用的數據。web
它工做的方式是很是直觀的。當組件須要數據進行渲染時,它會觸發(dispatch)一個 action 獲取所需的數據。Action 中獲取數據的 API 調用是異步的。一旦取得數據,action 會將數據提交(commit)給一個變化(mutation)。而後,Mutation 會使得咱們容器(store)的狀態發生改變(alert the state)。當組件使用的容器中的數據改變時,它會從新進行渲染。vue-router
Vuex 的狀態管理模式。vuex
在第一部分中,咱們已經經過 vue-cli 生成了一個 Vue 應用,咱們也安裝了所需的依賴。若是你沒有這樣作的話,請查看上面第一部分的連接。
若是你正確完成了各項的話,你的目錄看起來應該是這樣的:
新生成的 vue 應用。
小提示:若是你要從這裏複製粘貼代碼段的話,請在你的 .eslintignore 文件中添加 /src/,以避免出現縮進錯誤。
你能夠在終端中輸入 npm start
運行這個應用。首先咱們須要清理它包含的這個默認的 Vue 應用。 註解:儘管只有一個路由,可是咱們仍是會使用 Vue Router,雖然咱們並不須要,可是由於這個教程至關簡單,我想將其保留會更好。 貼士:在你的 Atom 編輯器右下角中將 .vue 文件設置爲 HTML 語法(高亮)
如今處理這個剛生成的應用:
<template>
<p>Hello</p>
</template>
<script>
export default {
name: 'hello-metamask'
}
</script>
<style scoped>
</style>
複製代碼
<template>
<hello-metamask/>
</template>
<script>
import HelloMetamask from '@/components/hello-metamask'
export default {
name: 'casino-dapp',
components: {
'hello-metamask': HelloMetamask
}
}
</script>
<style scoped>
</style>
複製代碼
import Vue from 'vue'
import Router from 'vue-router'
import CasinoDapp from '@/components/casino-dapp'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'casino-dapp',
component: CasinoDapp
}
]
})
複製代碼
關於 Vue Router:你能夠增長額外的路徑併爲其綁定組件,當你訪問定義的路徑時,在 App.vue 文件中的 router-view 標籤中,對應的組件會被渲染,並進行顯示。
export const NETWORKS = {
'1': 'Main Net',
'2': 'Deprecated Morden test network',
'3': 'Ropsten test network',
'4': 'Rinkeby test network',
'42': 'Kovan test network',
'4447': 'Truffle Develop Network',
'5777': 'Ganache Blockchain'
}
複製代碼
若是你在終端中執行 npm start
,並在瀏覽器中訪問 localhost:8000
,你應該能夠看到「Hello」出如今屏幕上。若是是這樣的話,就表示你準備好進入下一步了。
在這一節中,咱們要設置咱們的容器(store)。首先從在 store 目錄(上一節的最後一部分)下建立兩個文件開始:index.js 和 state.js;咱們先從 state.js 開始,它是咱們所檢索的數據一個空白表示(Blank representation)。
let state = {
web3: {
isInjected: false,
web3Instance: null,
networkId: null,
coinbase: null,
balance: null,
error: null
},
contractInstance: null
}
export default state
複製代碼
好了,如今咱們要對 index.js 進行設置。咱們會導入 Vuex 庫而且告訴 VueJS 使用它。咱們也會把 state 導入到咱們的 store 文件中。
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
Vue.use(Vuex)
export const store = new Vuex.Store({
strict: true,
state,
mutations: {},
actions: {}
})
複製代碼
最後一步是編輯 main.js ,以包含咱們的 store 文件:
import Vue from 'vue'
import App from './App'
import router from './router'
import { store } from './store/'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
複製代碼
幹得好!由於這裏有不少設置,(因此請)給你本身一點鼓勵。如今已經準備好經過 web3 API 獲取咱們 Metamask 的數據,並使其在咱們的應用發揮做用了。該來點真的了!
就像前面提到的,爲了讓 Vue 應用能獲取到數據,咱們須要觸發(dispatch)一個 action 執行異步的 API 調用。咱們會使用 promise 將幾個方法鏈式調用,並將這些代碼提取(封裝)到文件 util/getWeb3.js 中。粘貼如下的代碼,其中包含了一些有助你遵循的註釋。咱們會在代碼塊下面對它進行解析:
import Web3 from 'web3'
/* * 1. Check for injected web3 (mist/metamask) * 2. If metamask/mist create a new web3 instance and pass on result * 3. Get networkId - Now we can check the user is connected to the right network to use our dApp * 4. Get user account from metamask * 5. Get user balance */
let getWeb3 = new Promise(function (resolve, reject) {
// Check for injected web3 (mist/metamask)
var web3js = window.web3
if (typeof web3js !== 'undefined') {
var web3 = new Web3(web3js.currentProvider)
resolve({
injectedWeb3: web3.isConnected(),
web3 () {
return web3
}
})
} else {
// web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:7545')) GANACHE FALLBACK
reject(new Error('Unable to connect to Metamask'))
}
})
.then(result => {
return new Promise(function (resolve, reject) {
// Retrieve network ID
result.web3().version.getNetwork((err, networkId) => {
if (err) {
// If we can't find a networkId keep result the same and reject the promise
reject(new Error('Unable to retrieve network ID'))
} else {
// Assign the networkId property to our result and resolve promise
result = Object.assign({}, result, {networkId})
resolve(result)
}
})
})
})
.then(result => {
return new Promise(function (resolve, reject) {
// Retrieve coinbase
result.web3().eth.getCoinbase((err, coinbase) => {
if (err) {
reject(new Error('Unable to retrieve coinbase'))
} else {
result = Object.assign({}, result, { coinbase })
resolve(result)
}
})
})
})
.then(result => {
return new Promise(function (resolve, reject) {
// Retrieve balance for coinbase
result.web3().eth.getBalance(result.coinbase, (err, balance) => {
if (err) {
reject(new Error('Unable to retrieve balance for address: ' + result.coinbase))
} else {
result = Object.assign({}, result, { balance })
resolve(result)
}
})
})
})
export default getWeb3
複製代碼
第一步要注意的是咱們使用 promise 連接了咱們的回調方法,若是你不太熟悉 promise 的話,請參考此連接。下面咱們要檢查用戶是否有 Metamask(或 Mist)運行。Metamask 注入 web3 自己的實例,因此咱們要檢查 window.web3(注入的實例)是否有定義。若是是否的話,咱們會用 Metamask 做爲當前提供者(currentProvider)建立一個 web3 的實例,這樣一來,實例就不依賴於注入對象的版本。咱們把新建立的實例傳遞給接下來的 promise,在那裏咱們作幾個 API 調用:
還記得咱們說過 Vuex 容器中的 action 須要異步地進行 API 調用嗎?咱們在這裏將其聯繫起來,而後再從組件中將其觸發。在 store/index.js 中,咱們會導入 getWeb3.js 文件,調用它,而後將其(結果)commit 給一個 mutation,並讓其(狀態)保留在容器中。
在你的 import 聲明中增長:
import getWeb3 from '../util/getWeb3'
複製代碼
而後在(store 內部)的 action 對象中調用 getWeb3 並 commit 其結果。咱們會添加一些 console.log
在咱們的邏輯中,這樣作是但願讓 dispatch-action-commit-mutation-statechange 流程更加清楚,有助於咱們理解整個執行的步驟。
registerWeb3 ({commit}) {
console.log('registerWeb3 Action being executed')
getWeb3.then(result => {
console.log('committing result to registerWeb3Instance mutation')
commit('registerWeb3Instance', result)
}).catch(e => {
console.log('error in action registerWeb3', e)
})
}
複製代碼
如今咱們要建立咱們的 mutation,它會將數據存儲爲容器中的狀態。經過訪問第二個參數,咱們能夠訪問咱們 commit 到 mutation 中的數據。在 mutations 對象中增長下面的方法:
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
}
複製代碼
很棒!如今剩下要作的是在咱們的組件中觸發(dispatch)一個 action,取得數據並在咱們的應用中進行呈現。爲了觸發(dispatch)action,咱們將會用到 Vue 的生命週期鉤子。在咱們的例子中,咱們要在它建立以前觸發(dispatch)action。在 components/casino-dapp.vue 中的 name 屬性下增長如下方法:
export default {
name: 'casino-dapp',
beforeCreate () {
console.log('registerWeb3 Action dispatched from casino-dapp.vue')
this.$store.dispatch('registerWeb3')
},
components: {
'hello-metamask': HelloMetamask
}
}
複製代碼
很好!如今咱們要渲染 hello-metamask 組件的數據,咱們帳戶的全部數據都將在此組件中進行呈現。從容器(store)中得到數據,咱們須要在計算屬性中增長一個 getter 方法。而後,咱們就能夠在模板中使用大括號來引用數據了。
<template>
<div class='metamask-info'>
<p>Metamask: {{ web3.isInjected }}</p>
<p>Network: {{ web3.networkId }}</p>
<p>Account: {{ web3.coinbase }}</p>
<p>Balance: {{ web3.balance }}</p>
</div>
</template>
<script>
export default {
name: 'hello-metamask',
computed: {
web3 () {
return this.$store.state.web3
}
}
}
</script>
<style scoped></style>
複製代碼
太棒啦!如今一切都應該完成了。在你的終端(terminal)中經過 npm start
啓動這個項目,並訪問 localhost:8080
。如今,咱們能夠看到 Metamask 的數據。當咱們打開控制檯,應該能夠看到 console.log
輸出的 —— 在 Vuex 那段中的描述狀態管理模式信息。
說真的,若是你走到了這一步而且一切正常,那麼你真的很棒!這是本系列教程目前爲止,難度最大的一部分。在下一部分中,咱們將學到如何輪詢 Metamask(如:帳戶切換)的變化,並將在第一部分描述智能合約與咱們的應用相鏈接。
以防萬一你出現錯誤,在這個 Github 倉庫 的 hello-metamask 分支上有此部分完整的代碼
不要錯過本系列的最後一部分!
若是你喜歡本教程的話,請讓咱們知道,謝謝你堅持讀到最後。
ETH — 0x6d31cb338b5590adafec46462a1b095ebdc37d50
想完成本身的想法嗎?咱們提供以太坊(Ethereum)概念驗證和開發衆募。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。