フロントエンドをカスタマイズする

シンプルな Dapp の作成、ビルド、デプロイの基本的な方法を理解しデフォルトのプロジェクトファイルとサンプルのフロントエンドにも慣れたところで、プロジェクトのフロントエンドのユーザーエクスペリエンスをカスタマイズするさまざまな方法を試してみたくなったのではないでしょうか。

このチュートリアルでは、 React フレームワークを使って、デフォルトのサンプル Dapp の新しいフロントエンドを作成し、表示されるインターフェイスをカスタマイズするための基本的な修正方法を説明します。 後のチュートリアルでは、ここで紹介したテクニックをさらに発展させていきますが、CSS 、HTML 、JavaScript 、React や他のフレームワークを使ってユーザーインターフェースを構築する方法をすでに知っている場合は、このチュートリアルを読み飛ばしても構いません。

このチュートリアルでは、React フレームワークを使用して、 Canister スマートコントラクトの Document Object Model (DOM) を管理する方法を説明します。 React には独自のカスタム DOM 構文があるため、 JSX で書かれたフロントエンドコードをコンパイルするためには、 webpack の設定を変更する必要があります。React と JSX の使い方の学習については、 React のウェブサイトGetting start を参照してください。

始める前に

チュートリアルを始める前に、以下のことを確認してください:

  • フロントエンド開発のために node.js がインストールされており、プロジェクトで npm install を使用してパッケージをインストールすることができること。 ローカルのオペレーティングシステムやパッケージマネージャに node をインストールする方法については、Node のウェブサイトを参照してください。

  • DFINITY Canister SDK パッケージを ダウンロード&インストール からダウンロードしてインストールする。

  • IDE として Visual Studio Code を使用している場合、 言語編集プラグインのインストール で説明されているように、Motoko の Visual Studio Code プラグインがインストールされていること。

  • ローカルコンピュータ上で実行されている DFINITY Canister SDK プロセスをすべて停止していること。

このチュートリアルでは、 DFINITY Canister SDK のバージョン 0.8.0 以降を使用する必要があります。

このチュートリアルは約30分で終了します。

新しいプロジェクト生成

カスタムフロントエンド Dapp 用の新しいプロジェクトディレクトリを作成する:

  1. ローカルコンピューターでターミナルシェルを開きます(まだ開いていない場合)。

  2. Internet Computer プロジェクトで使用しているフォルダがあれば、そのフォルダに変更します。

  3. 以下のコマンドを実行して、ローカルに node.js がインストールされていることを確認します:

    which node
    which npm

    もし node.js がインストールされていない場合は、次のステップに進む前にダウンロードしてインストールする必要があります。 お使いのローカル OS やパッケージマネージャーに合わせて node をインストールする方法については、 Node のウェブサイトをご覧ください。

  4. 次のコマンドを実行して、新しいプロジェクトを作成します:

    dfx new custom_greeting

    + dfx new custom_greeting+ コマンドは、新しい custom_greeting プロジェクトを作成します。

  5. 以下のコマンドを実行して、プロジェクト・ディレクトリに移動します:

    cd custom_greeting

React フレームワークのインストール

これまで React を使ったことがない場合は、フロントエンドのコードを編集する前に、Reactイントロダクション チュートリアルや React ウェブサイト を調べてみるといいでしょう。

必要なフレームワークモジュールをインストールする:

  1. 以下のコマンドを実行して、 React モジュールをインストールします:

    npm install --save react react-dom
  2. 以下のコマンドを実行して、必要な TypeScript 言語のコンパイラ・ローダをインストールします:

    npm install --save-dev typescript ts-loader
    これらのモジュールをインストールする代わりに、デフォルトの package.json ファイルを編集して、プロジェクトの依存関係を追加することができます。
    {
      "name": "custom_greeting_assets",
      "version": "0.1.0",
      "description": "Internet Computer starter application",
      "keywords": [
        "Internet Computer",
        "Motoko",
        "JavaScript",
        "Canister"
      ],
      "scripts": {
        "build": "webpack",
        "prebuild": "npm run copy:types",
        "start": "webpack serve --mode development --env development",
        "prestart": "npm run copy:types",
        "copy:types": "rsync -avr .dfx/$(echo ${DFX_NETWORK:-'**'})/canisters/** --exclude='assets/' --exclude='idl/' --exclude='*.wasm' --delete src/declarations"
      },
      "devDependencies": {
        "@dfinity/agent": "0.10.0",
        "@dfinity/candid": "0.10.0",
        "@dfinity/principal": "0.10.0",
        "assert": "2.0.0",
        "buffer": "6.0.3",
        "copy-webpack-plugin": "^9.0.1",
        "events": "3.3.0",
        "html-webpack-plugin": "5.3.1",
        "process": "0.11.10",
        "stream-browserify": "3.0.0",
        "terser-webpack-plugin": "5.1.1",
        "util": "0.12.3",
        "webpack": "5.24.4",
        "webpack-cli": "4.5.0",
        "webpack-dev-server": "^3.11.2"
      },
      "browserslist": [
        "last 2 chrome version",
        "last 2 firefox version",
        "last 2 safari version",
        "last 2 edge version"
      ],
      "dependencies": {
        "react": "^17.0.2",
        "react-dom": "^17.0.2",
        "ts-loader": "^9.2.3",
        "typescript": "^4.3.5"
      }
    }

初期設定の確認

このチュートリアルで React を使用するための変更を行う前に、プロジェクトの dfx.json 設定ファイルにあるデフォルトのフロントエンド設定を確認しましょう。

デフォルトの dfx.json 設定ファイルを確認するには以下の様にします:

  1. 設定ファイル dfx.json をテキストエディターで開きます。

  2. また、 canisters キーには、 custom_greeting_assets Canister の設定が含まれています。

    {
      "canisters": {
        ...
    
        "custom_greeting_assets": {
          "dependencies": [
            "custom_greeting"
          ],
          "frontend": {
            "entrypoint": "src/custom_greeting_assets/src/index.html"
          },
          "source": [
            "src/custom_greeting_assets/assets",
            "dist/custom_greeting_assets/"
          ],
          "type": "assets"
        }
      }
    }

    このセクションの設定を見てみましょう。

    • プロジェクトのフロントエンドアセットは、独自の Canister にコンパイルされます。ここでは、 custom_greeting_assets という名前の Canister になります。

    • アセット Canister は、プロジェクトの メイン Canister にデフォルトで依存しています。

    • frontend.entrypoint は、 Dapp のエントリーポイントとして使用するファイル(ここでは、 index.html ファイル)のパスを指定します。 たとえば、カスタムの first-page.html ファイルのように、別のエントリーポイントがある場合には、この設定を変更します。

    • source の設定では、 srcdist のディレクトリのパスを指定します。 src 設定は、プロジェクトをビルドするアセット Canister に含まれる静的アセットに使用するディレクトリを指定します。 カスケードスタイルシート (CSS ) や JavaScript のカスタムファイルがある場合は、このパスで指定されたフォルダにインクルードします。 プロジェクトをビルドすると、 dist の設定で指定したディレクトリからプロジェクトのアセットが提供されます。

    • type の設定は、 custom_greeting_assets が、 認証済みアセット Canister を使用することを指定します。この Canister には、 Internet Computer プラットフォーム 上で静的アセットをホストするために必要なものがすべて付属しています。

    このチュートリアルでは、 React の JavaScript を index.jsx ファイルに追加しますが、そのためには dfx.json ファイルのデフォルト設定を変更する必要はありません。

  3. 続けるには、 dfx.json ファイルを閉じてください。

デフォルトのフロントエンドファイルの確認

このチュートリアルでは、カスタムフロントエンドを使って、デフォルトの main.mo Canister を呼び出すことになっています。 しかし、変更を加える前に、プロジェクトのデフォルトのフロントエンドファイルに何があるかを見てみましょう。

デフォルトのデフォルトのフロントエンドファイルを確認するには以下のようにします:

  1. テキストエディタで src/custom_greeting_assets/src/index.html ファイルを開きます。

    このテンプレートファイルは、 dfx.json ファイルの frontend.entrypoint 設定で指定された Dapp のデフォルトのフロントエンドエントリーポイントとなります。

    このファイルは標準的な HTML で、 src/custom_greeting_assets/assets ディレクトリにある CSS ファイルと画像への参照を含んでいます。 デフォルトの index.html ファイルには、 name 引数の入力フィールドとクリック可能なボタンを表示するための標準的な HTML 構文も含まれています。

    これは、 デフォルトのフロントエンドを確認する で見たのと同じデフォルトのフロントエンドです。

  2. テキストエディターで、 src/custom_greeting_assets/src/index.js ファイルを開きます。

    import { custom_greeting } from "../../declarations/custom_greeting";
    
    document.getElementById("clickMeBtn").addEventListener("click", async () => {
      const name = document.getElementById("name").value.toString();
      // custom_greetingアクターを動かしてgreetメソッドを呼び出す
      const greeting = await custom_greeting.greet(name);
    
      document.getElementById("greeting").innerText = greeting;
    });
    • import ステートメントは、 ”../declarations" から custom_greeting Canister への呼び出しを可能にする Actor を指しています。

    • declarations はまだ作成されていませんが、それについてはまた改めて説明します。

  3. 続けるには index.js ファイルを閉じてください。

フロントエンドファイルの修正

これで、デフォルトの Dapps に新しいフロントエンドを作成する準備が整いました。

フロントエンドのファイルを準備するために:

  1. テキストエディターで、 webpack の設定ファイル (webpack.config.js) を開きます。

  2. フロントエンドのエントリーを変更して、デフォルトの index.htmlindex.jsx に置き換えます。

    entry: {
      //frontend.entrypoint は、このビルドの HTML ファイルを指しているので
      //拡張子を `.js` に変更する必要があります。
      index: path.join(__dirname, asset_entry).replace(/\.html$/, ".jsx"),
    },
  3. 以下の module キーを plugins セクションの上に追加します:

    module: {
      rules: [
        { test: /\.(js|ts)x?$/, loader: "ts-loader" }
      ]
    },

    この設定は、プロジェクトが React JavaScript の index.jsx ファイルに ts-loader コンパイラーを使用することを可能にします。 デフォルトの webpack.config.js ファイルにはコメントされたセクションがあり、これを修正して module キーを追加することができることに注意してください。

  4. プロジェクトのルートディレクトリに、 tsconfig.json という名前の新規ファイルを作成します。

  5. テキストエディターで tsconfig.json ファイルを開き、以下をコピーしてファイルに貼り付けます:

    {
        "compilerOptions": {
          "target": "es2018",        /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
          "lib": ["ES2018", "DOM"],  /* Specify library files to be included in the compilation. */
          "allowJs": true,           /* Allow javascript files to be compiled. */
          "jsx": "react",            /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
        },
        "include": ["src/**/*"],
    }
  6. 変更内容を保存し、 tsconfig.json ファイルを閉じて次に進みます。

  7. デフォルトの src/custom_greeting_assets/src/index.js ファイルをテキストエディターで開き、2行目から9行目までを削除します。

  8. 以下のサンプルコードをコピーして、 index.js ファイルに貼り付けてください:

    import * as React from "react";
    import { render } from "react-dom";
    import { custom_greeting } from "../../declarations/custom_greeting";
    
    const MyHello = () => {
      const [name, setName] = React.useState('');
      const [message, setMessage] = React.useState('');
    
      async function doGreet() {
        const greeting = await custom_greeting.greet(name);
        setMessage(greeting);
      }
    
      return (
        <div style={{ "fontSize": "30px" }}>
          <div style={{ "backgroundColor": "yellow" }}>
            <p>Greetings, from DFINITY!</p>
            <p>
              {" "}
              Type your message in the Name input field, then click{" "}
              <b> Get Greeting</b> to display the result.
            </p>
          </div>
          <div style={{ margin: "30px" }}>
            <input
              id="name"
              value={name}
              onChange={(ev) => setName(ev.target.value)}
            ></input>
            <button onClick={doGreet}>Get Greeting!</button>
          </div>
          <div>
            Greeting is: "
            <span style={{ color: "blue" }}>{message}</span>"
          </div>
        </div>
      );
    };
    
    render(<MyHello />, document.getElementById("app"));
  9. 以下のコマンドを実行して、修正した index.js ファイルの名前を index.jsx に変更します:

    mv src/custom_greeting_assets/src/index.js src/custom_greeting_assets/src/index.jsx
  10. デフォルトの src/custom_greeting_assets/src/index.html ファイルをテキストエディタで開き、 body の内容を <div id="app"></div> で置き換えます。

    例:

    <!doctype html>
    <html lang="en">
     <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width">
        <title>custom_greeting</title>
        <base href="/">
        <link type="text/css" rel="stylesheet" href="main.css" />
     </head>
     <body>
        <div id="app"></div>
     </body>
    </html>

ローカル Canister 実行環境の起動

custom_greeting プロジェクトをビルドする前に、ライブの Internet Computer プラットフォーム か、開発環境でローカルに実行されている Canister 実行環境に接続する必要があります。

実行環境をローカルで起動する:

  1. ローカルコンピュータで新しいターミナルウィンドウまたはタブを開きます。

  2. 必要に応じて、プロジェクトのルートディレクトリに移動します。

  3. 次のコマンドを実行して、ローカルコンピュータ上でローカル Canister 実行環境を起動します:

    dfx start --background

    ローカル Canister 実行環境の起動操作が完了したら、次のステップに進みます。

Dapp の登録、ビルド、デプロイ

ローカル Canister 実行環境に接続すると、ローカルで Dapp の登録、ビルド、デプロイを行うことができます。

Dapp をローカルにデプロイする:

  1. 必要に応じて、プロジェクトのルートディレクトリにいることを確認します。

  2. 以下のコマンドを実行して、 Dapp の登録、ビルド、デプロイを行います:

    dfx deploy

    dfx deploy コマンドの出力には、実行した操作に関する情報が表示されます。

新しいフロントエンドを確認する

ブラウザで アセット Canister の Canister 識別子を入力すると、デフォルトの Dapp の新しいフロントエンドにアクセスできるようになりました。

カスタム・フロントエンドを確認する:

  1. ターミナルの新しいタブまたはウィンドウを開き、以下を実行します。

    npm start
  2. ブラウザを開き、 http://localhost:8080 に移動します。

  3. greeting を入力するプロンプトが表示されていることを確認します。

    例:

    Sample front-end

  4. 入力フィールドの Name を表示したいテキストに置き換えて、 Get Greeting をクリックすると、結果が表示されます。

    例:

    Greeting result

フロントエンドを修正し、変更点をテストする

フロントエンドを見た後に、いくつかの変更を加えたいと思うかもしれません。

フロントエンドを変更するには:

  1. テキストエディタで index.jsx ファイルを開き,そのスタイル設定を変更します。 例えば、フォントファミリーを変更したり、入力フィールドにプレースホルダーを使用したりするには、以下のように変更します:

    import * as React from "react";
    import { render } from "react-dom";
    import { custom_greeting } from "../../declarations/custom_greeting";
    
    const MyHello = () => {
      const [name, setName] = React.useState('');
      const [message, setMessage] = React.useState('');
    
      async function doGreet() {
        const greeting = await custom_greeting.greet(name);
        setMessage(greeting);
      }
    
      return (
        <div style={{ "fontFamily": "sans-serif" }}>
          <div style={{ "fontSize": "30px" }}>
            <p>Greetings, from DFINITY!</p>
            <p>
              {" "}
              Type your message in the Name input field, then click{" "}
              <b> Get Greeting</b> to display the result.
            </p>
          </div>
          <div style={{ margin: "30px" }}>
            <input
              id="name"
              placeholder="Type text here"
              value={name}
              onChange={(ev) => setName(ev.target.value)}
            ></input>
            <button onClick={doGreet}>Get Greeting!</button>
          </div>
          <div>
            Greeting is: "
            <span style={{ color: "green" }}>{message}</span>"
          </div>
        </div>
      );
    };
    
    render(<MyHello />, document.getElementById("app"));
  2. ファイルを保存し、更新されたページをブラウザで表示してください。

    例:

    Modified styles on your entry page

  3. 新しいメッセージを入力すると、新しい greeting 表示されます。例:

    Modified greeting result

ローカル Caninster の実行環境の停止

Dapp のフロントエンドのテストが終わったら、ローカルの Canister 実行環境を停止して、バックグラウンドでの実行が続かないようにします。 ローカルネットワーク停止するには:

  1. webpack の開発サーバーが表示されているターミナルで、 Control-C を押して dev-server を中断します。

  2. ネットワーク操作を表示する端末で Control-C を押して、ローカルネットワークの処理を中断する。

  3. 以下のコマンドを実行して、ローカル Canister 実行環境を停止します:

    dfx stop