Substrate 前端开发系列 - 1/2在前面的文章中,大家应该对使用 Substrate 进行开发有了初步的了解。现在,我们能够启动节点,编写运行时来执行特定功能,甚至能够构建一个包含多个节点的网络。然而,最终用户并不直接与这个区块链网络进行交互。因此,我们需要搭建一个前端,以便用户可以通过它来与网络进行交互。接下来,我们将介绍如何利用 Substrate 生态中的 Polkadot-JS API(以下简称 JS API)来实现前端与 Substrate 节点的交互。尽管项目名称中包含“Polkadot”,但实际上它能够连接到所有基于 Substrate 开发的节点。本篇文章是前端系列的第一篇,我们将深入探讨如何使用 JS API 连接到 Substrate 节点并进行交互。本文内容适用于任何前端框架,即使是创建一个 Node.js 的中间件来订阅 Substrate 节点事件(events)也是可以的。如果你打算使用 React 来构建前端,请关注我们接下来的文章,我们将介绍如何在 Substrate Front-end Template 的基础上构建你的前端,该模板将 Polkadot-JS API 封装在 React 组件中。在本机成功运行 Substrate(还未做到这一步的朋友可以参考这里)的情况下,假设 Substrate 的 WebSocket 端口设置为默认的 localhost:9944。连接到开发节点首先,在你的 JS 项目中添加 JS API 的库。我们建议使用 yarn 作为你的项目包管理工具。在与 Substrate 网络互动之前,创建一个 api 对象,如下所示:注意,我们使用 ES2015 的 JS 规范来编写代码,因此使用 import 来引用外部库,并支持 async / await 等功能。完成以上操作后,你可以从 api 对象中获取所有与 Substrate 交互所需的函数和常量。读取链上数据(Queries)接下来,以下是如何获取链上数据的示例。获取链上数据的方法是使用 api.query,而 query 后的名字是在连接到 Substrate 节点时动态创建的,取决于连接的 Substrate 网络加载了哪些模块(pallets)、这些模块中的存储项(storage)以及对应的读取函数(getter function)。了解更多关于 Substrate 存储项读取函数的信息。基本原则如下:这些函数只会对链上数据进行简单的读取操作。因为这是需要与 Substrate 节点实时交互的操作,所以它是一个异步操作,返回一个 Promise,然后使用 await 等待结果。订阅链上数据的变化(Subscription)在前端开发过程中,你有时不仅需要在加载网页的那一刻获取链上的数据,还需要随着这些链上数据的变更而动态更改页面上的内容。这也是为什么我们一开始连接时,不是使用简单的 HTTP API 请求,而是使用 WebSocket 连接。在上面的获取用户余额的例子中,你也可以传入一个回调函数。这样,除了获取用户余额之外,每当余额数值发生变化时,也会进行回调。使用这种方法,返回的将是一个取消订阅函数。当你不再需要监听该数值时,就可以调用这个函数。JS API 还有一个便捷的方法,可以一次性订阅多个链上数值。例如:使用 api.queryMulti(queries 数组,回调函数)。查询节点常量(Constants)方法和读取链上数据类似。在获取 api 接口后,可以使用 api.const...toNumber() 来获取。以下是一些示例。需要注意的是,常量在 api 连接到节点时就已经获取了,因此它们是直接返回的,不需要以 Promise 的方式异步返回。其次,尽管常量是一个数字,但返回时 JS API 会将其封装到一个对象中,需要使用 toNumber() 从该对象中取出在 JS 中可以识别的值。关于自定义结构的更多信息,我们将在后面的文章中介绍。提交外部交易(Extrinsics)这部分操作将直接更改链上的数据,并且都需要有一个用户/主体对调用函数进行签名,因此我们称之为外部交易。以下是一个示例,即用户 Alice 将 12345 个单位货币打款到另一个账户:其使用方法是:返回的是一个哈希值,表示该交易已被记录到区块链上。但这并不意味着交易已经成功执行。因此,接下来我们可以监听事件(events)来确定交易是否已成功完成(或出现错误)。此外,你可能也注意到了,我们还没有提到 Alice 是什么值。这部分将在账户管理/签名部分进行讨论。使用以下方法,我们可以监听自己提交的交易。方法和上面订阅链上数据类似:我们将回调函数放在 signAndSend 的最后一个参数中。这个回调函数将返回一个包含两个属性的对象。账户管理/签名(Keyring)接下来,我们将详细说明上文提到的 Alice 用户对象是如何获得的。这当然不是一条字符串地址。如果这样,那么任何人都可以冒充另一个人向其他人发送交易。要创建用户对象,首先将 @polkadot/keyring 库添加到项目中:然后在 JS 代码中:接下来,你可以使用以下几种方法创建具有公钥和私钥的用户账户:然后,你可以这样签名和验证信息:当你使用 signAndSend() 提交外部交易时,内部已经自动对交易信息进行了签名操作。读取及订阅网络内的事件(Events)你也可以使用类似链上数据查询的方法来订阅链上发出的所有事件。方法如下:上面是查询 System 模块中读取所有 events。在回调函数中,过滤掉我们不关注的事件(第 15 行),然后对我们关注的事件进行处理。收听自定义类型(Custom Types)当 Substrate 将数据返回到 JS 时,并不会返回元数据(meta-data)。因此,当 Substrate 网络内有自定义类型时,我们相应地也要在前端将这些类型的定义作为参数输入,以便 JS API 在收到这些数据时可以重构为这些对象。另外,需要注意的是,Substrate 节点是用 Rust 编写的。Rust 的数据类型和 JS 也没有一对一的对应关系。因此,有一个库 @polkadot/types 将 Rust 的基本数据类型封装成不同的 JS 对象。当我们要处理自定义类型时,首先在项目中加载这个库:然后在初始化 api 对象时,可以传入一个 types 参数,说明前端会触及到的所有自定义数据类型。以下是一个示例:在这个示例中,我们定义了一个 Price 类型,其中包含三个属性:dollars、cents、currency。它们分别对应 u32、u32、Vec 类型。这些数据类型都是在 Substrate Node 那边定义的。因此,这方面的信息需要提前由 Substrate Runtime 开发人员告知前端开发人员。而 @polkadot/types 将 u32、u8、Vec 等 Rust 类型在 JS 中进行了封装,创建成一个独立的对象。这个对象中包含了一些函数,可以进一步处理数据。例如,toJSON()、toString()、isEmpty()、toNumber() 等。小结本文介绍了如何在 JS 中建立一个与 Substrate 网络交互的前端。主要使用 Polkadot-JS API 进行。你可以通过 JS API 读取和订阅链上数据及常量、提交外部交易、创建用户账户并使用它来签名交易等。实际上,这里只是概括性地描述了 Polkadot JS 的功能。更详细的内容可以在它的官方文档中查看。此外,你还可以现在尝试使用 JS API 构建前端。下一步,你可以:本篇读后有什么意见,欢迎在下方留言。对了,如果这篇文章你已经读到这儿,可能你还有兴趣了解两个消息: