# Dapp 前端开发小记

Original: <https://lifelonglearn.ing/dapp-frontend>

Author: [奶爸](https://lifelonglearn.ing/users/3b04650c-8231-499b-8ef0-381fc4d7efb0)

Published: 2020-08-25T15:38:15Z

Updated: 2026-09-27T06:57:43Z

Version: 1

前端开发可谓是占据了 Dapp 开发的半壁江山，除了上篇文章讲到的 Solidity 智能合约开发，作为我们 Dapp 的逻辑和储存，前端就作为一个与 Dapp 交互的 User Interface 啦。

那么首先我也是半个专业的前端开发工程师，除去我们传统的前端开发的一些基本规范：

* i18n
* 路由守卫
* 权限
* state 管理
* model
* api 封装
* 持久化

之外呢，奶爸通过深度参与我司几个 Dapp 项目的前端开发后，也有一些自己的心得体会。

## Web3.js

我们做 Dapp 主要关注两个方面，

1. 钱包签名验签
2. sendTransaction 和 处理 transaction 的执行结果

以太坊生态不是十分健全，钱包应用做的良莠不齐，No.1 当属 MetaMask，这里又一个非常有用的 GitHub pages：[https://danfinlay.github.io/js-eth-personal-sign-examples/](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly9kYW5maW5sYXkuZ2l0aHViLmlvL2pzLWV0aC1wZXJzb25hbC1zaWduLWV4YW1wbGVzLw==) ，可以在钱包中检测签名兼容性十分有用。

因为博文时效性的问题，MathWallet、TrustWallet、imToken、MetaMask Mobile 这些钱包都会有些许不同，不展开描述每个钱包的异同。

### 连接钱包

![image.png](https://lifelonglearn.ing/upload/1600227455037713873.png)

现在是非常注重隐私的时代，在用户首次进入您的 Dapp 应用时，可能只是想浏览基本信息，尚未决定要参与进来。这时候不能进入页面立即请求连接钱包，获取当前钱包的一些信息。

如果我们要连接钱包的话有这么几种：

1. MetaMask 为主的 PC 浏览器端离线钱包
2. imToken、TrustWallet 为主的移动端离线钱包
3. Portis 等的线上 iframe 嵌入钱包

如果要维护这么多钱包的接入工作，对于一个小团队来说十分繁琐，所以推荐使用 [Web3Modal/web3modal](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly9naXRodWIuY29tL1dlYjNNb2RhbC93ZWIzbW9kYWw=) 的统一解决方案，十分便捷（如果贵司有对项目进行改进，一定要同步到上游做贡献哦）

### 免密码登录

Dapp 的前端开发里面，使用 Web3 与钱包节点交互是重中之重，那么参考 CryptoKitties 等一些知名的 Dapp 项目的交互来说：

1. 首先进入 Dapp 后，会展示一些统计、说明类的信息（此时并不会要求钱包授权获取用户当前选择的钱包地址）
2. 放置一个 `Connect` 按钮，点击后再进行 `window.ethereum.enable()` 之类的初始化 `web3js` 的操作。
3. 然后可以进行钱包签名免密码登录。

说到 ​**钱包签名登录**​，坑就多了。在签名验签这里，负责签名的前端是很好处理的，直接将参数传入签名方法即可，但是负责验签的后端就比较考验能力了（先留个坑，后面奶爸可能把部署、测试工具，及签名使用的 lib 抽取出来在 GitHub 上面发布）。

### Transaction 处理

首先是由 abi 初始化合约实例，然后调用一些读、写的方法。主要还是多看 web3 的文档。

对于 **区块重组** 这个事情对前端来说影响不大，只要你是完全去中心化的，数据都会最终一致，前端在重组时那短暂的展示数据不一致不会造成影响。

在 transaction 执行失败时，你或许想要获取错误信息然后展示出来，看博文最后一个部分里面有个取 revert 信息的库。

Transaction 即便是链上确认后，节点的读接口也可能有几秒钟的数据延时，这里就需要在 操作前对某些业务上的数据进行快照，在链上确认后先验证数据刷新，再对页面上的数据进行更新。

### Transaction 重入

因为 Transaction 从广播到链上确认需要时间，如果这期间用户关闭了页面你当如何？这里奶爸从项目中总结了一些经验：

* 把所有与链交互相关的按钮的状态（loading）集合到 state 里面
* 将 Transaction 与用户绑定，持久化到数据库中（包含 ​**提交时间**​、​**操作名称**​、​**TransactionHash**​、**extraData**  ​*（可能用于 Transaction 确认后的节点数据刷新验证）*​）
* 前端在网页刷新后，拉取 Transaction 列表，对有待获取结果的 Transaction 进行二次确认，将操作相关的按钮置为 loading 状态
* 取到 Transaction 最终结果后，对按钮状态进行更新

对用户 Transaction 历史的保存有两种方式，一种是保存在服务端，另一种是储存在用户的 localStorage 里面，储存在 localStorage 里面时要注意实现**基于钱包地址的操作**隔离逻辑。

## 大数运算与展示

Dapp 开发中，不论是展示还是运算，都少不了跟 `BigNumber` 打交道，`ERC20` 的代笔包括 `ether` 都是18位的。那么在我司几个项目中，最佳实践是，在前端逻辑运算、展示时，使用 [BigNumber.js](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly9naXRodWIuY29tL01pa2VNY2wvYmlnbnVtYmVyLmpzLw==) 这个库，然后在与链交互，将要发起 Transaction 时呢 使用 web3 使用的 **bn.js** 库，由  BigNumber 对象格式化为字符串，然后再转化为 BN 对象。

因为 BN 库不能进行小数运算和展示，所以结合 BigNumber.js 与 BN.js 的话既保证了展示的多样性，又不会产生精度问题，还能顺利与链交互。

## 一些有用的东西

* [https://www.trufflesuite.com/drizzle](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly93d3cudHJ1ZmZsZXN1aXRlLmNvbS9kcml6emxl)  Dapp 前端框架 · React
* [https://github.com/ethjs](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly9naXRodWIuY29tL2V0aGpz) 轻量级 Web3
* [https://github.com/justinjmoses/eth-reveal](https://lifelonglearn.ing/r/go?url=aHR0cHM6Ly9naXRodWIuY29tL2p1c3Rpbmptb3Nlcy9ldGgtcmV2ZWFs) 取transaction的revert信息（目前只能在主网使用）
