第三步
因此,您已经掌握了基础知识。在上一节中,您开发了一个智能合约,并使用Truffle进行了部署。但是,在上一节中,您的智能合约已部署到本地开发网络中-没什么好玩的,因为只有您可以部署事物并与该本地测试网络进行交互!我们要朋友!并访问其他人已部署的其他智能合约!
因此,在本部分中,我们将过渡到使用公共的以太坊测试网,以便您可以参与以太坊生态系统周围发生的所有操作!
让我们开始吧!
首先,我们将讨论您如何访问这些公共以太坊网络.
要访问这些网络,您需要连接到已连接到相应网络的节点。您可以将每个以太坊网络视为自己的以太坊小世界,也可以将以太坊节点视为每个世界的网关或接入点!因为以太坊是一个分布式网络,所以每个以太坊节点都存储与其连接的网络的整个状态(有些节点不需要存储完整状态,但现在不必担心),并且可以不断进行通信与网络中的其他节点保持最新状态!因此,为了能够读取和写入此状态,我们需要访问这些节点之一.
您可以很好地使用当前可用的许多以太坊客户端之一(Hyperledger Besu(由ConsenSys开发的Java客户端),Geth(Go客户端),Parity(Rust客户端)等)托管自己的节点–但是,这里有很多托管和维护自己的以太坊节点会带来一些DevOps开销–特别是如果您想可靠地做到这一点!因此,我们在ConsenSys建立了世界一流的以太坊基础设施产品Infura。 Infura会为您处理整个“节点管理”部分,为您提供对以太坊节点集群的即时,可靠和可扩展的访问!您可以将Infura视为“以太坊节点即服务” 🙂
Infura入门
要开始使用Infura,您需要在以下位置注册一个帐户 信息. 不用担心-它完全是免费的入门指南,您无需输入任何敏感信息!
注册后,您将被定向到如下所示的页面:
就像该页面所建议的那样,要开始使用,您将选择第一个选项“开始使用并创建您的第一个项目以访问以太坊网络!”
您可以随意命名项目-我们将其命名为“测试项目”.
现在,将为您提供访问Infura节点所需的凭据!
保持此页面打开状态!我们待会儿会再说的 🙂
接下来,我们将初始化一个新的Truffle项目。如果您需要安装松露的帮助,请参阅本文档的上一节.
要初始化新的Truffle项目,请创建一个新文件夹,然后运行
松露初始化
接下来,您需要将Truffle HD Wallet提供程序添加到新初始化的项目中,以便您可以在将交易发送到Infura节点之前对其进行签名。您对以太坊进行的每个状态更改都以事务的形式出现-无论是部署合同,调用合同中的函数还是发送令牌!每个交易都需要由一个帐户签名–因此,我们的应用程序需要具有对交易进行签名的能力,以便可以对以太坊进行状态更改!
每笔交易也要花费以太。该交易成本称为“天然气成本”。因此,为了让我们将已签名的交易发送到Infura节点后由网络处理,我们需要用一些以太币为我们的帐户注资。我们稍后再讨论,但这只是您需要钱包的另一个重要原因 & 钱包提供商!
要将Truffle HD Wallet提供程序添加到终端中新初始化的项目类型中,请执行以下操作:
npm install –save @ truffle / hdwallet-provider
这可能会引发一些警告,但是只要安装了,就可以使用了!
现在我们可以创建一个以太坊账户供我们的应用程序使用!由于我们的钱包提供商是HD(分层确定性)钱包,因此我们可以使用相同的种子短语或助记符确定性地生成帐户.
要创建我们的帐户,我们首先需要启动Ganache。 Ganache是一种Truffle产品,使我们可以轻松创建自己的本地开发网络。要运行ganache,只需键入
加纳奇
如果完成了本指南的第2步,则应该已经安装了Ganache / ganache-cli –如果没有安装,则可以使用npm命令安装它:
npm install -g ganache-cli
或者如果您使用的是纱线
毛线全球添加ganache-cli
接下来,我们需要允许我们的应用与Ganache对话。转到您的项目目录并签出truffle-config.js文件,只需取消注释(或添加)网络下面的以下行:
发展:{主持人: "127.0.0.1", // Localhost(默认:无)端口:8545,// Standard Ethereum端口(默认:无)network_id: "*" //任何网络(默认值:无)},
好的!现在,我们的应用程序可以与运行在127.0.0.1:8545的Ganache开发网络进行通讯了!现在,在新的终端窗口中(但仍在您的项目文件夹中),运行命令
松露控制台
连接到您的Ganache网络。不用担心-我们稍后将连接到公共网络!我们现在只需要连接到Ganache来创建我们的密钥 🙂
注意:如果遇到问题,请确保在Ganache中,您的RPC服务器端口号与松露配置文件匹配。在默认情况下,8545应该可以工作,否则请更改您的配置文件以匹配Ganache.
现在在Truffle控制台中输入以下命令来创建您的钱包:
const HDWalletProvider = require(’@ truffle / hdwallet-provider’);
这将导致响应为“未定义”
对于12字助记符,可以使用助记符生成器,例如 这个 如果你想!
确保您保存了记忆(种子)短语! 我们稍后再用 😃
接下来,在终端中添加以下命令(仍处于松露开发阶段):
const mnemonic =’这里有12个单词’; const wallet = new HDWalletProvider(助记符, "http://本地主机:8545");
现在,在松露控制台中输入以下命令
钱包
如果向上滚动,您应该会看到一个帐户列表,如下所示!
尽管在连接Ganache时生成了该账户,我们仍可以在任何以太坊网络中使用相同的以太坊账户(但是请注意–尽管同一账户可以在任何以太坊网络中使用,但与该账户相关的资产/活动该帐户是特定于网络的–例如,如果我在以太坊主网上进行交易,则该交易将仅在以太坊主网上进行,而不会发生在其他网络上)。现在,我们将停止与Ganache(本地开发者网络)进行互动,并开始使用该帐户与某些公共网络进行互动!!
通常,与公用网络进行交互时,您要做的第一件事就是获取该网络的某些功能。就我们而言,我们将连接到Ropsten公共测试网络,因此我们需要获取一些Ropsten醚(ETH)!不用担心-测试净ETH是免费且丰富的,而且超级容易获得 👍
是时候获取测试ETH
要获取一些Ropsten ETH,请前往 罗普森水龙头. 粘贴您的帐户地址,然后中提琴!您已经收到了一些Ropsten ETH,并且可以开始发送Ropsten网络的交易(即更改状态)!
作为参考,Ropsten测试网络是一个公共的以太坊测试网络,您可以在其中测试与Ethereum主网络极为相似的环境中的代码。 Ropsten测试网(和其他公共以太坊测试网)之间的主要区别在于,在测试网领域,以太坊(ETH)丰富且没有现实价值!当您开始与以太坊主网进行交互时,您用来支付交易费用的以太币(天然气成本)将为ACTUAL美元-因此,我们需要确保事先做正确的事,以免失去辛勤工作赚取的现金/我们宝贵的主网ETH!
Ropsten测试网以及大多数其他公共测试网都有许多区块浏览器,可让您查看链上发生的活动(https://ropsten.etherscan.io/)。要查看您的资金帐户,只需将您的帐户地址粘贴到资源管理器中-您就可以查看与其相关的所有历史记录:
好吧!现在我们有了钱包提供商和由Ropsten ETH资助的帐户,我们可以回到我们的项目,并将其指向连接到Ropsten测试网络的Infura节点.
我们要做的第一件事是创建一个.env文件来容纳我们宝贵的秘密!这些秘密包括我们的Infura API密钥(在创建Infura帐户时生成)以及助记词.
在项目的根级别,只需创建一个新文件“ .env”。另外,您需要通过在终端中输入以下命令来安装dotenv NPM软件包
npm install-保存dotenv
在这个new.env文件中,您需要做两件事:
INFURA_API_KEY =在此处插入您的API密钥(无引号)
MNEMONIC =“镜头鲸鱼扇形泡沫在线座椅暴露股票编号句子获胜者”
INFURA_API_KEY是您先前在infura中创建的项目中的Project ID:
MNEMONIC是您之前用来生成帐户的12词种子短语.
您的文件现在应如下所示:
好了,我们越来越近了!
注意:如果您要将其推送到Github存储库,或以任何方式将该项目公开,请确保将.env文件放入.gitignore中,以免泄露您的秘密。!
现在,我们需要转到truffle-config.js文件。在这里,我们需要添加一些东西来表示我们的提供程序(用于与Infura(我们先前安装的Truffle HDWallet提供程序)进行交互),并将我们的应用程序指向Ropsten Infura节点.
在文件顶部,添加:
要求("Dotenv").config(); const HDWalletProvider = require("@ truffle / hdwallet-provider");
接下来,您要在“网络”下添加以下网络:
罗普森:{provider:()=> 新的HDWalletProvider(process.env.MNEMONIC,`https://ropsten.infura.io/v3/$ {process.env.INFURA_API_KEY}`),network_id:3,// Ropsten的ID气体:5500000,// Ropsten有一个比主网确认低的块限制:2,//在两次部署之间等待的confs数。 (默认值:0)timeoutBlocks:200,//部署超时之前的块数(最小/默认值:50)skipDryRun:true //迁移前跳过空运行? (默认值:对于公共网络为false)}
现在您的truffle-config.js文件应该看起来像这样!
边注:
如果您使用的是Infura端点,则必须使用from参数,因为它们没有钱夹。如果您使用的是Ganache或Geth RPC端点,则这是一个可选参数.
现在我们已经为魔术准备好了!现在是将智能合约部署到ROPSTEN的时候了!
设置智能合约
实体设置
首先,我们要创建一个智能合约进行部署!您可以获取在本指南的上一部分中开发的智能合约,构建自己的智能合约,或仅使用以下(极其简单的)示例合约:
语用固执 >= 0.5.8;合约SimpleStorage函数集(uint256 x)public {storageData = x; }函数get()公共视图返回(uint256) }}
该合同应在项目的“ contracts”文件夹中以“ .sol”(Solidity)文件创建(在这种情况下,我们已经创建了SimpleStorage.sol文件,这是我们的SimpleStorage合同:
迁移设置
接下来,我们需要设置迁移文件!
迁移是JavaScript文件,可帮助您将合同部署到以太坊网络。这些文件负责分阶段部署任务,它们是根据您的部署需求随时间而变化的假设编写的。随着项目的发展,您将创建新的迁移脚本,以进一步推动区块链的发展。以前运行的迁移历史通过特殊的迁移合同记录在链上。您可以找到有关它们的更多信息 这里.
用于部署合同的迁移文件如下所示:
const SimpleStorage = artifacts.require("SimpleStorage.sol"); module.exports = function(deployer){deployer.deploy(SimpleStorage); };
将此文件保存在“ migrations”文件夹中,名称为“ 2_deploy_contracts.js”.
部署您的第一个公共合同
迁移时间
现在您已经准备就绪,可以开始进行魔术了!回到控制台,然后键入
松露迁移-网络罗普斯
繁荣!💣您的代码已部署到公共Ropsten以太坊测试网!!!
刚发生的是:
-
您的Solidity智能合约(位于“ contracts”文件夹中)已编译为字节码-字节码以太坊虚拟机使用的机器可读代码.
-
该字节码以及其他一些数据被捆绑到一个事务中.
-
该交易是由您的帐户签名的.
-
该事务已发送到连接到Ropsten的Infura节点.
-
交易在整个网络中传播,由Ropsten矿工负责,并包含在Ropsten区块中.
-
您的智能合约现已在Ropsten区块链上生效!
您可以使用Etherscan查看合同: https://ropsten.etherscan.io/ –只需粘贴合同的地址(应该在您的终端中)即可查看!
惊人的!我们刚刚将第一个智能合约部署到PUBLIC以太坊网络! 🤯
该过程与部署到以太坊主网完全相同,只不过您需要在truffle-config.js文件中换出以太坊主网的网络(并且,当然,请运行mainnet Truffle migration命令而不是Ropsten一个)。 !我们不会在这里介绍此过程,因为部署到以太坊主网将花费您实际的成本–但是,如果您需要协助,请跳至 ConsenSys Discord 我们很乐意为您提供帮助!
建立一个Web3前端
现在我们已将合同部署到Ropsten,让我们构建一个简单的用户界面来与之交互!
注意:dApp“前端”只是您日常的常规旧前端-因此,我们可以使用我们熟悉的所有旧工具(create-react-app等)来加速前端。 ,然后添加一些内容以允许前端读写以太坊!这意味着,您所有以前的Web开发技能都可以直接转移到以太坊/ Web3!!
启动我们的React项目
好吧,让我们开始吧.
首先,请确保您有一个目录,其中包含我们刚刚为存储合同制作的所有信息。我将文件夹命名为“ storage-back”,其中包含我们刚刚完成的工作,以建立和部署合同.
现在,我们将从开始一个React项目开始,在这个示例中,我们将其称为“存储实验室”
在我们的终端中,运行以下命令以开始我们的项目
npx create-react-app存储实验室
现在我们有了新的项目样板,让我们进入项目目录
cd储存实验室
现在我们进入项目内部,现在将添加Web3软件包,这将使我们的项目能够与以太坊进行交互!有关web3的更多信息 这里
npm安装web3
Web3是我们可以使用的两个主要软件包之一,另一个是ethers.js。对于此示例,我们将使用web3,但如果您想了解有关ethers.js的更多信息,请查看 这里
有关两者的详细说明,请看一下这篇文章 web3与以太币
伟大的!现在,我们几乎准备好让我们的反应项目与合同互动!
首先,让我们从较早的目录开始(对我来说,这是“回存”),其中仅包含我们已经完成的涉及智能合约的工作,现在将其添加到新的react项目中。这将与我们的src处于同一级别,现在我们应该将所有需要的东西放到react REPO中.
接下来,我们需要设置包含ABI信息的文件.
“ ABI?”
很高兴你问!
合约应用程序二进制接口(ABI)是在以太坊生态系统内与合约进行交互的标准方法,既可以从区块链外部进行,也可以用于合约间的交互。当我们在较早的步骤中编译SimpleStorage合同时,它为我们创建了一个JSON文件。检查一下自己,我们的构建/合同中有一个SimpleStorage.json文件
初步查看该文件将揭示很多信息,现在我们只需要关注ABI即可使我们的合同与正在开发的前端保持同步。该JSON包含我们与前端进行合同沟通所需的信息.
我们的ABI是一个包含对象的数组。仔细查看文件,您可以看到每个对象实际上是SimpleStorage合同包含的每个函数.
您可以快速看到
“名称”:“设置”
“名称”:“获取”
两者都带有一个“类型:”函数”编写智能合约时我们声明的两个函数!
尽管Truffle混淆了接下来的几个步骤,但我们将逐步介绍一种更加“手动”的操作方式,以便您了解所有基本知识 🙂
首先,继续并复制您的abi信息-我们将在短时间内需要它.
让我们在src中创建一个名为“ abi”的文件夹.
现在,在我们新创建的abi文件夹中,创建一个名为abi.js的文件
注意:从技术上讲,我们不需要这种分离,而只需将abi.js添加到src中,但是将abi.js文件保留在其中有助于组织.
现在,我们将复制先前从SimpleStorage.JSON文件中获取的abi数组,并将其添加到我们新创建的abi.js文件中。我们将稍稍更改文件,以使我们的项目可以将信息导入到App.js中。别忘了,因为这是一个.js文件,我们需要添加一个导出文件,以便稍后能够将其提取到我们的app.js中。让我们将const命名为与合同相同的名称,但驼峰命名法除外(请参见下面的代码):
这将是我们存储在abi.js文件中的代码
export const simpleStorage = [{常量:false,输入:[{名称: "X", 类型: "uint256", }, ], 姓名: "放", 输出:[],应付帐款:false,stateMutability: "不可付的", 类型: "功能", },{常数:true,输入:[],名称: "得到", 输出:[{名称: "", 类型: "uint256", },],应付款项:false,stateMutability: "看法", 类型: "功能", },];
是时候访问我们的App.js并导入web3和我们新创建的abi.js文件.
在此示例中,我们还将使用钩子(这就是为什么我们也导入{useState}的原因,您可以阅读有关useState的更多信息 这里.
现在,App.js文件的顶部应如下所示:
从导入React,{useState} "反应";从导入{simpleStorage} "./ abi / abi";从中导入Web3 "web3";进口 "./App.css";
现在,我们需要确保我们有能力让任何任意用户具有连接和使用我们的dApp的能力,只要他们拥有钱包提供商即可!
以太坊空间中用于dApp交互的主要钱包是MetaMask,在步骤1中引入.
如果您没有MetaMask,请访问 元掩码.
安装MetaMask后,我们可以通过以下方式在dapp中访问我们的钱包:
const web3 =新的Web3(Web3.givenProvider);
将在以太坊支持的浏览器中设置“ Web3.givenProvider”.
(您可以了解更多有关为什么这样做的必要信息 这里)
因此,现在我们的代码应如下所示:
从导入React,{useState} "反应";从导入{simpleStorage} "./ abi / abi";从中导入Web3 "web3";进口 "./App.css"; const web3 =新的Web3(Web3.givenProvider);
好的!到目前为止,我们已经:
- 整理一个React项目
- 已安装的Web3
- 将包含构建+合约+迁移的文件夹添加到我们的React项目中
- 创建了一个abi.js文件,其中包含我们从SimpleStorage.json中提取的abi数据
- 导入我们需要与合同进行交互的数据
- 创建了一个变量,使我们的dApp可以与用户的钱包通信
同样,尽管Truffle使接下来的步骤变得不必要(我们将在后面逐步简化),但出于教育目的,我们将为dApp添加更多的手动复杂性.
现在,我们要做的是创建两个新变量:一个用于存储我们在Ropsten上部署的合同的地址,另一个用于将合同与我们的ABI相匹配,以便我们的应用知道如何与之对话!
要找到合同地址,请导航到我们之前的JSON文件(其中包含ABI(SimpleStorage.json)),然后滚动到底部。地址在此处的“地址”字段中:
"编译器":{ "姓名": "溶胶", "版本": "0.5.8 + commit.23d335f2.Emscripten.clang" }, "网路":{ "3":{ "大事记":{}, "链接":{}, "地址": "0x24164F46A62a73de326E55fe46D1239d136851d8", "transactionHash": "0x1f02006b451b9e85f70acdff15a01c6520e4beddfd93a20e88a9b702a607a7b0" }}, "schemaVersion": "3.0.16", "UpdatedAt": "2020-06-30T20:45:38.686Z", "开发文档":{ "方法":{}}, "用户文档":{ "方法":{}}}
或者,您可以前往 https://ropsten.etherscan.io/ 并查找部署合同的帐户的地址!在Etherscan中,单击“合同创建”将显示合同地址本身.
现在,我们将获取您合同地址的副本,并创建一个新变量来存储它.
否则,我们将无法与合同进行通信,而dApp将无法按预期工作.
您将其添加到我们的const web3 = new Web3(Web3.givenProvider);下。
const contractAddress = "您的合同地址在这里";
然后,我们将创建另一个名为“ storageContract”的新变量,其中将包含我们的合同地址(因此我们的应用程序知道合同在哪里)和ABI(因此我们的应用程序知道如何与合同进行交互).
const storageContract =新的web3.eth.Contract(simpleStorage,contractAddress);
我们的App.js现在应该像这样
从导入React,{useState} "反应";从导入{simpleStorage} "./ abi / abi";从中导入Web3 "web3";进口 "./App.css"; const web3 =新的Web3(Web3.givenProvider); const contractAddress = "您的合同地址在这里"; const storageContract =新的web3.eth.Contract(simpleStorage,contractAddress);
现在,我们需要挂钩来保存将与我们的合约和前端交互的变量。我们将通过在我们的应用程序函数中声明以下内容来做到这一点:
从导入React,{useState} "反应";从导入{simpleStorage} "./ abi / abi";从中导入Web3 "web3";进口 "./App.css"; const web3 =新的Web3(Web3.givenProvider); const contractAddress = "您的合同地址"; const storageContract =新的web3.eth.Contract(simpleStorage,contractAddress);函数App(){const [number,setUint] = useState(0); const [getNumber,setGet] = useState("0");
我们对useState(0)的首次使用将保存用户声明的uint256.
(数字,setUint,getNumber,setGet的命名约定有望帮助显示正在发生的事情)
useState(“ 0”)值将充当占位符,直到我们对已签名的操作进行确认(我们声明的uint256)为止
setUint我们将很快在返回中调用(稍后会详细介绍)
我们逻辑的时间
接下来,我们将添加numberSet和NumberGet逻辑(我们在函数App中添加numberSet)
const numberSet =异步(t)=> {t.preventDefault();常量帐户=等待window.ethereum.enable(); const account = accounts [0]; const gas =等待storageContract.methods.set(number).estimateGas(); const post = await storageContract.methods.set(number).send({from:account,gas,}); }; const numberGet =异步(t)=> {t.preventDefault(); const post =等待storageContract.methods.get()。call(); setGet(post); };
我们设置了一个preventDefault(在preventDefault上找到了详细信息 这里)
我们还对合同的get使用了异步调用(有关异步的详细信息,请参见 这里)
我们的钩子setGet()存储了我们最初查看的默认值(“ 0”)
常量帐户=等待window.ethereum.enable();
确保我们通过MetaMask呼叫我们的连接地址.
const account = accounts [0];
拉入连接帐户
您可能想知道发生了什么事
const gas =等待storageContract.methods.set(number).estimateGas();
我们的应用程序需要访问用户资金以支付汽油费的权限,任何功能都需要以太坊,无论它在测试网还是主网上。在这里,我们与MetaMask的连接可以方便地签署此用法以设置我们的uint256并为之付款(使用测试ETH).
因此,对于需要气体的任何函数,您必须计算使用的潜在气体.
我们合同的“设置”功能需要加油
“获取”不.
(这是因为“获取”正在查看已用“设置”声明的内容)
const post将采用uint256中传递的内容,在Ropsten网络上从您的MetaMask钱包中确认交易(支付加油费).
接下来,我们通过methods.set()传递函数参数,并使用声明的地址(用户地址)处理Set函数。.
我们通过将函数参数传递到智能合约的methods.set(),并将估计的天然气和用户帐户地址传递给send()来创建智能合约交易。.
const post = await storageContract.methods.set(number).send({from:account,gas,});
这应该是我们涵盖numberSet所需的所有逻辑.
现在我们需要我们的号码
const numberGet =异步(t)=> {t.preventDefault(); const post =等待storageContract.methods.get()。call(); setGet(post); };
我们的const帖子检索我们的设置号,setGet传入我们声明的新值
因此,我们的“ 0”将onClick引用我们的数字获取并呈现我们的unint256!
所以现在您的app.js应该看起来像这样
从导入React,{useState} "反应";从导入{simpleStorage} "./ abi / abi";从中导入Web3 "web3";进口 "./App.css"; const web3 =新的Web3(Web3.givenProvider); const contractAddress = "您的合同地址"; const storageContract =新的web3.eth.Contract(simpleStorage,contractAddress);函数App(){const [number,setUint] = useState(0); const [getNumber,setGet] = useState("0"); const numberSet =异步(t)=> {t.preventDefault();常量帐户=等待window.ethereum.enable(); const account = accounts [0]; const gas =等待storageContract.methods.set(number).estimateGas(); const post = await storageContract.methods.set(number).send({from:account,gas,}); }; const numberGet =异步(t)=> {t.preventDefault(); const post =等待storageContract.methods.get()。call(); setGet(post); };
让我们创建一个非常基本的渲染回报,以便我们测试是否可以
- 设置一个unint256值,
- 拉起我们的Metamask钱包并确认交易
- 支付汽油费
- 然后在交易完成后获取我们存储的值(unint256).
我们的回报看起来像这样:
return(设置您的uint256:setUint(t.target.value)} /> 确认
获取您的uint256 {getNumber}); }导出默认应用;
一些快速的CSS
现在转到App.css文件,删除样板代码,然后添加它
.main {text-align:center;显示:flex;证明内容:中心;背景颜色:#f2f1f5;高度:100vh; } .card {min-height:50vh;宽度:50vw;显示:flex; flex-direction:列; align-items:居中;证明内容:中心; } .form {高度:20vh;宽度:20vw;显示:flex;证明内容:空间均匀; flex-direction:列; } .button {width:20vw;高度:5vh; }
现在我们准备测试!
在您的终端运行中
纱线起头
在我们的localhost:3000中,我们应该看起来像这样
现在,我们应该能够在输入字段中输入unint256值!
在dApp中确认编号后,我们将通过MetaMask进行签名(确保您的钱包已设置为Ropsten网络)
我们做到了! 🤗
现在,我们将智能合约连接到前端,并且能够操纵Set功能(前提是我们有测试ETH来支付交易的汽油费)。然后,我们可以调用Get函数并检索存储的uint265值.
挺酷的吧!?!
额外的造型
现在是时候展示将更流行的Web2技术实施到我们的项目中有多么容易.
我们将使用MUI添加基本样式,如果您已经使用React进行开发,那么您可能会熟悉material-ui。 (找到详细信息 这里)Material-UI或MUI是一个非常流行的React框架,如果您遵循命名约定,它可以使您快速启动具有很多样式的项目。如果您只想使用基础并从中进行自定义,那么操作也非常容易.
*这将是一个非常简短的示例,说明如何通过少量添加将MUI添加到项目中,以演示您可以多么迅速地将我们的项目与Web2技术结合起来.
添加MUI
我们将从运行命令开始(仍然在终端的项目目录中(如果您的应用仍在运行,则需要将其关闭(ctrl + c)或打开一个新标签)):
要使用npm进行安装:
npm install @ material-ui / core
或与纱线:
纱线添加@ material-ui / core
现在已经注入了MUI,我们将从更改样式开始。在app.js文件的顶部,我们将导入一些新内容:
从导入{simpleStorage} "./ abi / abi";从导入按钮 "@ material-ui / core / Button";从导入TextField "@ material-ui / core / TextField";从导入{makeStyles} "@ material-ui / core / styles";
{makeStyles}的导入允许我们操纵样式(在这种情况下)以及按钮和文本字段的样式,以及导入默认的MUI样式.
现在,我们将创建一个变量(在函数上方),该变量会引入MUI的样板样式
const useStyles = makeStyles((theme)=> ({ 根: { "& > *":{margin:theme.spacing(1),},},})));
现在,在我们的App函数中,我们还将添加一个名为“类”的变量,以获取上面刚刚声明的已定义样式.
函数App(){const classes = useStyles(); const [number,setUint] = useState(0); const [getNumber,setGet] = useState("0");
现在,我们将在退货中进行调整,以将我们的某些字段替换为我们刚刚导入的字段.
return(setUint(t.target.value)} variant ="概述" /> 确认
获取您的uint256 {getNumber}); }导出默认应用;
您的代码现在应如下所示
从导入React,{useState} "反应";从导入{simpleStorage} "./ abi / abi";从中导入Web3 "web3";进口 "./App.css";从导入{makeStyles} "@ material-ui / core / styles";从导入按钮 "@ material-ui / core / Button";从导入TextField "@ material-ui / core / TextField"; const useStyles = makeStyles((theme)=> ({ 根: { "& > *":{margin:theme.spacing(1),},},}))); const web3 =新的Web3(Web3.givenProvider); const contractAddress = "您的合同地址在这里"; const storageContract =新的web3.eth.Contract(simpleStorage,contractAddress);函数App(){const classes = useStyles(); const [number,setUint] = useState(0); const [getNumber,setGet] = useState("0"); const numberSet =异步(t)=> {t.preventDefault();常量帐户=等待window.ethereum.enable(); const account = accounts [0]; const gas =等待storageContract.methods.set(number).estimateGas(); const post = await storageContract.methods.set(number).send({from:account,gas,}); }; const numberGet =异步(t)=> {t.preventDefault(); const post =等待storageContract.methods.get()。call(); setGet(post); }; return(setUint(t.target.value)} variant ="概述" /> 确认
获取您的uint256 {getNumber}); }导出默认应用;
现在,如果我们看一下我们的react项目,它应该看起来像这样!
做得好!
我们仍然拥有以前的所有功能,现在已经注入了易于使用的框架,以便根据需要进一步自定义我们的项目。看看MUI 文件资料 尝试自己的添加/修改!
奖金回合
很高兴在dApp中向用户显示连接地址,不是吗?
好吧,让我们做一个非常快速和基本的组件来做到这一点!
我们将首先制作一个单独的组件,然后将其导入回我们的App.js文件。最好分开逻辑,不仅使App.js易于浏览,而且遵循组件的做法,理想情况下只能做一件事。如果最终增长,则应分解为较小的子组件.
组件扩展
我们将在与src相同的级别上创建一个名为components的新文件夹,并在该文件夹中创建Nav.js文件。我们的项目脚手架现在应该看起来像这样
我们还将在components文件夹中创建一个Nav.css文件,以导入我们专门应用于Nav组件的任何样式.
让我们打开Nav.js并导入React,Web3和empty.css文件
从中导入React "反应";从中导入Web3 "web3";进口 "./Nav.css"
现在,我们将创建一个名为Nav的类,并在其中添加一些内容以显示我们的连接地址。我们将从设置状态开始以读取帐户
Nav类扩展了React.Component {state = {account: "" };
仍然在我们的类中,我们将通过添加异步loadAccount逻辑来加载要读取的帐户
async loadAccount(){const web3 = new Web3(Web3.givenProvider || "http://本地主机:8080"); const network =等待web3.eth.net.getNetworkType(); const account =等待web3.eth.getAccounts(); this.setState({account:accounts [0]}); }
接下来,我们将创建一个componentDidMount(将在组件安装后立即调用)。在本例中,是拉入已加载的帐户。阅读更多 这里
componentDidMount(){this.loadAccount(); }
边注:
这可以通过不同的方式完成,我们可以创建一个函数并使用与componentDidMount相对的钩子来代替类,但是出于本示例的考虑,我们将坚持使用此方法.
然后,我们将在返回值上方创建一个渲染器,渲染器是使用类方法编写React组件时所需的一种方法。在返回中,我们沿着p标签向div添加一个地址类(以供日后使用基本样式),以显示我们使用{this.state.account}获取的连接地址。
render(){return(您的连接地址:{this.state.account});导出默认导航;
现在,我们的Nav.js文件应如下所示
从中导入React "反应";从中导入Web3 "web3";进口 "./Nav.css" Nav类扩展了React.Component {state = {account: "" }; async loadAccount(){const web3 = new Web3(Web3.givenProvider || "http://本地主机:8080"); const network =等待web3.eth.net.getNetworkType(); const account =等待web3.eth.getAccounts(); this.setState({account:accounts [0]}); } componentDidMount(){this.loadAccount(); } render(){return(您的连接地址:{this.state.account});导出默认导航;
让我们转到Nav.css文件并添加非常基本的样式
.地址{display:flex;证明内容:中心; }
从技术上讲,您可以将其添加到App.css文件中,但要记住,尽管很快就会变得凌乱。组件应该是可重复使用的,并且通过划分工作区域来尽可能地避免摩擦,这可以使您免于头痛.
现在,让我们回到App.js并导入我们新创建的组件,并确保将其添加到返回中以显示它!
我们完成的App.js文件应如下所示
从导入React,{useState} "反应";从导入{simpleStorage} "./ abi / abi";从中导入Web3 "web3"; 从导入Nav "./components/Nav.js"; 进口 "./App.css";从导入{makeStyles} "@ material-ui / core / styles";从导入按钮 "@ material-ui / core / Button";从导入TextField "@ material-ui / core / TextField"; const useStyles = makeStyles((theme)=> ({ 根: { "& > *":{margin:theme.spacing(1),},},}))); const web3 =新的Web3(Web3.givenProvider); const contractAddress = "您的地址在这里"; const storageContract =新的web3.eth.Contract(simpleStorage,contractAddress);函数App(){const classes = useStyles(); const [number,setUint] = useState(0); const [getNumber,setGet] = useState("0"); const numberSet =异步(t)=> {t.preventDefault();常量帐户=等待window.ethereum.enable(); const account = accounts [0]; const gas =等待storageContract.methods.set(number).estimateGas(); const post = await storageContract.methods.set(number).send({from:account,gas,}); }; const numberGet =异步(t)=> {t.preventDefault(); const post =等待storageContract.methods.get()。call(); setGet(post); };返回 ( setUint(t.target.value)} variant ="概述" /> 确认
获取您的uint256 {getNumber}); }导出默认应用;
现在,我们应该在顶部看到连接的地址,并且仍然保留所有功能!
🎉我们做到了! 🎉
现在,我们有了一个从头开始构建的dApp。我们将智能合约引入了一个React项目中,编写了逻辑以确保我们具有用户功能,创建了一个组件来呈现连接的地址,甚至为我们的项目添加了流行的样式框架.
做得好!这仅仅是Web3开发冒险的开始,并且已经有一些东西可以证明您不仅已经创建而且还可以全力以赴。在Discord中与我们联系,并与我们分享您的项目(尤其是您进行了任何修改或添加)!
- 开发人员入职:步骤1

开发人员入职:步骤1
开发人员入职:步骤2
开发人员入职:步骤2
10分钟以太坊定向