エントリポイント

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

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

今回のTodoアプリは処理をモジュール化し、それぞれのモジュールを別々の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から他のモジュールを読み込み利用します。 このようにすることでモジュール間は1つの<script type="module">のスコープ内に収まるため、モジュール同士で連携できます。 このHTMLから読み込むJavaScriptファイル(index.js)をJavaScriptにおけるエントリポイントとします。

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

  • index.html: もっとも最初に読み込まれるファイル、index.jsを読み込む
  • index.js: index.htmlから読み込れるファイル、JavaScript間においては最初に読み込まれる

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

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

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

任意の名前で問題ありませんが、ここではtodoappという名前のディレクトリを作成します。

HTMLファイルの用意

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

<html lang="en">
<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.jsには、スクリプトが正しく読み込まれたことを確認できるように、コンソールにログを出力する処理だけを書いておきます。

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

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

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

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

コマンドラインでtodoappディレクトリへ移動し、次のコマンドでローカルサーバを起動します。 npxコマンドを使い、この書籍用に作成された@js-primer/local-serverというローカルサーバモジュールをダウンロードと同時に実行します。

# 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)を押下する

詳細は"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ではモジュールであるindex.jsが読み込めないためコンソールログは出力されません。

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


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

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

最終的に現在のtodoappディレクトリは次のような構造になります。

todoapp
├── index.html
├── index.js
├── node_modules
├── package.json
└── src
    └── App.js

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

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

次に、このsrc/App.jsindex.jsから取り込み(import)します。 index.jsを次のように書き換え、App.jsからAppクラスを取り込みインスタンス化します。

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

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

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";

まとめ

このセクションでは次のことを行いました。

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

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