- 原文地址:Create your first Ethereum dAPP with Web3 and Vue.JS (Part 2)
- 原文作者:Alt Street
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:L9m
- 校對者:allen, 玉兒
- 使用 Web3 和 Vue.js 來建立你的第一個以太坊 dAPP(第一部分)
- 使用 Web3 和 Vue.js 來建立你的第一個以太坊 dAPP(第二部分)
- 使用 Web3 和 Vue.js 來建立你的第一個以太坊 dAPP(第三部分)
歡迎回到這個很棒的系列教程的第二部分,在個教程中我們要親身實踐,建立我們的第一個去中心化應用(decentralized application)。在第二部分中,我們將介紹 VueJS 和 VueX 的核心概念以及 web3js 與 metamask 的互動。
如果你錯過了第一部分,你可以在下面找到,也請在 Twitter 上關注我們。
進入正題:VueJS
VueJS 是一個用於構建使用者介面的 JavaScript 框架。初看起來,它類似傳統的 mustache(譯者注:原文為 moustache)模板,但其實 Vue 在後面做了很多工作。
<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/。
VueJS 的另一個重要特徵是元件。元件是小的、可複用的並且可巢狀的小段程式碼。本質上,一個 Web 應用是由較小元件組成的元件樹構成的。當我們著手編寫我們前端應用時,我們會愈加清楚。
這個頁面示例是由元件構成的。頁面由三個元件組成的,其中的兩個有子元件。
狀態的整合: Vuex
我們使用 Vuex 管理應用的狀態。類似於 Redux,Vuex 實現了一個對於我們應用資料「單一資料來源」的容器。Vuex 允許我們使用一種可預見的方法操作和提供應用程式使用的資料。
它工作的方式是非常直觀的。當元件需要資料進行渲染時,它會觸發(dispatch)一個 action 獲取所需的資料。Action 中獲取資料的 API 呼叫是非同步的。一旦取得資料,action 會將資料提交(commit)給一個變化(mutation)。然後,Mutation 會使得我們容器(store)的狀態發生改變(alert the state)。當元件使用的容器中的資料改變時,它會重新進行渲染。
Vuex 的狀態管理模式。
在我們繼續之前…
在第一部分中,我們已經通過 vue-cli 生成了一個 Vue 應用,我們也安裝了所需的依賴。如果你沒有這樣做的話,請檢視上面第一部分的連結。
如果你正確完成了各項的話,你的目錄看起來應該是這樣的:
新生成的 vue 應用。
小提示:如果你要從這裡複製貼上程式碼段的話,請在你的 .eslintignore 檔案中新增 /src/,以免出現縮排錯誤。
你可以在終端中輸入 npm start
執行這個應用。首先我們需要清理它包含的這個預設的 Vue 應用。
註解:儘管只有一個路由,但是我們還是會使用 Vue Router,雖然我們並不需要,但是因為這個教程相當簡單,我想將其保留會更好。
貼士:在你的 Atom 編輯器右下角中將 .vue 檔案設定為 HTML 語法(高亮)
現在處理這個剛生成的應用:
- 在 app.vue 中刪除 img 標籤和 style 標籤中的內容。
- 刪除 components/HelloWorld.vue,建立兩個名為 casino-dapp.vue(我們的主元件)和 hello-metamask.vue(將包含我們的 Metamask 資料)的兩個新檔案。
- 在我們的新 hello-metamask.vue 檔案中貼上下面的程式碼,它現在只顯示了在一個 p 標籤內的「hello」文字。
<template>
<p>Hello</p>
</template>
<script>
export default {
name: `hello-metamask`
}
</script>
<style scoped>
</style>
複製程式碼
- 現在我們首先匯入 hello-metamask 元件檔案,通過匯入檔案將其載入到主元件 casino-app 中,然後在我們的 vue 例項中,引用它作為模板中一個標籤。在 casino-dapp.vue 中貼上這些程式碼:
<template>
<hello-metamask/>
</template>
<script>
import HelloMetamask from `@/components/hello-metamask`
export default {
name: `casino-dapp`,
components: {
`hello-metamask`: HelloMetamask
}
}
</script>
<style scoped>
</style>
複製程式碼
- 現在如果你開啟 router/index.js 你會看到 root 下只有一個路由,它現在仍指向我們已刪除的 HelloWorld.vue 元件。我們需要將其指向我們主元件 casino-app.vue。
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 標籤中,對應的元件會被渲染,並進行顯示。
- 在 src 中建立一個名為 util 的新資料夾,在這個資料夾中建立另一個名為 constants 的新資料夾,並建立一個名為 networks.js 的檔案,貼上下面的程式碼。我們用 ID 來代替以太坊(Ethereum)網路名稱顯示,這樣做會保持我們程式碼的整潔。
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`
}
複製程式碼
- 最後的但同樣重要的(實際上現在用不到)是,在 src 中建立一個名為 store 的新資料夾。我們將在下一節繼續討論。
如果你在終端中執行 npm start
,並在瀏覽器中訪問 localhost:8000
,你應該可以看到「Hello」出現在螢幕上。如果是這樣的話,就表示你準備好進入下一步了。
設定我們的 Vuex 容器
在這一節中,我們要設定我們的容器(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 的資料,並使其在我們的應用發揮作用了。該來點真的了!
入門 Web3 和 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 呼叫:
- web3.version.getNetwork() 將返回我們連線的網路 ID。
- web3.eth.coinbase() 返回我們節點挖礦的地址,當使用 Metamask 時,它應該會是已選擇的賬戶。
- web3.eth.getBalance(<address>) 返回作為引數傳入的該地址的餘額。
還記得我們說過 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、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。