欢迎回到这个很牛的教程系列的第2部分,在教程中咱们亲手构建咱们的第一个分布式应用程序。 在第二部分中,咱们将介绍VueJS和Vuex的核心概念,并引入web3js以与metamask进行交互。javascript
若是你错过了第一部分,你能够在下面找到它。前端
正事: VueJS
VueJS是一个用于构建UI的javascript框架。乍一看,它看起来与经典的moustache模板相似,但在底层为了使Vue变得响应式发生了不少事情。vue
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
这将是一个很是基础的Vue应用程序的结构。数据对象中的消息属性将渲染到id为'app'的元素的屏幕区域,当咱们更改此消息时,它将在屏幕上更新并没有需刷新。你能够在这个jsfiddle中查看它(须打开自动运行):https://jsfiddle.net/tn1mfxwr/2/ 。java
VueJS的另外一个重要特性是组件。组件是小规模的、可重用的和自包含的代码片段。本质上,Web应用程序能够抽象为一颗更小的组件树。当咱们开始编写咱们的前端应用程序时,这将会变得更加清晰。web
将网页抽象成组件的示例。该网页由三部分组成。其中两个组件具备子组件。vue-router
状态的联合:Vuex
咱们将使用Vuex来管理咱们的应用程序的状态。与redux相似,Vuex实现了一个仓库,做为咱们app相关数据的“惟一真实数据源”。Vuex容许咱们以可预测的方式操做和提供咱们的应用程序所使用的数据。vuex
它的工做方式很是简单。组件在渲染时是须要数据的,它会派发一个动做(action)来获取它所须要的数据。获取数据的API调用发生在action的async中。 一旦数据被提取完成,action就会将这些数据提交给mutation。 以后mutation会改变咱们仓库中的状态。当该组件所使用的数据在仓库中发生更改时,它将从新渲染。vue-cli
Vuex 的状态管理模式npm
在继续以前…
在第一部分中,咱们使用 vue-cli 生成了 Vue 应用程序,同时还安装了须要的依赖项。 若是你尚未完成这一部分,请点击的顶部的连接。redux
若是你正确地完成了一切,目录结构应该以下所示:
新生成的 vue 应用
注意: 若是你打算从这里复制粘贴代码,添加/src/ 到 .eslintignore 文件中,避免缩进错误。
你能够在终端输入‘npm start’来启动这个App。这个App会包含默认的vue应用,所以咱们能够将它放出来。
注意:咱们正在使用vue Router尽管只有一个route,咱们不须要它,可是由于它太简单了,我认为将它放到教程里挺好的。
提示:在.vue文件将你的atom语法(例如 bottom right)添加到HTML。
如今来清理一下这个新项目:
在app.vue中删除img-tag,同时删除在style-tags之间的全部东西。
删除components/HelloWorld.vue,新建两个文件分别名为casino-dapp.vue(咱们的主模块)和hello-metamask.vue(会包括咱们的metamask数据)
在咱们的新文件hello-metamask.vue中粘贴如下代码,这些代码如今只是在一个p-tag中显示文字‘Hello’。
Hello
如今若是你打开了router/index.js,你就会看到咱们根目录下只有一个route,它仍然指向咱们已经删掉的HelloWorld.vue模块。咱们须要将它改成指向咱们的casino-dapp.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文件中有route-view tag,正确的模块会被渲染。
在src中建立一个名为util的新文件夹。在这个文件夹中建立另外一个文件夹名为constants。建立一个名为network.js的新文件,而后将下面代码粘贴进去。当咱们代码在清理的时候,这会让咱们显示Ethereum 网络名称而不是它的id。
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:8080,你应该就能在屏幕上看到‘Hello’。若是是这样的,你就能够准备继续了。
设置咱们的 Vuex store
在这一章咱们要设置咱们的store。如今开始在咱们的全新的store目录(上一章的最后一部分)建立两个文件:index.js和state.js;咱们由state.js开始,这个文件将会做为一个新的咱们检索数据的表明。
let state = {
web3: {
isInjected: false,
web3Instance: null,
networkId: null,
coinbase: null,
balance: null,
error: null
},
contractInstance: null
}
export default state
好的,如今咱们开始在index.js中配置咱们的store。咱们要引入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: '
})
作得不错,完成了这么多的配置,给本身一点表扬。如今咱们准备好了开始经过web3 API获取咱们的metamask数据,而后让他为咱们的应用服务。就要实现了!
从Web3和Metamask开始
就像以前提到的,为了将数据获取到咱们的Vue app,咱们须要发送一个action来作异步API调用。咱们使用promises将一些调用链接到一块儿,而后将其抽象成一个文件。所以在util文件夹中建立一个名为getWeb.js的新文件。将下面的代码粘贴到里面,这些代码包含了你要遵循的至关多的注解。咱们在代码块下面也会提到。
import Web3 from 'web3'
/*
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
首先要注意的是咱们使用promises来链接回调,若是你不知道promises,查看相关连接。下一步咱们检查用户是否有Metamask(或者 Mist)在运行。Metamase注入他本身的web3的实例,咱们所以确认window.web3(被注入的实例)已经定义了。若是没有定义的话,咱们要使用Metamask建立一个web3实例做为当前provider,所以咱们不依赖被注入的实例的版本。咱们将咱们新建立的实例传给下一个promises,在这里咱们作几个API调用:
web3.version.getNetwork() 会返回咱们链接的网络ID。
web3.eth.coinbase() 返回咱们节点属于的地址,当使用Metamask的时候这会是咱们选择的帐户。
web3.eth.getBalance(
) 返回咱们做为参数传过去的地址的balance。还记得咱们说的在咱们的Vuex store中须要在一个action产生异步API调用吗?咱们如今会把它hook出来,以后从咱们的组件dispatch出去。在store/index.js中咱们会引入咱们的getWeb3.js文件,调用它而后将它commit给一个mutation,而后在咱们的store中保存。
在你的引用语句中添加
import getWeb3 from '../util/getWeb3'
而后在action对象(在你的sotre中)中咱们会调用getWeb3而后commit结果。咱们在逻辑中添加了大量的console.logs,所以咱们能够看到进程的步骤,这会让咱们对彻底的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,它会在咱们的store中将数据保存到state。经过访问第二个参数,咱们能够在mutation中访问传进commit的数据。在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
}
不错!如今还要作的就剩下从咱们的组件将action dispatch给实际检索数据,而后把它渲染到咱们的应用。为了dispatch咱们的actions,咱们要利用Vue的生命周期hooks。在咱们的例子中,咱们须要从main casino-dapp组件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函数传入computed。而后咱们能够在咱们的模板中使用curly-braces关联数据。
若是你能作到这一点,一切都很好,那么就认真的作下去。目前这块是这个系列最难的部分。在下一部分,咱们将学习如何轮询Metamask的更改(如更换帐户)和将咱们在第一部分写的智能合约链接到咱们的应用。
本文地址:https://www.oschina.net/translate/create-your-first-ethereum-dapp-with-web3-and-vue-js-part
原文地址:https://itnext.io/create-your-first-ethereum-dapp-with-web3-and-vue-js-part-2-52248a74d58a