老胡茶室
老胡茶室
Beta

面向 dapp 开发者的前端工程实践

胡键

老实讲,促使本文写作的动机有三点:

  • blog 和公众号好久没更新了,间隔这么长时间,总得写点什么来宣告自己的存在。
  • 这段时间项目的开发自我总结,包括自己犯的错误。
  • 跟一些团队和开发者打过交道后,发现一些本应是常识的内容,似乎并没有想象中那么普及。

由于我目前的空闲时间有限,也就以条目的形式随意写一点东西吧。

语言和框架

按理说,这部分内容完全是多余,但其实不是。

在我的印象里,国内前端开发应该基本上被三大框架瓜分得差不多,但怪事恰恰被我遇上:某 dapp 项目在我介入的时候,居然发现了久违的 jQuery ,而且完全没有任何前端工程而言:一个 html 文件 + 一堆 js 文件。

这里首先声明,我已不是当年的我,现在早已对各类工具无感,只要能办成事,挣到钱,其实无所谓。因此 jQuery 并不在我歧视之列,并且自己写的 vscode-page 其实也用了历史悠久的模板库:handlebars.js

上面那个 jQuery 工程的问题在于:代码杂乱无章,到处可见复制黏贴的痕迹,即便放在 10+ 年前,该工程也不能说得上好。因此,除非你对自己的代码掌控力十分自信且得到大家认可,还是俗套一点:选择一个前端框架,这样起码会有一个不错的起点。

至于语言,有条件的话还是采用 TypeScript,单单类型检查就能让你避免很多潜在问题。此外,也别忘了一些配套设施:自动化的代码风格检查、vscode 插件等等。这里摘抄一下以前文章的内容

工程相关

因为本质上 vscode 插件开发是前端工程的范畴,并且我们团队主要采用 TypeScript ,因此用到的插件和规范如下:

代码风格:

风格可商量余地:

  • “strict”: true + “no-any”: false,允许

相关插件:

  • ESLint
  • Bracket Pair Colorizer 2
  • Prettier
  • markdownlint
  • indent-rainbow
  • Path Intellisense
  • Peacock

最后,补充一个极大提高效率的 vscode 插件:tabnine,谁用谁知道。

数据精度

数据精度问题让我丢了大人,至今觉得过意不去。但是,这类问题在做后端时候从未出现,现在想来,有几个原因:

  • 数据精度问题并非我的知识盲区,在数据库设计(比如数据库内精度到“分”)和后端类型(Java 选择 BigDecimal)选择上都会注意这一点。
  • 对于 JavaScript / TypeScript 的这方面知识储备不如后端,且对问题重视不足,混淆了数据范围和数据精度的含义。
  • 当时处于救火赶工状态,选择了捷径。

结果待到上线日,出现了状况。

由于 dapp 项目对于数字很敏感,稍有差池就会出现问题,因此作为 dapp 开发者需要对数据精度保持高度重视。这里列出几个我觉得比较重要的地方:

  • 一般情况下,避免四舍五入。尤其是跟价格相关的部分,四舍五入肯定会有问题。如果出于显示目的不能显示全部,那么只能截取。
  • 选择字符串传入传出,避免直接使用 number 来转换。
  • 使用成熟类库完成计算,个人推荐:bignumber.js。相比起其他同类库,使用简单,并且非常类似 java 的 bigdeimal。同时,作者贡献了 big.js 和 decimal.js ,分别用于不同情况,各位可自行搜索相关资料。

合约交互

本来,我觉得像 ethers.js 这么好用的库没有理由不被很多人知道。结果恰恰相反,实在让人大跌眼镜!

除非你有非常特异的需求,并且不仅限于跟合约交互,比如还想去试试发消息和存储,我建议直接使用 ethers.js,不要用 web3.js。原因很简单:

  • ethers.js 更简单好用,没那么多废话。
  • human readable abi,根本不需要你再去生成什么 abi 文件,用哪些方法单单复制出来函数签名就好了,省掉了不必要的构建步骤。
  • 合约方法更好用,完全的对象化。
  • 钱包支持。

假如你还想增强合约方法(比如你想一次性完成:先预估 gas,然后设置 gaslimit ,然后执行合约方法)也很方便。

状态管理

页面状态很关键,对于 dapp 更是如此。因为这里至少有两个地方需要注意:

  • 程序运行时自身导致的状态变化
  • 因为链上状态变化而导致的状态变化

以 uniswap 兑换为例:

  • 前者是,你输入一个数字之后,另一个的数字会依据当前的市场行情自动算出并填充;
  • 后者是,当 pool 内变化而导致价格变化,这一点同样要体现出来。通俗点说就是数字要自动更新。

所有这些状态应该在前端各个页面流畅地体现出来,即这里牵涉到状态传递地问题。如果不使用状态管理工具库的话,其实很容易写出很繁杂地代码。

可能有人说,这个简单,一个全局对象就行了:一个全局的 json 对象。不错,单从静态最终结果而言,全局对象是数据共享的最简单方法。但是,如何应对数据的变化呢?即,数据变化后能方便地通知出去。你可能会说,这个也很简单,封装成一个 Observable 然后让需要页面订阅就行了。不错哟,连 Observable 都知道。

但,即便如此,我还是希望劝你不要自己干这个事情,因为很快就会又有新的需求出现,而这些已经在那些成熟的状态管理库里实现好了。

这里,我推荐 Akita,简单、轻量、好用,相比 redux 和 ngrx 而言。

至于如何接收链上通知,你可以去看看 ethers.js 的文档,其中有相关的介绍。我建议的最简使用方式,直接监听 block mined 事件,然后去获取你需要的内容。

Web3 和 Metamask

Metamask 在做些 breaking change 的事情,涉及到 API 的变化,比如不再注入 web3 ,取而代之的是 window.ethereum 。如果你还抄的是网上那些老代码,最好赶紧检查一下尽快替换。

关于 api 的情况,请检查其开发文档

部署

最后,说一说部署方式,这一点我原本以为也是常识性内容。

dapp 基本上就是一个静态站点,部署其实很简单,直接一个 web server 就行了。在当今处处大吹特吹“云原生”的时代,我们是不是也应该蹭蹭热点呢?!

停止自己买机器,然后搭 web server,再上传文件的做法!这里,我也发现不少人也没搞清楚前端构建是怎么回事。还以为放进去的代码可以很容易让人看出来咋回事。这完全是 10+ 年前的思维!

就当下而言,主流前端框架基本都至少具备了打包、压缩和混淆的功能,最后的代码基本没法看。因此,这种担心基本是多余的。

就部署方式而言,当前简单、省钱且更优的做法就是选用云服务商的:oss + cdn 架构。比起自建服务器而言,效果完全不是一个档次。

好了,这次就说这么多吧,等有时间再细聊。


广告时间

或许你对以下的付费内容感兴趣 …… 😄