DApp实战:开发属于你的第一个DApp - 我的日记本
效果预览
项目的视频教程部分已经发布到了b站
https://space.bilibili.com/391924926/channel/seriesdetail?sid=2745034
初始化状态
添加
删除
开发环境准备
系统环境
- Remix
- Ganache
- nodejs最新版
- metamask
开发框架
- vue-cli脚手架
- web3.js
- element-ui
vue-cli脚手架创建工程
vue create my-node
Vue CLI v5.0.1
┌─────────────────────────────────────────────┐
│ │
│ New version available 5.0.1 → 5.0.8 │
│ Run yarn global add @vue/cli to update! │
│ │
└─────────────────────────────────────────────┘? Please pick a preset: Default ([Vue 2] babel, eslint)Vue CLI v5.0.1
✨ Creating project in /Users/sleep/Desktop/my-node.
�� Initializing git repository...
⚙️ Installing CLI plugins. This might take a while...yarn install v1.22.17
info No lockfile found.
[1/4] �� Resolving packages...
[2/4] �� Fetching packages...
[3/4] �� Linking dependencies...success Saved lockfile.
info To upgrade, run the following command:
$ curl --compressed -o- -L https://yarnpkg.com/install.sh | bash
✨ Done in 14.18s.
�� Invoking generators...
�� Installing additional dependencies...yarn install v1.22.17
[1/4] �� Resolving packages...
[2/4] �� Fetching packages...
[3/4] �� Linking dependencies...
[4/4] �� Building fresh packages...success Saved lockfile.
✨ Done in 4.74s.
⚓ Running completion hooks...�� Generating README.md...�� Successfully created project my-node.
�� Get started with the following commands:$ cd my-node
$ yarn serve
安装依赖库
npm 安装 elementUI,web3js
npm i element-ui web3 -S
Solidity编写智能合约
注意点:
删除数组的练习, 我们发现数组元素不会真正删除 只会置为0值
1,2,3,5 => 1,2,0,5
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;contract Array {uint256[] public arr=[1,2,3,4];constructor() {arr[0] = 1;arr[1] = 2;arr[2] = 3;arr[3] = 5;}function addArr(uint _x) public {arr.push(_x);}function deleteArr(uint256 i) public {delete arr[i];}function getArr() public view returns (uint256[] memory) {return arr;}
}
数组无法直接将某个元素删除,只能从后向前逐个删除。基于这个特性,我需要通过把把元素从右往左移动从而删除元素 保留元素的顺序
// [1, 2, 3] -- remove(1) --> [1, 3, 3] --> [1, 3]
// [1, 2, 3, 4, 5, 6] -- remove(2) --> [1, 2, 4, 5, 6, 6] --> [1, 2, 4, 5, 6]
// [1, 2, 3, 4, 5, 6] -- remove(0) --> [2, 3, 4, 5, 6, 6] --> [2, 3, 4, 5, 6]
function remove(uint _index) public {require(_index < arr.length, "index out of bound");for (uint i = _index; i < arr.length - 1; i++) {arr[i] = arr[i + 1];}arr.pop();//交换后再把最后一个元素移除
}
我们也可以把最后一个元素copy 到被移动元素的位置从而达到移除元素。无序。
function remove(uint index) public {arr[index] = arr[arr.length - 1];arr.pop();//删除最后一个元素
}
最终合约代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;contract Note {// 定义日记本数据结构struct node {string name;string content;string date;}//定义每个用户自己的日记本列表存储对象mapping(address => node[]) public userNodeList;//添加成功的通知event addSuccess(address);//删除成功的通知event deleteSuccess(address);//获取当前用户的日记列表function getUserNodeList() external view returns (node[] memory) {return userNodeList[msg.sender];}//添加一条日记function addNode(string memory _name,string memory _content,string memory _date) external {userNodeList[msg.sender].push(node({name: _name, content: _content, date: _date}));emit addSuccess(msg.sender);}//删除一条日记function deleteNode(uint256 _x) external {require(userNodeList[msg.sender].length > _x, "out of index");for (uint256 i = _x; i < userNodeList[msg.sender].length - 1; i++) {userNodeList[msg.sender][i] = userNodeList[msg.sender][i + 1];}userNodeList[msg.sender].pop();emit deleteSuccess(msg.sender);}
}
编写前端界面样式
在 main.js 中写入以下内容:
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false//先导入elementUI库
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);new Vue({render: h => h(App),
}).$mount('#app')
把前端的html结构搭建出来
<template><div id="app"><el-button type="primary" size="mini" @click="addNode">添加</el-button><el-table:data="tableData"style="width: 100%; "><el-table-columnprop="date"label="日期"width="180"></el-table-column><el-table-columnprop="name"label="标题"width="180"></el-table-column><el-table-columnprop="content"label="内容"><template v-slot="scope"><el-button @click="getContent(scope.row.content)">查看</el-button></template></el-table-column><el-table-column><template v-slot="scope"><el-button @click="deleteNode(scope.$index)" size="mini">删除</el-button></template></el-table-column></el-table><el-dialog :visible.sync="open"><el-form ref="form" :model="form" label-width="80px"><el-form-item label="标题"><el-input type="text" v-model="form.name"/></el-form-item><el-form-item label="内容"><el-input type="textarea" v-model="form.name"/></el-form-item><el-form-item label="时间"><el-date-picker v-model="form.date" type="datetime" placeholder="请输入时间"/></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="open = false">取 消</el-button><el-button type="primary" @click="open = false">确 定</el-button></div></el-dialog><el-dialog :visible.sync="contentOpen"><div>{{ form.content }}</div></el-dialog></div>
</template><script>export default {name: 'App',components: {},data() {return {tableData: [{date: '2016-05-02',name: '王小虎',content: '上海市普陀区金沙江路 1518 弄'}],form: {name: "",content: "",date: "",},open: false,contentOpen: false}},methods: {addNode() {this.open = true},async deleteNode(index) {console.log(index)try {let res = await this.$confirm("确定要删除吗?");if (!res) {this.$message.error("删除失败")}//todo 调用删除合约方法} catch (e) {console.log(e)}},getContent(content) {this.contentOpen = truethis.form.content = content}}
}
</script>
准备区块链环境
Ganache
官网:https://trufflesuite.com/docs/ganache/
Ganache 是一个用与本地开发的区块链,用于在Ethereum区块链上开发去中心化的应用程序。Ganache 模拟了Ethereum网络,你可以在发布到生产环境之前看到你的 DApp 将如何执行。
Ganache 有两种形式:UI 和 CLI。
1.下载Ganache https://trufflesuite.com/ganache/,选择适合您操作系统的版本和安装文件
2。按照提示进行安装。
3。启动应用
首次打开会看到这样的界面
4.启动节点
- 点击quickstart按钮启动一个ETH节点
- 创建工作区后,屏幕将显示有关服务器的一些详细信息,并列出一些帐户。每个账户自带100ETH 。这种模式启动,数据只保存在内存中,关闭重新打开数据会重新初始化。另请注意,您可以从顶部的搜索框中搜索区块编号或交易哈希。
- 点击 New workspace 创建一个新的工作区
- 需要预先配置参数
- 定义工作区的名称和描述
- 定义服务相关参数配置
- 可以配置每个地址默认分配的ETH数量和账户总数等信息
- 配置好以后点击
save workspace
完成最终配置。数据会保存到本地磁盘中,下次再启动应用还会重新加载数据。
web3.js调用合约
初始化web3js
import Web3 from 'web3';const web3 = new Web3(window.ethereum)
console.log(web3.version)
实例化合约
const contract = new web3.eth.Contract([abi], "合约地址");
调用合约方法
contract.methods.方法名(参数)//读取类的用 call.call({from: account})//写入类的用 send.send({from: account})
合约事件监听
myContract.events.event({filter: {},fromBlock: 'latest'}).on("connected", (subscriptionId) => {console.log("connected", subscriptionId);}).on('data', event => {console.log('data',event);//可以在这里写逻辑,比如刷新列表this.getUserNodeList(event.returnValues[0])}).on('changed', event => {// 从本地数据库中删除事件console.log('changed', event)}).on('error', (error, receipt) => {// 如果交易被网络拒绝并带有交易收据,第二个参数将是交易收据。console.log(error, receipt)});
最终代码
<template><div id="app"><div v-if="!view"> {{ tips }}</div><div v-else><el-button type="primary" size="mini" @click="addNode">添加</el-button><el-table:data="tableData"style="width: 100%; "><el-table-columnprop="date"label="日期"width="180"></el-table-column><el-table-columnprop="name"label="标题"width="180"></el-table-column><el-table-columnprop="content"label="内容"><template v-slot="scope"><el-button @click="getContent(scope.row.content)">查看</el-button></template></el-table-column><el-table-column><template v-slot="scope"><el-button @click="deleteNode(scope.$index)" size="mini">删除</el-button></template></el-table-column></el-table><el-dialog :visible.sync="open"><el-form ref="form" :model="form" label-width="80px"><el-form-item label="标题"><el-input type="text" v-model="form.name"/></el-form-item><el-form-item label="内容"><el-input type="textarea" v-model="form.content"/></el-form-item><el-form-item label="时间"><el-date-picker v-model="form.date" type="datetime" placeholder="请输入时间"/></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="open = false">取 消</el-button><el-button type="primary" @click="saveNode">确 定</el-button></div></el-dialog><el-dialog :visible.sync="contentOpen"><div>{{ form.content }}</div></el-dialog></div></div>
</template><script>
import Web3 from "web3";export default {name: 'App',components: {},data() {return {tableData: [{date: '2016-05-02',name: '王小虎',content: '上海市普陀区金沙江路 1518 弄'}],form: {name: "",content: "",date: "",},open: false,contentOpen: false,view: false,tips: "",web3: null,contract: null,//部署合约后填写正确的合约地址contractAddress: "0x........",abi: [//....从json获取],accounts: []}},async mounted() {this.view = this.checkWallet()this.checkNetwork(0)await this.eventChange('connect')await this.eventChange('accountsChanged')await this.eventChange('chainChanged')this.accounts = await this.enableAccounts()console.log(this.accounts[0])this.web3 = await this.initWeb3js();this.contract = new this.web3.eth.Contract(this.abi, this.contractAddress);this.watchEvents(this.contract.events.addSuccess);this.watchEvents(this.contract.events.deleteSuccess);console.log(this.contract.methods)await this.getUserNodeList(this.accounts[0])},methods: {addNode() {this.open = true},async deleteNode(index) {console.log(index)try {let res = await this.$confirm("确定要删除吗?");if (!res) {this.$message.error("删除失败")}//todo 调用删除合约方法try {let res = await this.contract.methods.deleteNode(index).send({from: this.accounts[0]})console.log(res)await this.getUserNodeList(this.accounts[0])} catch (e) {this.$message.error(e)}} catch (e) {console.log(e)}},getContent(content) {this.contentOpen = truethis.form.content = content},/*web3js 相关方法*/async checkWallet() {if (typeof window.ethereum === 'undefined') {this.tips = "请安装钱包插件";return false;}return true;},checkNetwork(networkId) {if (!window.ethereum.isConnected()) {console.log('connect error!', networkId);this.view = false;this.tips = "请配置钱包链接地址";return false;}this.view = true;return true},async eventChange(event) {try {window.ethereum.on(event, async (data) => {console.log(data)switch (event) {case "connect":console.log(data)this.checkNetwork(data)break;case "networkChanged":this.checkNetwork(data)break;case 'accountsChanged':console.log(data)//这里注意一定要重新复制accountthis.accounts = data;await this.getUserNodeList(data[0]);break}})} catch (e) {this.$message.error(e)}},initWeb3js() {return new Web3(window.ethereum)},async enableAccounts() {try {return await window.ethereum.request({method: 'eth_requestAccounts'});} catch (e) {this.$message.error(e)}},watchEvents(event) {event({filter: {},fromBlock: 'latest'}).on("connected", (subscriptionId) => {//当订阅成功连接时触发一次。返回订阅 id。console.log("connected", subscriptionId);}).on('data', event => {console.log('data',event);//可以在这里写逻辑,比如刷新列表this.getUserNodeList(event.returnValues[0])}).on('changed', event => {//当事件从区块链上移除时触发。 // 从本地数据库中删除事件console.log('changed', event)}).on('error', (error, receipt) => {//当订阅中出现错误时触发。// 如果交易被网络拒绝并带有交易收据,第二个参数将是交易收据。console.log(error, receipt)});},/*合约函数*/async getUserNodeList(account) {this.tableData = await this.contract.methods.getUserNodeList().call({from: account})},async saveNode() {let {name, content, date} = this.formconsole.log(this.form)try {let res = await this.contract.methods.addNode(name, content, date.toString()).send({from: this.accounts[0]})console.log(res)this.open = falsethis.form = {}await this.getUserNodeList(this.accounts[0])} catch (e) {this.$message.error(e)}},}
}
</script>
DApp实战:开发属于你的第一个DApp - 我的日记本相关推荐
- 技术员如何开发一个DAPP区块链应用(以宠物商店为例)
1. 文章摘要 [本文目标] 通过逐步的指导和截图举证,一步步带领一个技术新手完成一个宠物商店DAPP应用的开发和部署. [环境前置条件] 参考<第一课 如何在WINDOWS环境下搭建以太坊开发 ...
- 第七课 技术小白如何开发一个DAPP区块链应用(以宠物商店为例)
1. 文章摘要 [本文目标] 通过逐步的指导和截图举证,一步步带领一个技术小白完成一个宠物商店DAPP应用的开发和部署. [环境前置条件] 参考<第一课 如何在WINDOWS环境下搭建以太坊开发 ...
- 什么是Dapp?带你从零开始搭建一个Dapp
什么是Dapp?零基础带你搭建一个Dapp 前言:Dapp就是去中心化应用,它和我们平时使用的App(微信,支付宝等)只差了一个去中心化,如何理解这一去中心化?从体验层面来说:Dapp中并没有管理者, ...
- 以太坊开发入门,如何搭建一个区块链DApp投票系统
点击关注异步图书,置顶公众号 每天与你分享 IT好书 技术干货 职场知识 第一节 概述 对于初学者,需要了解以太坊开发相关的基本概念,另外就是如何构建一个基于以太坊的完整去中心化应用例如一个区块链投票 ...
- webpack原理篇(六十二):实战开发一个自动合成雪碧图的loader
说明 玩转 webpack 学习笔记 支持的语法 对样式里面图片引用后面加 __sprite 进行图片合并 如何将两张图片合成一张图片? 使用 spritesmith https://github.c ...
- 鸿蒙系统开发实战-开发一个聊天技巧软件堪称聊天神器
鸿蒙开发实战-开发一个聊天助手APP 鸿蒙系统开发实战-开发一个聊天技巧软件堪称聊天神器.目前鸿蒙系统可真是过了一把自主研发的瘾,通过一个鸿蒙程序开发实战教程来演示如何开发一款聊天神器,视频教程放在了 ...
- 【Android开发VR实战】一.给用户呈现一个360°全景图片
转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53905681 本文出自[DylanAndroid的博客] [Android开发 ...
- 搭建第一个Dapp应用——存证签证(DAPP开发)——2021.5.4
搭建第一个Dapp应用(1)--搭建FISCO BCOS联盟链 搭建第一个Dapp应用(2)--搭建WeBase-Front中间件 搭建第一个Dapp应用(3)--Solidity防伪溯源存证签证合约 ...
- [零基础学JAVA]Java SE实战开发-37.MIS信息管理系统实战开发[JDBC](1)
MIS信息管理系统实战开发之使用MySQL实现保存 开发背景 ID.姓名.年龄为公共信息,而学生有成绩,工人有工资 定义一个抽象类Person(ID.姓名.年龄),学生是其子类,有成绩,工人是其子类有 ...
最新文章
- 快速排序的递归和非递归实现 c语言版本
- SDNU 1209.磊磊的随机数
- linux新建备份数据库的脚本文件,Linux下shell脚本:自动每日备份网站文件和数据库上传FTP空间...
- 锁屏壁纸开发 Android,Android开发自己的锁屏壁纸
- html如何绘制棒棒糖,如何使用css来画一个棒棒糖
- ckc交易什么意思_1379ip0在股市是什么意思,600875东方电气股票,股市交易手续费计算...
- webstorm简单介绍,webstrom基本使用
- 深度学习基础(十)—— 稀疏编码(二)
- 【Verilog设计—数字传输系统】ASK调制与FSK调制
- Linux 编译debug内核
- 嵌入式Linux开发环境搭建
- 魏吉英:IPONE5越狱
- 关闭windows锁屏,提升开机速度
- PHP登陆页面完整代码
- Easy Excel使用说明
- JavaScript 面向对象(二) —— 案例篇
- k8s.io/client-go@v0.20.2/tools/cache/reflector.go:167: Failed to watch *v1beta1.Ingress: failed to l
- ubuntu18.04使用xrdp远程连接
- 队名 Booqmz 具体读音可以详见谷歌翻译声优~
- 2021年焊工(技师)考试技巧及焊工(技师)模拟考试题库
热门文章
- Android附近基站+Wifi+IP+GPS多渠道定位方案
- 3款不为人知的黑科技软件,每一款都良心十足
- ios调用百度地图,与在ios中通过url进行百度地图的跳转进行导航
- MYSQL创建课程表course_MySQL创建表
- 企业架构与数字化转型的实践方法
- 人工学院2卡顿_玩人工学院2特别卡 | 手游网游页游攻略大全
- IMG Series4 NNA实现高效推理
- 高效治理磷酸铁锂生产废水 让其不再是行业发展的“绊脚石”
- 六、关于阿里云CentOS7被挖矿木马程序入侵的解决办法
- nucleo stm32 h743 FREERTOS CUBE MX配置小记录