导入库模块

在本教程中,您将编写一个简单的 dapp,使您能够存储和查找电话号码。 本教程说明了如何导入和使用一些基本的 Motoko 库函数。

对于本教程,Motoko 基础库函数在 ListAssocList 模块中定义,使您能够将列表作为链接的键值对使用。 在本例中,keynamevalue 是与该名称关联的 phone 文本字符串。

该 dapp 支持以下函数调用:

  • insert 函数接受 namephone 键值对作为存储在 book 变量中的输入。

  • lookup 函数是一个查询,它使用指定的 name 键作为输入来查找关联的电话号码。

开始之前

在开始本教程之前,请验证以下内容:

  • 您已按照下载并安装中的说明下载并安装了 {sdk-short-name} 包。

  • 您已经停止了 dfx 提供的本地容器执行环境。

完成本教程大约需要 10 分钟。

创建一个新项目

要为本教程创建一个新项目:

  1. 在本地计算机上打开终端 shell(如果您还没有的话) 有一个打开。

  2. 更改为您用于 Internet Computer 项目的文件夹(如果您正在使用一个)。

  3. 通过运行以下命令创建一个新项目:

    dfx new phonebook
  4. 通过运行以下命令切换到您的项目目录:

    cd phonebook

修改默认dapp

对于本教程,让我们为简单的电话号码查找 dapp 创建一个新的 main.mo 文件。

修改默认模板:

  1. 在文本编辑器中打开 src/phonebook/main.mo 文件并删除现有内容。

  2. 将以下示例代码复制并粘贴到 main.mo 文件中:

    // Import standard library functions for lists
    
    import L "mo:base/List";
    import A "mo:base/AssocList";
    
    // The PhoneBook actor.
    actor {
    
        // Type aliases make the rest of the code easier to read.
        public type Name = Text;
        public type Phone = Text;
    
        // The actor maps names to phone numbers.
        flexible var book: A.AssocList<Name, Phone> = L.nil<(Name, Phone)>();
    
        // An auxiliary function checks whether two names are equal.
        func nameEq(l: Name, r: Name): Bool {
            return l == r;
        };
    
        // A shared invokable function that inserts a new entry
        // into the phone book or replaces the previous one.
        public func insert(name: Name, phone: Phone): async () {
            let (newBook, _) = A.replace<Name, Phone>(book, name, nameEq, ?phone);
            book := newBook;
        };
    
        // A shared read-only query function that returns the (optional)
        // phone number corresponding to the person with the given name.
        public query func lookup(name: Name): async ?Phone {
            return A.find<Name, Phone>(book, name, nameEq);
        };
    };

    在查看此示例 dapp 时,您可能会注意到以下关键元素:

    • 代码将 NamePhone 定义为自定义文本类型。 创建用户定义的类型可以提高代码的可读性。

    • insert 函数是更新调用,lookup 函数是查询调用。

    • Phone 类型通过使用`?Phone` 语法被识别为可选值。

启动本地容器执行环境

出于开发目的,“dfx”提供了一个本地容器执行环境。 这需要一个 dfx.json 文件,因此您应该确保您位于项目的根目录中。 对于本教程,您应该有两个独立的终端 shell,以便您可以在一个终端中启动并查看本地容器执行环境的输出,并在另一个终端中管理您的项目。

启动本地容器执行环境:

  1. 在本地计算机上打开一个新的终端窗口或选项卡。

  2. 如有必要,导航到项目的根目录。

    • 您现在应该打开了两个终端

    • 您应该将 project 目录 作为您的 current 工作目录

  3. 通过运行以下命令在本地计算机上启动本地容器执行环境:

    dfx start --clean

    对于本教程,我们使用 --clean 选项以干净状态启动本地容器执行环境。

    此选项会删除任何可能会中断正常操作的孤立后台进程或容器标识符。 例如,如果您在项目之间移动时忘记发出`dfx stop,则可能有一个进程在后台或另一个终端中运行。 `--clean 选项确保您可以启动本地容器执行环境并继续下一步,而无需手动查找和终止任何正在运行的进程。

  4. 将显示本地容器执行环境的输出的终端保持打开状态,然后将焦点切换到创建新项目的原始终端。

注册、构建和部署 dapp

一旦本地容器执行环境在您的开发环境中启动并运行,您就可以在其上注册、构建和部署您的 dapp。

要在本地部署 dapp:

  1. 如果需要,请检查您是否仍在项目的根目录中。

  2. 通过运行以下命令在本地注册、构建和部署您的 dapp:

    dfx deploy phonebook

    dfx.json 文件提供了用于创建 dapp 前端入口点和 assets 容器智能合约的默认设置。

    在之前的教程中,我们删除了资产容器的条目,因为我们没有为示例 dapp 添加前端。 通过消除将不使用的文件,该更改使我们的项目工作区保持整洁。 但是,没有要求这样做,将资产容器智能合约描述保留在“dfx.json”文件中也没有什么坏处。 例如,如果您打算稍后添加前端资产,您可能希望将其用作占位符。

    对于本教程,您可以使用 dfx deploy phonebook 命令仅部署电话簿后端容器智能合约,因为该项目不包含任何前端资产,您将通过终端与其交互。

    虽然本教程说明了如何跳过编译前端容器,但您可以稍后通过浏览链接为这个 dapp 添加一个简单的用户界面link:https://github.com/dfinity/examples/tree/master/motoko/phone- 链接中的 book[phone-book] 项目examples 存储库。

使用插入函数添加名称和数字

您现在在本地容器执行环境中部署了一个 dapp 作为 canister 智能合约,并且可以使用 dfx canister call 命令测试您的 dapp。

要测试您已部署的 dapp:

  1. 使用 dfx canister call 命令通过 insert 函数调用 canister 智能合约 phonebook,并通过运行以下命令将名称和电话号码传递给它:

    dfx canister call phonebook insert '("Chris Lynn", "01 415 792 1333")'
  2. 通过运行以下命令添加第二个名称和号码对:

    dfx canister call phonebook insert '("Maya Garcia", "01 408 395 7276")'
  3. 通过运行以下命令,使用 lookup 函数验证该命令是否返回与“Chris Lynn”关联的数字:

    dfx canister call phonebook lookup '("Chris Lynn")'

    该命令返回类似于以下内容的输出:

    (opt "01 415 792 1333")
  4. 通过运行以下命令,尝试使用与“Maya Garcia”关联的数字调用 lookup 函数:

    dfx canister call phonebook lookup '("01 408 395 7276")'

    请注意,在这种情况下,该命令返回 (null),因为电话号码不是与“Maya Garcia”名称条目关联的键。

  5. 尝试再次调用 lookup 函数,通过运行以下命令返回“Maya Garcia”和“Chris Lynn”的电话号码:

    dfx canister call phonebook lookup '("Maya Garcia","Chris Lynn")'

    因为 dapp 被编写为为一个键返回一个值,所以该命令仅返回与第一个键关联的信息,在本例中为“Maya Garcia”的电话号码。

在浏览器中测试功能

容器接口描述语言(通常称为 Candid 或更一般地称为 IDL)为指定容器智能合约的签名提供了一种通用语言。 Candid 为您提供了一种统一的方式来与用不同语言编写或使用不同工具访问的容器智能合约进行交互。 例如,无论底层程序是原生 Rust、JavaScript 还是 Motoko,Candid 都能提供一致的服务视图。 Candid 还启用了不同的工具——例如`dfx` 命令行界面和网络神经系统 dapp——来共享服务的通用描述。

基于 actor 的类型签名,Candid 还提供了一个 Web 界面,允许您调用容器函数进行测试和调试。

使用 dfx deploydfx canister install 命令在本地容器执行环境中部署项目后,您可以在浏览器中访问 Candid Web 界面端点。 此 Web 界面 — Candid UI — 以表单形式公开服务描述,使您无需编写任何前端代码即可快速查看和测试功能并尝试输入不同的数据类型。

要使用 Candid Web 界面测试容器功能:

使用 dfx canister id __Candid_UI 命令查找与当前项目关联的 Candid UI 容器标识符。

+

dfx canister id __Candid_UI

+ 该命令显示 Candid UI 的容器标识符,输出类似于以下内容:

+

r7inp-6aaaa-aaaaa-aaabq-cai

+ . 复制 Candid UI 容器标识符,使其在剪贴板中可用。 . 如果您已停止本地容器执行环境,请通过运行以下命令在本地重新启动它:

+

dfx start --background
  1. 打开浏览器并导航到 dfx.json 配置文件中指定的地址和端口号。

    默认情况下,本地容器执行环境绑定到`127.0.0.1:8000`地址和端口号。

  2. 将所需的 canisterId 参数和 dfx canister id __Candid_UI 命令返回的 Candid UI 容器标识符添加到 URL。

    例如,完整的 URL 应该类似于以下内容,但带有由 dfx canister id __Candid_UI 命令返回的`CANDID-UI-CANISTER-IDENTIFIER`:

    http://127.0.0.1:8000/?canisterId=<CANDID-UI-CANISTER-IDENTIFIER>

    例如,对于 Candid UI 的示例容器标识符,如上所示,可能如下所示:

    http://127.0.0.1:8000/?canisterId=r7inp-6aaaa-aaaaa-aaabq-cai

    然后,浏览器会显示一个表单供您指定容器标识符或选择 Candid 描述 (.did) 文件。 请注意,此字段是指您要与之交互的容器的容器标识符(与我们在上一步中使用的 Candid UI 的容器标识符相反)。

  3. Provide a canister ID 字段中指定要测试的容器的容器标识符,然后单击 Go 以显示服务描述。

    如果您不确定要使用哪个容器标识符,可以运行 dfx canister id 命令来查找特定容器名称的标识符。 例如,要获取名为 my_counter 的容器的容器标识符,您可以使用:

    dfx canister id my_counter
  4. 查看 dapp 中定义的函数调用和类型列表。

  5. 为函数键入适当类型的值或单击 Random 生成值,然后单击 CallQuery 以查看结果。

    请注意,根据数据类型,Candid 界面可能会显示用于测试功能的其他配置设置。 例如,如果一个函数接受一个数组,您可能需要在输入值之前指定数组中的项目数。

    Phonebook functions

修改 dapp 中的源代码

要扩展您在本教程中学到的知识,您可能需要尝试修改源代码以返回不同的结果。

例如,您可能希望更改源代码,以便代替插入和查找当前键值(姓名-电话)对的 dapp 来创建存储联系人信息的 dapp,类似于数据库“记录”,其中一个主键与多个字段相关联。 在此示例中,您的 dapp 可能允许用户或其他 dapp 添加信息——例如家庭电话号码、手机号码、电子邮件地址和街道地址——并有选择地返回所有或特定字段值。

停止本地容器执行环境

完成 dapp 试验后,您可以停止本地容器执行环境,使其不会继续在后台运行。

要停止本地容器执行环境:

通过运行以下命令停止本地容器执行环境:

+

dfx stop