ddd009

ddd009

twitter

Dfinity フロントエンド開発チュートリアル

title: Dfinity フロントエンド開発チュートリアル#

date: 2021-08-05 15:58:32

Dfinity フロントエンド開発チュートリアル#

作者 转载请注明出处

前段時間 Uniswap の特定のコインのフロントエンド制限が大きな議論を呼びました。Dfinity は優れたフルスタックの分散型ソリューションです。本チュートリアルでは、dfx new helloworldから始めて、Dfinity 上でのフロントエンド開発の方法を詳しく紹介し、公式が提供する identity サービス(identity.ic0.app)の使用方法についても説明します。一緒に分散型フロントエンド開発の世界に参加しましょう!
この記事を読む前に、dfx 開発ツールの使用に慣れておく必要があります。

チュートリアル目次:#

  • hello world から始める

  • フロントエンドスキャフォールド

  • webpack の設定と dev サーバーについて

  • サードパーティの canister を呼び出す方法

  • Internet Identity サービスの使用方法

hello world から始める#

任意のディレクトリでdfx new helloworldと入力すると、初期プロジェクトが作成されます。dfx start & dfx deployを実行します。

ブラウザでフロントエンドid.localhost:8000(推奨)またはlocalhost:8000/?canisterId=フロントエンドidと入力すると、下の画像のようなインターフェースが得られます。

截屏 2021-07-28 下午 5.54.03.png

js コードは以下の通りです。

import { Actor, HttpAgent } from "@dfinity/agent";
import {
  idlFactory as helloworld_idl,
  canisterId as helloworld_id,
} from "dfx-generated/helloworld";

const agent = new HttpAgent();
const helloworld = Actor.createActor(helloworld_idl, {
  agent,
  canisterId: helloworld_id,
});

document.getElementById("clickMeBtn").addEventListener("click", async () => {
  const name = document.getElementById("name").value.toString();
  const greeting = await helloworld.greet(name);
  document.getElementById("greeting").innerText = greeting;
});

このコードは最初に HttpAgent を作成し、ネットワークリクエストを送信します。ここで agent のデフォルトの host はあなたのネットワーク環境のアドレスです。例えば、localhost:8000 にフロントエンドをデプロイした場合、agent は localhost:8000 にリクエストを送信します。したがって、dev サーバーを使用する際は、手動で host を設定することに注意してください。以下に host を設定するコードを示します(本番環境では使用しないでください)。

const agent = new HttpAgent({
     host: "http://localhost:8000",
})

Dfinity の canister は Actor モデルを使用しているため、フロントエンドが canister を呼び出す際には、まず actor を作成する必要があります。ここでは、バックエンドを呼び出すためのツールとして簡単に理解できます。

次は actor を作成するコードで、ここでは 3 つのパラメータ、IDL、agent、canisterId が必要です。agent は先ほど作成した HttpAgent で、他の 2 つのパラメータはサードパーティの canister を呼び出す方法で紹介します。

actor の作成が完了したら、actor を通じてバックエンドの public method を呼び出すことができます。ここでの呼び出しはすべて非同期呼び出しです。非同期呼び出しの処理については本チュートリアルでは詳しく説明しませんが、比較的推奨される方法は async/await 構文を使用することです。このコードは greeting メソッドを呼び出し、テキストを返します。

以上が最もシンプルな Dfinity フロントエンドアプリケーションです。これらのプロセスを理解すれば、フロントエンドとバックエンドの相互作用が可能になります。

フロントエンドスキャフォールド#

私たちが vue や react などのフレームワークを使用してフロントエンド開発を行う際、一般的にスキャフォールドを使用します。Dfinity コミュニティには多くの利用可能なスキャフォールドがありますが、現在のところほとんどが初期段階にあり、使用中にいくつかの問題に直面する可能性があります。
create-ic-app
create-ic-app は新しいフロントエンドビルドツール Vite を使用しており、react、vue、typescript、vanilla などをサポートしており、継続的に更新されています。Vite の dev サーバーは webpack よりもはるかに速く、試してみることをお勧めします。
dfinity-vue
vue スキャフォールドで、1 つのブランチに vuetify が統合されています。使用する場合は vuetify ブランチに切り替えてください。
cra-template-dfx
react スキャフォールドで、最後の更新は 7 ヶ月前で、参考用としてのみ推奨されません。

webpack の設定と dev サーバーについて#

Dfinity プロジェクトはデフォルトで webpack を使用してパッケージ化され、自動的に設定ファイルが生成されます。私たちが実際に行うことは非常に少ないです。
webpack について説明する前に、dfx.json を開いてデフォルト設定を確認する必要があります。dfx ツールを頻繁に使用している場合、dfx.json には非常に慣れているはずですが、ここでも簡単に説明します。

{
 "canisters": {
     ...
    //assetsはフロントエンドcanisterの名前で、Dfinityではすべてのコードがcanisterにデプロイされます。
   "assets": {
       //フロントエンドcanisterの依存関係
     "dependencies": [
       "backend"
     ],
    //frontend.entrypointはフロントエンドファイルのエントリーポイントで、これをwebpackで使用します。
     "frontend": {
       "entrypoint": "dist/index.html"
     },
   //sourceはdistとsrcディレクトリを指定します。
     "source": [
       "dist/"
     ],
   //canisterのタイプをフロントエンドcanisterとして指定します。
     "type": "assets"
   }
 }
}

截屏 2021-07-28 下午 7.47.19.png

dfx を使用したことがある方は、その開発体験が非常に悪いことを知っているでしょう。コードを変更するたびに再コンパイルとデプロイが必要です。フロントエンド開発にとって、dev サーバーが必要です。

webpack-dev-server をインストールするには、npm i webpack-dev-serverと入力します。

インストールが完了したら、webpack serveまたはwebpack-dev-serverコマンドで Dev Server を起動できます。私のバージョンは 3.11.2 で、webpack serveを使用して起動する必要があります。

package.json に dev オプションを設定することもできます。

截屏 2021-07-30 下午 12.24.21.png

これでnpm run devと入力して Dev サーバーを起動できます。

webpack にプラグインnew webpack.HotModuleReplacementPlugin(),を追加し、

dev サーバーの設定を追加します。

   devServer: {
      hot: true,
    }

これでファイルを変更すると自動的に再コンパイルされるようになります。

サードパーティの canister を呼び出す方法#

内容はkyle のブログを参考にしています。

canister を呼び出すには、2 つのパラメータが必要です。1 つは IDL インターフェースの説明、もう 1 つは canister ID です。

IDL は canister のデータ型とインターフェースの説明です。ローカルで canister をデプロイすると、自動的に canister.did.js ファイルが生成されます。

截屏 2021-07-31 下午 2.33.38.png

export default ({ IDL }) => {
  return IDL.Service({ 'greet' : IDL.Func([IDL.Text], [IDL.Text], []) });
};
export const init = ({ IDL }) => { return []; };

hello world の IDL はこのようになります。

import { idlFactory } from "dfx-generated/helloworld";
または
import { idlFactory } from './helloworld.did.js';

IDL をインポートします。

サードパーティの canister の場合、ic.rocks で対応する IDL ファイルを見つけることができます。identity サービスの例を挙げます。

截屏 2021-07-31 下午 2.46.28.png

截屏 2021-07-31 下午 2.46.52.png

javascript を選択し、新しい identity.did.js を作成してコピー&ペーストします(ts は identity.did.d.ts です)。

// src/declarations/identity/index.js
import { Actor, HttpAgent } from "@dfinity/agent";

// Imports and re-exports candid interface
import { idlFactory } from './identity.did.js';
export { idlFactory } from './identity.did.js';
// CANISTER_IDはnode環境に基づいてwebpackによって置き換えられます。
export const canisterId = process.env.IDENTITY_CANISTER_ID;

/**
 * 
 * @param {string | import("@dfinity/principal").Principal} canisterId AgentのCanister ID
 * @param {{agentOptions?: import("@dfinity/agent").HttpAgentOptions; actorOptions?: import("@dfinity/agent").ActorConfig}} [options]
 * @return {import("@dfinity/agent").ActorSubclass<import("./identity.did.js")._SERVICE>}
 */
 export const createActor = (canisterId, options) => {
  const agent = new HttpAgent({ ...options?.agentOptions });
  
  // 開発中の証明書検証のためにルートキーを取得します。
  if(process.env.NODE_ENV !== "production") {
    agent.fetchRootKey().catch(err=>{
      console.warn("ルートキーを取得できません。ローカルレプリカが実行中であることを確認してください。");
      console.error(err);
    });
  }

  // candidインターフェースとHttpAgentを使用してactorを作成します。
  return Actor.createActor(idlFactory, {
    agent,
    canisterId,
    ...options?.actorOptions,
  });
};
  
/**
 * identity canister用の準備が整ったエージェント
 * @type {import("@dfinity/agent").ActorSubclass<import("./identity.did.js")._SERVICE>}
 */
 export const identity = createActor(canisterId);

identity サービスを呼び出すための actor を作成します。

candid ファイルだけがある場合、dfx ツールを使用して did.js を生成できますが、この方法はかなり面倒です。もう 1 つの簡単な方法は、公式が提供するdidc ツールを使用することです。

自動インストールスクリプト:(コピーして sh ファイルとして保存して実行)

unameOut="$(uname -s)"
case "${unameOut}" in
    Linux*)     machine=Linux;;
    Darwin*)    machine=Mac;;
    *)          machine="UNKNOWN:${unameOut}"
esac

release=$(curl --silent "https://api.github.com/repos/dfinity/candid/releases/latest" | grep -e '"tag_name"' | cut -c 16-25)

if [ ${machine} = "Mac" ]
then
  echo "Mac用のdidcを~/bin/didcにダウンロードしています"
  curl -fsSL https://github.com/dfinity/candid/releases/download/${release}/didc-macos > /usr/local/bin/didc
elif [ ${machine} = "Linux" ]
then
  echo "Linux用のdidcを~/bin/didcにダウンロードしています"
  curl -fsSL https://github.com/dfinity/candid/releases/download/${release}/didc-linux64 > ~/bin/didc
else
  echo "サポートされているオペレーティングシステムを検出できませんでした。didcは現在MacとLinuxのみサポートされています。"
fi

date

インストールが完了したら、didc bind ./identity.did -t js > ./identity.did.jsと入力すると did.js ファイルが生成されます(didc bind ./identity.did -t ts > ./identity.did.d.tsは ts バージョン)。

Internet Identity サービスの使用方法#

Internet Identity(略称 II)は Dfinity が公式に提供する DID サービスで、WebAuth に基づいています。Dfinity 上のアプリケーションは一般的に II ログインをサポートします。

ローカルの開発環境で II をデバッグするには、Ineternet Identityをローカルにクローンし、開発用の dfx ネットワーク環境にデプロイする必要があります。

git clone [email protected]:dfinity/internet-identity.git
cd internet-identity
dfx start --clean

ここでは --clean オプションを使用して起動することをお勧めします。なぜなら、II サービスのデフォルト canister ID がローカルウォレットのデフォルト ID と同じであるため、環境を掃除して衝突を避けるためです。ローカルで複数の dfx ネットワークを起動できるため、II をデプロイしたネットワークとプロジェクトのネットワークが同じ(ポートが同じ)であることを確認してください。

npm install
II_ENV=development dfx deploy --no-wallet --argument '(null)'

これで II のローカルデプロイが完了しました。

package.json を開いて、@dfinity/auth-client、@dfinity/authentication がインストールされていることを確認してください。

以下はフロントエンドが II サービスを呼び出すためのコードです。

const init = async () => {
  authClient = await AuthClient.create();
  await authClient.login({
      maxTimeToLive: BigInt("0x7f7f7f7f7f"),
      identityProvider:
        "http://localhost:8000/?canisterId=rwlgt-iiaaa-aaaaa-aaaaa-cai",
      onSuccess: async () => {
        handleAuthenticated(authClient);
      },
    });
}

このコードも非常に理解しやすいです。最初に authclient を作成し、次に login メソッドを呼び出します。
maxTimeToLive は委任キーの有効時間を設定し、identityProvider は II がデプロイされた canister です。メインネットの場合は identity.ic0.app になります。onSuccess は認証完了後のコールバック関数です。ここには指定できる多くのパラメータがあり、具体的にはAgent JS Docsを参照してください。詳しくは説明しません。

次に、認証された呼び出しを開始する方法について説明します。前述の hello world の例では、ランダムに生成された匿名のアイデンティティを使用して呼び出しを開始する方法を説明しました。ユーザーが認証を完了した後、彼らのアイデンティティを取得できるようになります。ユーザーのアイデンティティを使用して認証された呼び出しを開始する方法は以下の通りです。agent を作成する際に identity を渡します。

 async function handleAuthenticated(authClient) {
     identity = await authClient.getIdentity(); 
      const agent = new HttpAgent({
        identity: identity,
        host: "http://localhost:8000",
      });
     //ローカル開発時にはRoot Keyを取得する必要があります。
      if(process.env.NODE_ENV !== "production") await agent.fetchRootKey();
      let actor = Actor.createActor(idlFactory, {
        agent,
        canisterId: canisterId,
      });
     const greeting = await actor.greet(name);
  }
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。