フロントエンドの概要

Internet Computer プラットフォーム では、私たちの JavaScript agent を使って、あなたの dapps のために Web 3.0 のフロントエンドをホストすることができます。また、dfx が提供する asset canister を使って静的ファイルを Internet Computer にアップロードすることで、アプリケーション全体を分散型技術で実行することができます。このセクションでは、dfx new で提供されるデフォルトのフロントエンドテンプレート、フロントエンドの設定オプション、プロジェクトのユーザーインターフェイスを構築するための他のフレームワークの使用について詳しく説明します。

ここではフロントエンド Dapps の開発の様々な段階でのサンプルコードを含むチュートリアルへのクイックリンクをご紹介します。

デフォルトテンプレートの使用方法

チュートリアルでお気づきのように、プロジェクトにはテンプレートの index.jswebpack.config.js ファイルが含まれています。

デフォルトでは、index.js ファイルは、src/declarations フォルダにあるエージェントをインポートします。このディレクトリは、dfx deploy を実行しローカルまたは Internet Computer にデプロイするときに、dfx によって生成されます。

その生成されたコードは以下のようになります。

import { Actor, HttpAgent } from "@dfinity/agent";

// candid interfaceをimport
import { idlFactory } from './hello.did.js';
// CANISTER_ID は、ノード環境に応じて webpack で置き換え
export const canisterId = process.env.HELLO_CANISTER_ID;

/**
 *
 * @param {string | Principal} canisterId Canister ID of Agent
 * @param {{agentOptions?: import("@dfinity/agent").HttpAgentOptions; actorOptions?: import("@dfinity/agent").ActorConfig}} [options]
 * @return {import("@dfinity/agent").ActorSubclass<import("./hello.did.js")._SERVICE>}
 */
export const createActor = (canisterId, options) => {
  const agent = new HttpAgent({ ...options?.agentOptions });

  // 開発中の証明書検証のためのルートキーをfetch
  if(process.env.NODE_ENV !== "production") agent.fetchRootKey();

  // actor をcandid インターフェースとHttpAgentを利用して生成
  return Actor.createActor(idlFactory, {
    agent,
    canisterId,
    ...options?.actorOptions,
  });
};

/**
 * A ready-to-use agent for the hello canister
 * @type {import("@dfinity/agent").ActorSubclass<import("./hello.did.js")._SERVICE>}
 */
export const hello = createActor(canisterId);

そして、index.js に戻ると、生成されたアクターを受け取り、それを使って hello Canisterの greet メソッドを呼び出しているのがわかります。

import { hello } from "../../declarations/hello";

document.getElementById("clickMeBtn").addEventListener("click", async () => {
  const name = document.getElementById("name").value.toString();
  // Interact with hello actor, greet methodをコール
  const greeting = await hello.greet(name);

  document.getElementById("greeting").innerText = greeting;
});

多くのプロジェクトでは、declarations にあるコードをそのまま使うことができ、hello_assets/src に変更を加えることができます。しかし、あなたのプロジェクトに追加の要件がある場合は、以下を読み進めてください。

webpackの設定を変更する

webpack は、JavaScript ベースのアプリケーション用のモジュールバンドラーとして人気があり、高度な設定が可能であるため、新しいプロジェクトでは、デフォルトの webpack.config.js ファイルが作成され、使用したい特定のモジュール(reactmarkdown など)を簡単に追加できるようになっています。

テンプレートの webpack.config.js ファイルのコードを確認すると、ローカル開発の場合は .dfx/local/canister_ids.json から、その他の環境を設定した場合は ./canister_ids.json から Canister ID を推定しています。どのネットワークを使用するかは、プロセス変数の DFX_NETWORK や、NODE_ENV"production" に設定されているかどうかに基づいて決定されます。

これらの手順は以下のコードブロックで見ることができます。

let localCanisters, prodCanisters, canisters;

try {
  localCanisters = require(path.resolve(".dfx", "local", "canister_ids.json"));
} catch (error) {
  console.log("No local canister_ids.json found. Continuing production");
}

function initCanisterIds() {
  try {
    prodCanisters = require(path.resolve("canister_ids.json"));
  } catch (error) {
    console.log("No production canister_ids.json found. Continuing with local");
  }

  const network =
    process.env.DFX_NETWORK ||
    (process.env.NODE_ENV === "production" ? "ic" : "local");

  canisters = network === "local" ? localCanisters : prodCanisters;

  for (const canister in canisters) {
    process.env[canister.toUpperCase() + "_CANISTER_ID"] =
      canisters[canister][network];
  }
}
initCanisterIds();

エントリーとアウトプットの設定

多くの場合、デフォルトの webpack.config.js ファイルをそのまま、何も変更せずに使用することもできますし、プラグインやモジュール、その他のカスタム構成を追加して、ニーズに合わせて使用することもできます。 具体的に webpack.config.js の設定をどのように変更するかは、使用したい他のツールやフレームワークに大きく依存します。

例えば、フロントエンドのチュートリアルである フロントエンドのカスタマイズスタイルシートの追加 を試したことがある方は、React JavaScript で動作するように以下の部分を変更しているかもしれません。

    module: {
      rules: [
        { test: /\.(ts|tsx|jsx)$/, loader: "ts-loader" },
        { test: /\.css$/, use: ['style-loader','css-loader'] }
      ]
    }
  };
}

ビルドスクリプトの実行に dfx を使用しないアプリケーションの場合は、自分で変数を用意することができます。例えば、以下のようになります。

DFX_NETWORK=staging NODE_ENV=production HELLO_CANISTER_ID=rrkah... npm run build

ノードがプロジェクトで利用可能であることを確認する

プロジェクトは、デフォルトのフロントエンドのフレームワークを提供するためにwebpackに依存しているので、開発環境に node.js がインストールされ、プロジェクトディレクトリにアクセスできる必要があります。

  • デフォルトの webpack 設定と Canister のエイリアスを使用せずにプロジェクトを開発したい場合は、dfx.json ファイルから assets Canister を削除するか、特定の Canister 名を使用してプロジェクトをビルドすることができます。例えば、以下のコマンドを実行すると、フロントエンドのアセットを使わずに hello プログラムだけをビルドすることができます。

    dfx build hello
  • デフォルトの webpack 構成を使用していて、dfx build の実行に失敗する場合は、プロジェクトディレクトリで npm install を実行してから、dfx build を再実行してください。

  • プロジェクトディレクトリで npm install を実行しても問題が解決しない場合は、webpack.config.js ファイルの設定にシンタックスエラーがないか確認してください。

他のモジュールをReactフレームワークで使用する

リポジトリにあるいくつかのチュートリアルやサンプルプロジェクトでは、npm install コマンドを使ってReactモジュールを追加する方法が説明されています。 これらのモジュールを使って、プロジェクトで使用したいユーザーインターフェースコンポーネントを構築することができます。 例えば、以下のコマンドを実行して、react-router モジュールをインストールすることができます。

npm install --save react react-router-dom

このモジュールを使って、以下のようなナビゲーションコンポーネントを作ることができます。

import React from 'react';
import { NavLink } from 'react-router-dom';

const Navigation = () => {
  return (
    <nav className="main-nav">
      <ul>
        <li><NavLink to="/myphotos">Remember</NavLink></li>
        <li><NavLink to="/myvids">Watch</NavLink></li>
        <li><NavLink to="/audio">Listen</NavLink></li>
        <li><NavLink to="/articles">Read</NavLink></li>
        <li><NavLink to="/contribute">Write</NavLink></li>
      </ul>
    </nav>
  );
}

export default Navigation;

webpack-dev-serverを使用した反復処理の高速化

dfx 0.7.7から、dfx new スターターにwebpack dev-serverが搭載されました。

webpack開発サーバ webpack-dev-server は、webpackアセットへのインメモリアクセスを提供し、ライブリロードを使って変更を行い、すぐにブラウザに反映させることができます。

webpack-dev-server を利用するには、次のようにします。

  1. 新しいプロジェクトを作成し、プロジェクトディレクトリに変更します。

  2. 必要に応じてInternet Computerをローカルで起動し、dfx deploy コマンドを実行するなど、通常の操作でデプロイします。

  3. 以下のコマンドを実行して、webpack開発サーバーを起動します。

    npm start
  4. Webブラウザーを開き、8080ポートを使用して、アプリケーションのアセットCanisterにナビゲートします。

    例:

    http://localhost:8080
  5. 新しいターミナルウィンドウまたはタブを開き、プロジェクトのディレクトリに移動します。

  6. プロジェクトの index.js ファイルをテキストエディターで開き、内容を変更します。

    例えば、JavaScriptを使ってページに要素を追加するような場合です。

    document.body.onload = addElement;

    document.body.onload = addElement;
    
    function addElement () {
      // div要素を新規に生成
      const newDiv = document.createElement("div");
    
      // それに乗せるコンテント
      const newContent = document.createTextNode("Test live page reloading!");
    
      // 新しく作成された div 要素に テキストノードである newContent を追加
      newDiv.appendChild(newContent);
    
      // 新しく作成したnewDiv要素とnewContentをDOMに追加
      const currentDiv = document.getElementById("div1");
      document.body.insertBefore(newDiv, currentDiv);
    }
  7. index.js ファイルへの変更を保存しますが、エディタを開いたままにしておき、変更を続けます。

  8. ブラウザを更新するか、またはブラウザが自動的に更新されるのを待つと、変更内容が表示されます。

    プロジェクトのフロントエンドの作業が終わったら、Control-Cを押してwebpack開発サーバーを停止することができます。

他のフレームワークを使うと

webpack以外のバンドラーを使いたい場合もあるでしょう。バンドラーごとの説明はまだ準備できていませんが、お使いのバンドラーに精通していれば、以下の手順で作業を進めることができます。

  1. package.json から copy:types, prestart, prebuild のスクリプトを削除します。

  2. dfx deploy を実行して、Canister用のローカルバインディングを生成します。

  3. 生成されたバインディングを保存したいディレクトリにコピーします。

  4. declarations/<canister_name>/index.js を修正し、process.env.<CANISTER_NAME>_CANISTER_ID をバンドラーの環境変数に相当するパターンに置き換えます。

    • 望ましいワークフローであれば、CanisterIDをハードコードすることもできます。

  5. 宣言をコミットして、コードベースにimportします。