

本文正是由DFINITY软件工程师Kyle Peacock编写的关于如何在互联网计算机上构建前端Dapp的教程,旨在帮助开发者更好的入门

编译:DfinityClub
作者:Kyle Peacock
编者按:根据DFINITY官方公布的最新消息,当前已经有超过500个开发者团队正基于DFINITY互联网计算机开发应用。本文正是由DFINITY软件工程师Kyle Peacock编写的关于如何在互联网计算机上构建前端Dapp的教程,旨在帮助开发者更好的入门。
根据作者的介绍,整个流程将适合区块链开发者,尤其是已经习惯于在Netlify、Fastly 或 S3 存储桶上托管静态资源的工程师。当开发者按照教程步骤一步步完成后,该Dapp或网站将加入到超过1000个已经在互联网计算机上运行的其他网站。
为了方便演示,Kyle Peacock使用了基于 React 构建的静态网站生成器 Gatsby.js。
开始
首先运行npm init gatsby命令,系统将提示关于网站的命名。本教程中网站被命名为
“contact_book”,让相应的文件夹也使用该名称。
接下来,系统提示是否会使用 CMS,回答“不”。从系统提示中选择“styled-components”作为首选项,跳过其他可选功能,然后开始一个新项目!

Gatsby 新项目 CLI
这将设置一个简单的文件结构:
├── README.md├── gatsby-config.js├── package-lock.json├── package.json└── src ├── images │ └── icon.png └── pages ├── 404.js └── index.js
部署静态网站
通过webpack-dev-server的命令npm run develop启动项目,然后我们可以使用npm run build命令将项目编译为静态 HTML、CSS 和 JavaScript 资产。
为了在互联网计算机上托管项目,需要执行以下操作:
创建dfx.json
因为 Gatsby 将其构建输出编译到 public 目录中,所以dfx.json文件将如下所示:
// dfx.json { "canisters": { "www": { "type": "assets", "source": ["public"] } } }
安装dfx
按照 https://dfinity.org/developers/ 上的说明安装 SDK,DFINITY支持Mac、Linux 和 Windows系统的WSL或VirtualBox。
部署网站
通过运行npm run build命令对网站进行编译,然后从与dfx.json相同目录下运行dfx deploy --network ic --no-wallet将网站发布到互联网计算机上。
站点部署完成后,可以通过运行 dfx canister id www 找到您的容器 ID,然后通过浏览器导航到 https://{canisterId}.ic0.app。
如何启动你的第一个容器
如果之前并没有进行容器的部署,那么可以通过之前发布的教程:《分步指南:如何在DFINITY上部署你的第一个容器智能合约》来查看操作步骤。
以上步骤完成之后,就实现了在互联网计算机部署Web应用程序的工作。你可以看到从HTML到图像都正常运行,就像你直接从老式Nginx 或 PHP 静态服务器中提取它们一样。
自定义应用程序
基础工作完成后,就可以对一些代码进行自定义了,比如想构建一个通讯录,可以用新的应用程序逻辑替换src/pages/index.js 中的逻辑。事实上,系统自带一个允许用户创建联系人的表单,可以通过电子邮件地址搜索到联系人,以及一个呈现已保存联系人的组件。
联系人信息可以通过多种方式来保存,比如先将数据作为简单文本写入 localStorage、Firebase 或 MongoDB Atlas,并使用 JSON.stringify() 进行编码。不过本次将在互联网计算机上,使用容器智能合约保存该数据。
添加后端
需要对项目进行一些调整以添加后端容器,大致步骤如下:
添加后端逻辑
创建一个 Motoko 容器,它将实现存储在 HashMap 中设置和获取信息的简单逻辑。
// Main.moimport HM "mo:base/HashMap";import Text "mo:base/Text";import Error "mo:base/Error";import Iter "mo:base/Iter";
actor { stable var entries : [(Text, Text)] = [];
let store: HM.HashMap<Text, Text> = HM.fromIter(entries.vals(), 16, Text.equal, Text.hash);
/// returns null if there was no previous value, else returns previous value public shared func set(k:Text,v:Text): async ?Text { if(k == ""){ throw Error.reject("Empty string is not a valid key"); }; return store.replace(k, v); };
public query func get(k:Text): async ?Text { return store.get(k); };
system func preupgrade() { entries := Iter.toArray(store.entries()); };
system func postupgrade() { entries := []; };
};
这里没有深入研究细节,这段代码使用一个stable var 来记录在升级过程中的数据,并初始化一个HashMap接口,用于存储文本类型Key和 Value的数据。
然后实现set和get接口,并添加preupgrade和postupgrade函数,再次在升级中保留数据。将其保存到 src 目录中的一个新文件夹中,比如位于 src/backend/contact_book/Main.mo。
这段代码是用 Motoko 语言编写的,这是一种由 DFINITY维护的语言,专门针对互联网计算机的功能。互联网计算机支持任何可以编译为 WebAssembly的语言,Rust 和 C 是容器开发的其他流行选择。Motoko 是一种开源语言,可以在官网了解更多信息。
配置 dfx.json
现在需要配置 dfx 以明确新创建的容器,需要为它添加一个新的容器对象,并将其链接为前端容器的依赖项。代码如下:
// dfx.json{ "canisters": { "contact_book": { "main": "src/backend/contact_book/Main.mo" }, "www": { "dependencies": ["contact_book"], "type": "assets", "source": ["public"] } }}
配置Gatsby
接下来需要使用dfx动态生成的代码来配置别名以更新 Gatsby,该代码将位于项目中隐藏的.dfx 文件夹中。
还需要在自己项目的根目录中创建一个 gatsby-node.js 文件,并编写一些代码,这些代码将使用我们在 dfx.json 中的设置来为新部署的后端导入自定义接口。
// gatsby-node.jsconst dfxJson = require("./dfx.json");const webpack = require("webpack");const path = require("path");
const aliases = Object.entries(dfxJson.canisters).reduce( (acc, [name, _value]) => { // Get the network name, or `local` by default. const networkName = process.env["DFX_NETWORK"] || "local"; const outputRoot = path.join( __dirname, ".dfx", networkName, "canisters", name );
return { ...acc, ["dfx-generated/" + name]: path.join(outputRoot, name + ".js"), }; }, {});
exports.onCreateWebpackConfig = ({ stage, actions }) => { actions.setWebpackConfig({ resolve: { alias: aliases, }, plugins: [ new webpack.ProvidePlugin({ Buffer: [require.resolve("buffer/"), "Buffer"], }), ], });};
此外,我们将向 gatsby-config.js 文件添加一个代理,代理 localhost:8000,这是dfx 副本的默认地址。
// gatsby-config.jsmodule.exports = { siteMetadata: { title: "contact book", }, plugins: ["gatsby-plugin-styled-components"], proxy: { prefix: "/api", url: "http://localhost:8000", },};
使用 @dfinity/agent
现在我们已经为 dfx 生成的资源设置了别名,可以导入并在我们的代码库中使用它们。
所以接下来将创建src/actor.js 并从 dfx-generated/contact_book 导入 @dfinity/agent。
// actor.jsimport { Actor, HttpAgent } from "@dfinity/agent";import { idlFactory, canisterId,} from "dfx-generated/contact_book";
const agent = new HttpAgent();const actor = Actor.createActor(idlFactory, { agent, canisterId });
export default actor;
在这里创建了一个代理并将其与从后端接口生成的代码中的 idlFactory 和 canisterId 一起传递给 Actor 构造函数。然后导出 Actor,它有两个方法(set 和 get)并且已经配置了一个基于 Promise 的 API,以对容器后端进行类型安全的调用。
最后连接起来
修改 index.js 页面,使其逻辑存储从我们容器上的表单提交,使用电子邮件字段。
我将导入 Actor(将其作为动态导入执行,以避免在 Gatsby 的服务器端渲染期间初始化 HttpAgent)。
React.useEffect(() => { import("../actor").then((module) => { setActor(module.default); });}, []);
使用设置的方法 handleSubmit 来存储数据,然后清理联系表单。
actor?.set(email, JSON.stringify(card.toJSON())).then(() => { alert("card uploaded!"); inputs.forEach((input) => { input.value = ""; }); setImage("");});
然后使用 get 方法通过电子邮箱搜索联系人。
actor?.get(email).then((returnedCard) => { if (!returnedCard.length) { return alert("No contact found for that email"); } setCard(vCard.fromJSON(returnedCard[0])); console.log(returnedCard);});
现在一个可以在互联网计算机运行的且功能齐全的应用程序就构建完成了。
圆满结束
现在已经调整了我们的代码库,我们的项目结构如下所示:
├── README.md├── dfx.json├── gatsby-config.js├── gatsby-node.js├── package-lock.json├── package.json└── src ├── actor.js ├── backend │ └── contact_book │ └── Main.mo ├── images │ └── icon.png └── pages ├── 404.js └── index.js
现在可以通过运行dfx deploy在本地测试 ,这将构建并上传我们后端在本地的副本。
一旦完成,我们将能够运行 npm run develop -- --port 3000再次运行我们的前端,使用所有不错的开发功能,例如组件的热加载。我们指定了一个端口,因为 Gatsby的默认为端口也是8000。
如果一切顺利,你就可以通过提交然后使用UI页面检索联系人,以进行应用程序的本地测试。
成功检索联系人的视图
以上为成功后的效果展示图,你可以自己尝试这些步骤,从 https://github.com/krpeacock/ic-vcf-gatsby 下载这个示例项目,或者使用本指南作为参考来开始你自己的项目。
欢迎在sdk.dfinity.org上构建应用程序并加入DFINITY开发者社区:forum.dfinity.org。
