エントリーポイント

エントリーポイントとは、アプリケーションの中で一番最初に呼び出される部分のことです。

Ajax通信:エントリーポイント」のユースケースでは、エントリーポイントはHTML(index.html)のみでした。 まずHTMLが読み込まれ、次にHTMLの中に書かれているscript要素で指定したJavaScriptファイルが読み込まれます。

今回のTodoアプリはJavaScriptの処理をモジュール化し、それぞれのモジュールを別々のJavaScriptファイルとして作成していきます。 JavaScriptモジュールはHTMLから<script type="module">で読み込むことができますが、script要素ごとに別々のモジュールスコープを持ちます。 モジュールスコープとは、モジュールのトップレベルに自動的に作成されるスコープで、グローバルスコープの下に作られます。 JavaScriptモジュールを別々のscript要素で読み込むと、モジュール同士でスコープが異なるため、モジュール同士で連携できません。

次のコードは、それぞれの<script type="module">同士のスコープが異なるため、別のscript要素で定義した変数にアクセスできないことを示しています。 これはJavaScriptモジュールをファイルにしてsrc属性で読み込んだ場合も同様です。

  <script type="module">
    export const scopeA = "A";
  </script>
  <script type="module">
    // 異なるmoduleスコープの変数には直接アクセスできない
    console.log(scopeA); // => ReferenceError: scopeA is not defined
  </script>

このようにモジュールを別々のscript要素で扱うとモジュール同士は連携できません。 そのため、HTMLではscript要素でindex.jsのみを読み込み、このindex.jsからimport文で他のモジュールを読み込みます。 import文を使うことで、モジュール間は1つの<script type="module">のスコープ内に収まるため、モジュール同士で連携できます。 このHTMLから読み込むJavaScriptファイル(index.js)をJavaScriptにおけるエントリーポイントとします。

つまり、今回作成するTodoアプリではエントリーポイントとしてHTMLとJavaScriptの2つを用意します。

  • index.html: 最初に読み込まれるファイル、index.jsを読み込む
  • index.js: index.htmlから読み込まれるファイル、JavaScriptでは最初に読み込まれる

このセクションでは、この2つのエントリーポイントを作成して読み込むところまでを確認します。

プロジェクトディレクトリを作成

今回作成するアプリには、HTMLやJavaScriptなど複数のファイルが必要となります。 そのため、まずそれらのファイルを置くためのディレクトリを作成します。

ここではtodoappという名前で新しいディレクトリを作成します。 ここからは作成したtodoappディレクトリ以下で作業していきます。

またこのプロジェクトで作成するファイルは、必ず文字コード(エンコーディング)をUTF-8、改行コードをLFにしてファイルを保存します。

HTMLファイルの用意

エントリーポイントとして、まずは最低限の要素だけを配置したHTMLファイルを作成しましょう。 エントリーポイントとなるHTMLとしてindex.htmltodoappディレクトリに作成し、次のような内容にします。 body要素の一番下でscript要素を使って読み込んでいるindex.jsが、今回のアプリケーションの処理を記述するJavaScriptファイルです。

index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>Todo App</title>
  </head>
  <body>
    <h1>Todo App</h1>
    <script type="module" src="index.js"></script>
  </body>
</html>

次にindex.jstodoappディレクトリに作成し、次のような内容にします。 index.jsにはスクリプトが正しく読み込まれたことを確認できるように、コンソールにログを出力する処理だけを書いておきます。

index.js

console.log("index.js: loaded");

ここまでのtodoappディレクトリのファイル配置は次のようになっています。

todoapp
├── index.html
└── index.js

次はこのindex.htmlをブラウザで開いて、コンソールにログが出力されることを確認していきます。

ローカルサーバーでHTMLを確認する

ウェブブラウザでindex.htmlを開く前に、開発用のローカルサーバーを準備します。 ローカルサーバーを立ち上げずに直接HTMLファイルを開くこともできますが、その場合file:///からはじまるURLになります。 fileスキーマではSame Origin Policyにより、JavaScriptモジュールが正しく動作しません。 そのため、本章ではローカルサーバーを立ち上げた上で、httpからはじまるURLでアクセスすることを前提としています。

コマンドラインでtodoappディレクトリへ移動し、次のコマンドでローカルサーバーを起動します。 npxコマンドを使って、この書籍用に作成された@js-primer/local-serverというローカルサーバーモジュールをダウンロードと同時に実行します。 まだnpxコマンドが用意できていなければ、先に「アプリケーション開発の準備」の章を参照してください。

# todoapp/ディレクトリに移動する
$ cd todoapp/
# todoapp/をルートにしたローカルサーバーを起動する
$ npx --yes @js-primer/local-server

todoappのローカルサーバーを起動しました。
次のURLをブラウザで開いてください。

  URL: http://localhost:3000

起動したローカルサーバーのURL(http://localhost:3000)へブラウザでアクセスしてみましょう。 ブラウザにはindex.htmlの内容が表示され、開発者ツールのコンソールにindex.js: loadedというログが出力されていることが確認できます。

Webコンソールにログが表示されている

開発者ツールでのコンソールログの確認方法

Console APIで出力したログを確認するには、ウェブブラウザの開発者ツールを開く必要があります。 ほとんどのブラウザに開発者ツールが同梱されていますが、本章ではFirefoxを使って確認します。 開発者ツールのコンソールタブを開くとConsole APIで出力したログを確認できます。

Firefoxの開発者ツールは次のいずれかの方法で開きます。

  • Firefox メニュー(メニューバーがある場合や macOS では、ツールメニュー)の "ブラウザーツール"のサブメニューから "ウェブ開発ツール" を選択する
  • キーボードショートカットCtrl+Shift+K(macOSではCommand+Option+K)を押下する

詳細は「ブラウザーの開発者ツールとは?」を参照してください。

コンソールログが表示されない

HTMLは表示されるがコンソールログにindex.js: loadedが表示されない場合は、次のような問題に該当してないかを確認してください。

[エラー例] index.jsの読み込みに失敗している

script要素のsrc属性に指定したindex.jsのパスにファイルが存在しているかを確認してください。 <script type="module" src="index.js">とした場合はindex.htmlindex.jsは同じディレクトリに配置する必要があります。

また、CORS policy Invalidのようなエラーがコンソールに表示されている場合は、Same Origin Policyによりindex.jsの読み込みが失敗しています。 先ほども紹介したように、file:からはじまるページ上からはJavaScriptモジュールは正しく動作しません。 そのため、ローカルサーバーを起動し、ローカルサーバー(http:からはじまるURL)にアクセスしていることを確認してください。

[エラー例] JavaScriptモジュールに非対応のブラウザを利用している

JavaScriptモジュールはまだ新しい機能であるため、バージョンが60以上のFirefoxが必要です。 バージョンが60未満のFirefoxでは、JavaScriptモジュールであるindex.jsが読み込めないためコンソールログは出力されません。

今回のTodoアプリでは、ネイティブでJavaScriptモジュールに対応しているブラウザが必要です。 Can I UseにネイティブでJavaScriptモジュールに対応しているブラウザがまとめられています。 非対応のブラウザでもBundlerと呼ばれるツールを使うことで対応できますが、本章では省略します。


モジュールのエントリーポイントの作成

最後にエントリーポイントとなるindex.jsから別のJavaScriptファイルをモジュールとして読み込んでみましょう。 このアプリではJavaScriptモジュールが複数登場するためsrc/というディレクトリを作り、src/の下にJavaScriptモジュールを書くことにします。 今回はsrc/App.jsというファイルを作成し、これをindex.jsからモジュールとして読み込みます。

次のようなファイル配置となるようにsrc/App.jsを作成します。

todoapp
├── index.html
├── index.js
└── src
    └── App.js

src/App.jsファイルを作成し、次のような内容のJavaScriptモジュールとします。 App.jsAppというクラスを名前つきエクスポートしているモジュールです。 また、Appクラスのコンストラクタにはコンソールログを出力するコードを確認用に書いておきます。

src/App.js

console.log("App.js: loaded");
export class App {
    constructor() {
        console.log("App initialized");
    }
}

次に、このsrc/App.jsindex.jsから利用するためにimportします。 index.jsを次のように書き換え、App.jsからAppクラスをインポートしてインスタンス化します。

index.js

import { App } from "./src/App.js";
const app = new App();

再度ローカルサーバーのURL(http://localhost:3000)にブラウザでアクセスし、リロードしてみましょう。 コンソールログには、次のように処理の順番どおりのログが出力されます。

App.js: loaded
App initialized

まずindex.jsからsrc/App.jsが名前つきエクスポートしているAppクラスを名前つきインポートしています。 次にAppクラスがインスタンス化されていることがログから確認できます。

これでHTMLとJavaScriptそれぞれのエントリーポイントの作成と動作を確認できました。

App.jsの読み込みに失敗する

ここまでのJavaScriptモジュールの読み込みでエラーが発生して動かない場合には、次のことを確認します。

ディレクトリ構造やimport文で指定したファイルパスが異なると、ファイルを読み込むことができずにエラーとなってしまいます。 この場合は開発者ツールを開き、コンソールにエラーが出ていないかを確認してみてください。

import文を使ったJavaScriptのモジュール読み込み時に起きる典型的なエラーと対処を次にまとめています。

[エラー例] SyntaxError: import declarations may only appear at top level of a module

import宣言はモジュールのトップレベルでしか利用できません」というエラーが出ています。 このエラーが出ているということは、import文を使える条件を満たしていないということです。 つまり、import文がトップレベルではないところに書かれている、またはモジュールではない実行コンテキストで実行されているということです。

関数の中などにimport宣言していると、import宣言がトップレベルではないためエラーが発生します。 この場合はimport文をトップレベル(プログラムの直下)に移動させてみてください。

モジュールではない実行コンテキストで実行されているというのは、裏を返せば実行コンテキストがScriptとなっているということです。 JavaScriptには実行コンテキストとしてScriptとModuleがあります。 import文は実行コンテキストがModuleでないと利用できません。 そのため、script要素のtype属性にmodule指定を忘れていないかをチェックしてみてください。

実行コンテキストをモジュールとして実行するには<script type="module" src="index.js">のようにtype=moduleを指定する必要があります (index.jsからimport文で読み込んだApp.jsは実行コンテキストを引き継ぐため、モジュールの実行コンテキストで処理されます)。

[エラー例] モジュールのソース “http://localhost:3000/src/App” の読み込みに失敗しました。

App.jsを読み込めないというエラーが出ています。 エラーメッセージをよく見るとAppとなっていてApp.jsではありません。

import文では、読み込むファイルの拡張子を省略しません。 そのため、Appのように拡張子(.js)を省略して書いている場合はこのエラーが発生します。

// エラーとなる例
import { App } from "./src/App";

正しくは次のように拡張子まで含めたパスを記述します。 また指定したパス(./src/App.js)にファイルが存在するかを確認してください。

// 正しい例
import { App } from "./src/App.js";

まとめ

このセクションでは、エントリーポイントとなるHTMLを作成し、JavaScriptモジュールのエントリーポイントとなるJavaScriptファイルを読み込むところまでを実装しました。

このセクションのチェックリスト

  • todoappという名前のプロジェクトディレクトリを作成した
  • エントリーポイントとなるindex.htmlを作成した
  • JavaScriptのエントリーポイントとなるindex.jsを作成しindex.htmlから読み込んだ
  • ローカルサーバーを使ってindex.htmlを表示した
  • src/App.jsを作成し、index.jsからimport文で読み込めるのを確認した

ここまでのTodoアプリは次のURLで確認できます。