使用React创建一个web3的前端
在之前的教程中,我们介绍了如何从头开始创建和部署一个NFT藏品智能合约。我们还探讨了如何在etherscan上验证我们的合约,并使自己和用户能够直接从合约的etherscan页面调用函数。
然而,大多数严肃的项目倾向于部署他们自己的网站,并允许用户直接从网站上铸币。
这正是我们将在本教程中涉及的内容。更具体地说,本教程将告诉你如何:
在本教程结束时,你将拥有一个用React构建的功能齐全的web3前端。你也将获得构建任何通用的web3前端所需的基础知识(除了NFT minter)。
本教程假设你已经开发并部署了智能合约到Rinkeby测试网络。如果你还没有,我们强烈建议你通过本教程。为了继续学习本教程,你将需要以下东西。
我们还假设你有一些使用React和Javascript的经验。如果没有,强烈建议你先看一下React网站的官方教程。
让我们从使用create-react-app
创建一个React项目开始。打开终端,运行以下命令:
npx create-react-app nft-collectible-frontend
安装过程将需要2-10分钟。一旦完成后,通过运行以下命令检查一切是否正常。
cd nft-collectible-frontend
npm start
如果一切顺利,你应该看到浏览器在localhost://3000打开了一个新的标签,屏幕如下。很标准的 React 内容:
现在让我们做一些清理工作。
进入public/index.html
,修改网站的标题和元描述(这一步是可选的)。
接下来,进入src文件夹,删除App.test.js
、logo.svg
和setupTests.js
文件。在本教程中,我们将不需要这些文件。
进入App.js
文件,用以下模板替换其内容。
import './App.css';
function App() {
return (
<h1>Hello World</h1>
);
}
export default App;
同时删除App.css
的所有内容。但是,不要删除这个文件。在后面的章节中,我们将提供一些基本的风格设计,对这个演示项目来说应该是足够好了。
如果你回到localhost,你应该看到一个屏幕,上面写着Hello World。我们现在有了一个基本的react项目,可以开始了。
为了使我们的React前端能够与智能合约连接和通信,它需要合约的ABI和地址。
ABI(应用二进制接口)是一个JSON文件,在合约编译过程中自动生成。我们部署到区块链上是以字节码的形式存储智能合约。为了在其上调用函数,传递正确的参数,并使用高级语言解析返回值,我们需要向前端指定有关函数和合约的细节(如名称、参数、类型等)。这正是ABI文件的作用。为了了解更多关于ABI的信息,建议你阅读:如何理解以太坊ABI。
要找到你的ABI文件,请进入你的hardhat项目并导航到artifacts/contracts/NFTCollectible.sol/NFTCollectible.json
。
我们现在需要复制JSON文件到React项目。在src
文件夹中创建一个名为contracts
的新文件夹并粘贴NFTCollectible.json
文件。
你应该已经有了部署的智能合约的地址。(如果你没有,只需再次将其部署到Rinkeby,并获得最新的地址和ABI文件)。
我们在上一个教程中的合约地址是0x355638a4eCcb777794257f22f50c289d4189F245。我们在本教程中也将使用这个合约。
现在让我们导入合约ABI并在App.js
文件中定义合约地址。
网站将是非常简单的。它将只有一个标题和一个连接钱包按钮。一旦钱包被连接,连接钱包按钮将被一个Mint NFT按钮取代。
我们不打算费力地创建单独的组件文件。相反,我们将在App.js
中编写所有的HTML和逻辑,在App.css
中编写所有的CSS。
将以下Github gist的内容复制到App.js
文件中。
import { useEffect } from 'react';
import './App.css';
import contract from './contracts/NFTCollectible.json';
const contractAddress = "0x355638a4eCcb777794257f22f50c289d4189F245";
const abi = contract.abi;
function App() {
const checkWalletIsConnected = () => { }
const connectWalletHandler = () => { }
const mintNftHandler = () => { }
const connectWalletButton = () => {
return (
<button onClick={connectWalletHandler} className='cta-button connect-wallet-button'>
Connect Wallet
</button>
)
}
const mintNftButton = () => {
return (
<button onClick={mintNftHandler} className='cta-button mint-nft-button'>
Mint NFT
</button>
)
}
useEffect(() => {
checkWalletIsConnected();
}, [])
return (
<div className='main-app'>
<h1>Scrappy Squirrels Tutorial</h1>
<div>
{connectWalletButton()}
</div>
</div>
)
}
export default App;
(记得在第5行设置正确的合约地址)
注意,这里已经定义了几个函数,这些函数目前没有什么作用。我们将在下面解释它们的用途,并在其中加入逻辑。
下面是相应的CSS,将以下内容复制到你的App.css
文件中。
.main-app {
text-align: center;
margin: 100px;
}
.cta-button {
padding: 15px;
border: none;
border-radius: 12px;
min-width: 250px;
color: white;
font-size: 18px;
cursor: pointer;
}
.connect-wallet-button {
background: rgb(32, 129, 226);
}
.mint-nft-button {
background: orange;
}
网站现在应该看起来像这样:
通过添加更多的样式和静态元素(图片、页眉、页脚、社交媒体链接等),可以自由地定制网站的外观。
我们已经把这个项目的大部分基础模块放在一起。现在处于一个很好的位置来解决本教程的第一个主要目标之一:允许用户将他们的钱包连接到我们的网站。
为了让用户能够从我们的合约中调用功能,他们需要能够将他们的钱包连接到我们的网站。钱包将使用户能够支付Gas和销售价格,以便从我们的集合中铸造一个NFT。
在本教程中,我们将专门使用Metamask钱包和它的一套API。有一些现成的解决方案,如Moralis和web3modal,允许你用很少的代码添加对多个钱包的支持。但在这个项目中,我们将专注于从头开始实现连接钱包功能。在以后的教程中介绍Moralis等解决方案。
我们假设你已经在浏览器中安装了Metamask钱包插件。如果你有,Metamask会将一个ethereum
对象注入你的浏览器的全局window
对象中。我们将访问window.ethereum
来执行我们的大部分功能。
用户无法在我们的网站上铸造NFT,除非他们有一个Metamask钱包。让我们在App
组件中补充checkWalletIsConnected
函数,检查Metamask钱包是否存在。
注意,我们还定义了useEffect
钩子,当App组件加载时检查Metamask的存在。
在你的应用程序的localhost页面上打开控制台。如果你已经安装了Metamask,你应该看到一条消息,说Wallet exists! We’re ready to go!(存在钱包!我们准备好了!)。
仅仅因为我们安装了Metamask插件,并不意味着Metamask会自动连接到我们访问的每个网站。我们需要提示Metamask要求用户这样做。
这就是连接钱包功能的用武之地。它相当于web3的一个登录按钮。它允许用户通过网站连接并发送调用合约功能请求。
Metamask通过 "window.ethereum.request"方法可以让连接变得简单。
让我们首先在App()
中定义一个变量,用useState钩子来跟踪用户的钱包地址。(别忘了从React导入useState
)
const [currentAccount, setCurrentAccount] = useState(null);
现在,让我们定义connectWalletHandler
函数。
让我们简单地看一下这个函数的作用:
目前,如果你在网站上打开Metamask插件,它会显示 Not conntected(没有连接)。
现在关键时刻到了,点击网站上的*Connect Wallet(连接钱包)按钮。Metamask将提示你与网站连接。一旦你同意,插件界面将看起来像这样:
恭喜你!已经成功地将钱包连接到网站。
一旦钱包被连接,我们最好用Mint NFT按钮取代Connect Wallet按钮。在App的返回值中,让我们用一个条件性的渲染来替换Connect Wallet按钮的渲染。
{currentAccount ? mintNftButton() : connectWalletButton()}
网站现在应该看起来像这样:
让我们刷新页面并检查插件。你会看到Metamask显示连接了网站(connected),但网站仍然显示一个连接钱包的按钮。
如果你熟悉React,应该很清楚为什么会发生这种情况。毕竟,我们只在 "connectWallet "函数中设置 "currentAccount "状态。
理想的情况是,网站应该在每次加载App
组件时(即每次刷新时)检查钱包是否被连接。
让我们插件checkWalletIsConnected
函数,在网站加载时立即检查账户,如果钱包已经连接,则设置currentAccount。
(注意,这个函数标记为async )。简单解释这个函数的作用:
如果你现在刷新页面,你会看到网站确实显示了Mint NFT按钮。
现在让我们来实现网站的核心功能。当用户点击Mint NFT按钮时,我们希望发生以下情况。
要做到这一点,我们将需要ethers
库来进行合约交互。在你的终端,运行以下命令:
npm install ethers
让我们在App.js
中导入这个库:
import { ethers } from 'ethers';
最后,让我们补充mintNftHandler
函数:
(别忘了把这个函数标记为 "async")
像往常一样,解释一下这个函数的作用:
ethereum
对象。ethereum
对象存在,它将Metamask设置为RPC提供者。这意味着,将使用Metamask钱包向矿工发出请求。在网站上,打开浏览器的控制台,这样你就能实时查看挖矿状态。
现在,点击Mint NFT按钮。Metamask将提示你支付0.01 ETH + gas。该交易将需要大约15-20秒的时间来处理。一旦完成,交易可以通过Metamask的弹出窗口和控制台的输出来确认。
你现在也可以在Opensea上查看NFT了。导航到你在testnets.opensea.io上的账户,你应该可以看到你的最新NFT。
祝贺你!你现在有了一个功能齐全的web3前端,用户可以通过它来铸造NFT。
然而,正如你可能已经注意到的,网站的用户体验还有很多需要改进的地方。以下是你应该考虑做的一些改进。
我们的网站假设用户在与网站交互时,已经连接到Rinkeby网络,这可能并不总是如此。
你能不能实现在用户没有连接到Rinkeby时提醒他(就像OpenSea那样)?另外,确保用户在连接到错误的网络时不能看到Mint NFT按钮。
目前,我们的网站将交易状态打印到控制台。在一个真实的项目中,你不能指望你的用户在与网站交互的同时打开他们的控制台。
你能实现跟踪交易状态并实时反馈给用户的状态吗?当交易正在处理时,它应该显示一个加载提示(loading),如果交易失败则通知用户,如果交易成功则显示交易哈希(或Opensea链接)。
如果你的Metamask钱包中没有任何ETH,点击Mint NFT将完全不会提示Metamask。事实上,用户将不会收到任何反馈。
你能确保在用户资金不足的情况下也能提示Metamask吗?最好是由Metamask来通知用户需要多少ETH,以及他/她还差多少。
这里有一些其他的改进,你可以考虑:
我们的NFT沙盒项目,Rinkeby Squirrels,实现了这里提到的大部分用户体验升级,你可以尝试铸造一个。
最终代码库:https://github.com/rounakbanik/nft-collectible-frontend
Scrappy Squirrels是一个由10,000多个随机生成的NFT组成的集合。Scrappy Squirrels是为那些对NFT生态系统完全陌生的买家、创作者和开发者准备的。
这个社区是围绕着学习NFT革命,探索其目前的使用案例,发现新的应用,并找到成员一起合作进行令人兴奋的项目而建立的。
在这里加入我们的社区:https://discord.gg/8UqJXTX7Kd
本翻译由 Duet Protocol 赞助支持。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!