之前我们学习solidity 并用它编写了智能合约 登上区块链 然后也做了基本的测试
但是 其实在web3时代 前端依旧扮演者非常重要的角色
我们现在就来打通web3 从合约到页面的一个管理
首先 我们还是将自己的ganache环境起起来
然后 在我们之前智能合约的项目终端执行
truffle migrate
将我们的智能合约部署上去
然后 我们用MetaMask 导入我们ganache环境的第一个用户
然后 我们拿个html文件 编写代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src = "./js/web3.min.js"></script>
</head>
<body>
<script type="module">
var web3 = new Web3(Web3.givenProvider || "http://localhost:8545");
let account = await web3.eth.requestAccounts();
</script>
</body>
</html>
注意 他这里引入的web3.min.js 你们需要本地确实有这个文件
然后 我们运行
他这里就会需要我们选择链接授权用户 我们选择刚刚导入的ganache账号
然后 我们在html中改一下代码 输出一下返回结果
再次运行 他就会将授权账号返回回来
那么 这样 就确认我们已经拿到它的一个授权了
但是 目前来讲 只是连到了我们的区块链 能拿到一些信息 但是 我们要拿区块链上的智能合约
web3.eth中有一个Contract函数
他接受两个参数
第一个参数 abi
可能很多人都熟悉API ABI的话差不多
也是基本的接口或者函数 只是 他是一种二进制的 JSON的交互智能合约 会比API更原始一点
然后 第二个参数是address
address的作用比较关键 就是 一个区块链上能部署很多智能合约 那么 它为什么就要连接你的呢?
我们每次部署智能合约都会输出这里一个contract address 就是填在里面 帮他找到你的合约的
那么 address知道怎么传了
但是 这个ABI估计大家还是有点懵
他其实就是让你传入 要用的这个合约上的接口
我们打开智能合约项目 找到 根目录下 编译生成的 build 下的 contracts下的 StudentStorage.json
这个文件 上来我们就会看到 ABI
我们直接点击 左侧 这个小箭头 将这个abi收起来
然后 将这里面的整个数组 复制 作为Contract的第一个参数 abi
在这个数组中 我们也能找到之前写的一些函数的信息
但是这里需要提醒 每次你改了智能合约 从小部署 他都会重新编译 这个json的内容也会改,所以你每次都要改这里的abi参数
可能很多人非常麻烦 之后呢确实我们可以处理到同一个文件夹 然后 我们web程序直接导入这个json 去取他里面的这个数据就好了
但是 目前来讲 我们还是快速建立功能
其实 address 也可以在build 下的 contracts下的 StudentStorage.json中找到
如图所见 如果编译过多次 那么 就会有很多个在这 直接取最后一个就好了
因为肯定是最新的
我们 HTML参考代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src = "./js/web3.min.js"></script>
</head>
<body>
<script type="module">
var web3 = new Web3(Web3.givenProvider || "http://localhost:8545");
let account = await web3.eth.requestAccounts();
console.log(account);
const StudentStorage = await new web3.eth.Contract([
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "userList",
"outputs": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "uint256",
"name": "age",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function",
"constant": true
},
{
"inputs": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "uint256",
"name": "age",
"type": "uint256"
}
],
"name": "addtList",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "queryList",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "uint256",
"name": "age",
"type": "uint256"
}
],
"internalType": "struct StudentStorage.userData[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function",
"constant": true
}
],
"0x9d80C47da52731Df5649Ff81f8EAB664c0c80429"
);
console.log(StudentStorage);
</script>
</body>
</html>
当然 Contract的函数 你们不能直接抄 但是什么 我已经说的够清楚了
我们页面控制台输出结果如下
能输出这个智能合约 我们就成功了
可能有人就会想 那么 既然我们用StudentStorage接了这个智能合约对象
是不是我们直接 StudentStorage.函数 就可以调用了呢?
其实不然
我们打开这个对象 发现 之前我们写的函数 并不在里面
不用急
我们里面必然会有一个 methods 打开你就会发现 我们的函数就都在里面了
然后 我们将html代码改写如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src = "./js/web3.min.js"></script>
</head>
<body>
<input type="text" id="myname">
<input type="number" id="myage">
<button id="add">添加</button>
<script type="module">
var web3 = new Web3(Web3.givenProvider || "http://localhost:8545");
let account = await web3.eth.requestAccounts();
console.log(account);
const StudentStorage = await new web3.eth.Contract([
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "userList",
"outputs": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "uint256",
"name": "age",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function",
"constant": true
},
{
"inputs": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "uint256",
"name": "age",
"type": "uint256"
}
],
"name": "addtList",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "queryList",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "uint256",
"name": "age",
"type": "uint256"
}
],
"internalType": "struct StudentStorage.userData[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function",
"constant": true
}
],
"0x9d80C47da52731Df5649Ff81f8EAB664c0c80429"
);
console.log(StudentStorage);
async function getList() {
let res = await StudentStorage.methods.queryList().call();
console.log(res);
}
add.onclick = async function() {
console.log(myname.value,myage.value)
await StudentStorage.methods.addtList(myname.value,myage.value).send({from: account[0]})
getList();
}
getList();
</script>
</body>
</html>
这里 我们通过智能合约返回的StudentStorage对象做了一些操作
首先是 getList 这个方法异步调用了我们写在合约上的 queryList 函数 获取数组的长度
然后输出
因为我们之前合约上getList声明了一个 view 这样 表示 我们这个函数是不会改变合约上的内容 也不会改变区块链的
我们只是做个访问 所以 我们声明call 告诉它我们这个方法是不需要消耗燃料的
那么 我们的添加函数 调用了addtList 传入了 我们的 myname输入框 和 myage输入框的value
至于这个add.onclick 这里是一个基础 我们html中 调用id内容声明事件是直接可以通过id名去绑定的
然后 调用了addtList 将这些函数添加进去 但是 这个函数是改变了我们合约内容的 所以它会消耗燃料
那么 这个谁来承担呢?
我们web3.eth.requestAccounts()获取了MetaMask的账号授权列表 我们直接取0下标 然第一个用户去扣这个燃料
我们尝试输入内容 然后点击添加
然后 我们在控制台 console 中 输出的myname.value,myage.value在控制台上了 然后MetaMask弹出 让我们确认燃料
我们直接确定
我们知道 当我们执行成功 他会调用getList
这里 也成功展示出我们添加后的数组
但是 智能合约添加的操作 很多会要当前用户的信息 例如我们这里添加一个用户 他会需要知道 谁记录的这条信息 就是 在操作是顺带将操作的这个用户的信息传过来