フロントエンドの概要
IC では、JavaScript agent を使うことで dapps のために Web 3.0 のフロントエンドをホストすることができます。また、dfx
が提供する asset canister を使って静的ファイルを IC にアップロードすることで、アプリケーション全体を分散型技術で実行することができます。このセクションでは、dfx new
で提供されるデフォルトのフロントエンドテンプレート、フロントエンドの設定オプション、プロジェクトのユーザーインターフェイスを構築するための他のフレームワークの使用について詳しく説明します。
ここではフロントエンド Dapps の開発の様々な段階でのサンプルコードを含むチュートリアルへのクイックリンクをご紹介します。
React dapp 構築のチュートリアル Customize the frontend
Candid を、Canister 内の機能を公開してテストするための最低限のインターフェースとして使用する。
生の HTML と JavaScript を使って、シンプルな HTML エントリーページを表示する。
React とコンパイルされた JavaScript を使って、HTML の属性や要素を直接ページに埋め込む。
React と TypeScript を使って、外部ファイルから CSS プロパティをインポートする。
デフォルトテンプレートの使用方法
チュートリアルでお気づきのように、プロジェクトにはテンプレートの index.js
と webpack.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
ファイルが作成され、使用したい特定のモジュール(react
や markdown
など)を簡単に追加できるようになっています。
テンプレートの 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
を利用するには、次のようにします。
新しいプロジェクトを作成し、プロジェクトディレクトリに変更します。
必要に応じて{IC}をローカルで起動し、
dfx deploy
コマンドを実行するなど、通常の操作でデプロイします。以下のコマンドを実行して、webpack 開発サーバーを起動します。
npm start
Web ブラウザーを開き、8080 ポートを使用して、アプリケーションのアセット Canister にナビゲートします。
例:
http://localhost:8080
新しいターミナルウィンドウまたはタブを開き、プロジェクトのディレクトリに移動します。
プロジェクトの
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);
}index.js
ファイルへの変更を保存しますが、エディタを開いたままにしておき、変更を続けます。ブラウザを更新するか、またはブラウザが自動的に更新されるのを待つと、変更内容が表示されます。
プロジェクトのフロントエンドの作業が終わったら、Control-C を押して webpack 開発サーバーを停止することができます。
他のフレームワークを使うと
webpack 以外のバンドラーを使いたい場合もあるでしょう。バンドラーごとの説明はまだ準備できていませんが、お使いのバンドラーに精通していれば、以下の手順で作業を進めることができます。
package.json
からcopy:types
,prestart
,prebuild
のスクリプトを削除します。dfx deploy
を実行して、Canister 用のローカルバインディングを生成します。生成されたバインディングを保存したいディレクトリにコピーします。
declarations/<canister_name>/index.js
を修正し、process.env.<CANISTER_NAME>_CANISTER_ID
をバンドラーの環境変数に相当するパターンに置き換えます。- 望ましいワークフローであれば、CanisterID をハードコードすることもできます。
宣言をコミットして、コードベースに import します。