エントリポイント

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

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>

そのため、HTMLから読み込むのは1つのJavaScriptファイル(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という名前になっている前提で進めていきます。

HTMLファイルの用意

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

index.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 @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では、ツールメニュー)の Web 開発サブメニューで "Web コンソール" を選択する
  • キーボードショートカットCtrl+Shift+K(macOSではCommand+Option+K)を押下する

詳細はMDNの「Webコンソールを開く」を参照してください。

[エラー例] コンソールログが表示されない

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

  • index.jsの読み込みに失敗している
  • JavaScriptモジュールに非対応のブラウザを利用している

index.jsの読み込みに失敗している

scirptタグに指定した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からモジュールとして読み込みます。

ここでのtodoappディレクトリは、次のようなファイル配置となるようにsrc/App.jsを作成を作成していきます。

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

src/App.jsにファイルを作成し、次のような内容のJavaScriptモジュールとします。 モジュールは、基本的には何かしらを外部に公開(export)します。 App.jsAppというクラスを公開するモジュールとして、今回はコンソールログを出力するだけです。

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://127.0.0.1:3000)にブラウザでアクセスし、リロードしてみましょう。 コンソールログには、次のように処理の順番どおりのログが出力されます。

App.js: loaded
App initialized

まずindex.jsからsrc/App.jsAppクラスが取り込まれています。 次にAppクラスがインスタンス化されていることがログから確認できます。

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

[エラー例] App.jsの読み込みに失敗する

ディレクトリ構造や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指定を忘れていないかをチェックしてみてください。

実行コンテキストをモジュールとして実行するには<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で確認できます。