見出し画像

小さな渋谷の会社でゼロから作るWebアプリケーション (6) インデックスページ表示

株式会社エー・アンド・ディのCTOの山口です。

前回で一通りフロントエンド開発の環境が整いました。

今回は、インデックスページが表示できるところまでやってみようと思います。

インデックスページを生成する方法

インデックスページ、いわゆるindex.htmlが表示できないと、フロントエンドは何も始まりません。

しかし、今回はSingle Page Application(SPA)、つまり基本的にはJavaScript(TypeScript)による画面描画を行います。

SPAで画面を作る場合、画面はJavaScriptに支配されます。HTML・CSSも常にJavaScriptと一緒に構築・パッケージングされることになります。
そのため、できればHTMLやCSSもJavaScriptの世界で扱いたいわけです。

Webpackを使うと、そういったHTML・CSS・JavaScriptなど多彩なフロントエンド向けファイルを1つに束ねて(バンドルして)扱えます。

今回は、そんなWebpackを活用して、index.htmlもWebpackで生成するようにします。

Webpackプラグインを追加する

WebpackでHTMLを生成するには、HtmlWebpackPluginという仕組みを使います。

読んで字のごとく、WebpackのHTMLのプラグインです。

Webpackプラグインとは、Webpackのビルド機能を拡張するための仕組みです。
こちらのリストにあるように、多数のプラグインが用意されており、必要に応じて機能を追加することが可能です。

色々あってすごいですが、むやみに追加していくとわけがわからなくなっていくので、やはり必要に応じて1つずつ追加していくのがおすすめです。
(プラグインは、Webpackとは別に、別の人が、別のタイミングで開発しているので、特に依存ライブラリやバージョン間の差異でトラブルが起きがちです……)

さて、早速HtmlWebPackPluginを追加してみましょう。

Webpackプラグインは、通常はnpmのパッケージとして提供されています。
package.jsonに依存ライブラリとして追加することで、プラグインが使用可能になります。

{
  // 前略
  "devDependencies": {
      // 中略

      "webpack": "4.41.5",
      "@types/webpack": "4.41.2",
      // 以下を追加
      "html-webpack-plugin": "3.2.0",
      "@types/html-webpack-plugin": "3.2.1",

      // 後略
  }
}

そしていつものinitializeProjectsを実行します。

インストールが完了したら、webpack.conf.tsにimportします。

/**
* Webpackビルド設定.
*/

import { Configuration, Module, Resolve, RuleSetRule } from "webpack";
import HtmlWebpackPlugin from "html-webpack-plugin"; // これを追加

そして、同じくwebpack.conf.tsのconfigにpluginsの設定を追加します。

// 前略
const config: Configuration = {
   // 中略

   // 以下を追加
   // プラグイン設定
   plugins: [
       // HTMLファイルを出力する。
       new HtmlWebpackPlugin({
           filename: "index.html",
           template: "./src/html/index.html"
       })
   ]
};

pluginsに追加したHtmlWebpackPluginの動作によって、テンプレートのsrc/html/index.htmlを元にindex.htmlが生成されます。

テンプレートのsrc/html/index.htmlは……これから作ります(汗)。

テンプレートのHTMLファイルを追加する

ではHtmlWebpackPluginで使用するHTMLテンプレートを作成しましょう。
結局HTMLかよ、という感じですが、HTMLをHTMLとして書けるのは自然で結局分かりやすいです。
実は、Webpackの機能で、書いた通りのHTMLが出るだけではありません。乞うご期待です。

テンプレートのindex.htmlはこんな感じにしました。これをsrc/html/index.htmlに配置します。

<!DOCTYPE html>
<html>

<head>
   <meta charset="UTF-8">
   <title>MORISOBA</title>
</head>

<body>
   <h1>MORISOBA!</h1>
</body>

</html>


タイトルとか内容は適当にいじってよいと思います。
このファイルが作成できたら、ビルドができるようになっています。

> Executing task in folder morisoba: .\gradlew.bat :frontend:build <

To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon: https://docs.gradle.org/5.5.1/userguide/gradle_daemon.html.
Daemon will be stopped at the end of the build stopping after processing
Configuration on demand is an incubating feature.

PS C:\Users\ikemen\morisoba\master> .\gradlew.bat :frontend:gulp_build
To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon: https://docs.gradle.org/5.5.1/userguide/gradle_daemon.html.
Daemon will be stopped at the end of the build stopping after processing
Configuration on demand is an incubating feature.

> Task :frontend:gulp_build
[19:06:04] Requiring external module ts-node/register
[19:06:07] Using gulpfile ~\morisoba\frontend\gulpfile.ts
[19:06:07] Starting 'default'...
[19:06:08] Finished 'default' after 1.03 s
Hash: ef456ed241fa0a69277dING [10s]
Version: webpack 4.41.5t
Time: 1657ms
Built at: 2020-01-21 19:06:10
    Asset       Size  Chunks             Chunk Names
index.html  196 bytes          [emitted]  
  main.js  969 bytes       0  [emitted]  main
Entrypoint main = main.js
[0] ./src/ts/index.ts 112 bytes {0} [built]
Child html-webpack-plugin for "index.html":
        Asset     Size  Chunks  Chunk Names
   index.html  533 KiB       0  
   Entrypoint undefined = index.html
   [0] ./node_modules/html-webpack-plugin/lib/loader.js!./src/html/index.html 352 bytes {0} [built]
   [1] ./node_modules/lodash/lodash.js 528 KiB {0} [built]
   [2] (webpack)/buildin/global.js 472 bytes {0} [built]
   [3] (webpack)/buildin/module.js 497 bytes {0} [built]

> Task :frontend:yarn_install
yarn install v1.21.1
[1/4] Resolving packages...
success Already up-to-date.
Done in 0.38s.

BUILD SUCCESSFUL in 19s
4 actionable tasks: 2 executed, 2 up-to-date

こんな感じになっていれば成功です。きっとこんなHTMLファイルが、distディレクトリに出力されていると思います。

<!DOCTYPE html>
<html>

<head>
   <meta charset="UTF-8">
   <title>MORISOBA</title>
</head>

<body>
   <h1>MORISOBA!</h1>
<script type="text/javascript" src="main.js"></script></body>

</html>

いっけん書いた通りのHTMLですが、よく見るとscriptタグが追加されています。

このscriptタグが重要で、WebpackでビルドされたJavaScriptが自動的に記載されるようになっています。

ファイル名などもWebpackのビルド設定に合わせて随時修正されます。

このファイル名の自動修正は、地味に大事な機能です。
Webpackを使用していると、自動分割されたり、バージョン番号が付与されたりする場合があります。
(ダウンロードするファイルのサイズを最小化したり、ブラウザのキャッシュを有効利用するため)
その時に、手作業でURLを書き換えていくのはほとんど不可能です。

WebpackによるHTMLの自動生成を利用していれば、必要なスクリプトの読み込みは自動で設定されるようになり、考える必要が無くなります。
(トラブルが生じた場合はやはり調べないといけませんけど……)

テスト用のWebサーバーを導入する

さて、HTMLファイルが出来たので、当然ブラウザで開けば表示できるのですが、まだHTTP通信を介した表示が行えていません。
つまり、Webサーバーからの配信が確認できていません。

Webpackには、webpack-dev-serverという開発用のWebサーバーを起動する機能があります。

webpack-dev-serverは、単なるWebサーバーではなくて、Webpackの設定に沿った自動ビルド・ブラウザ更新なども行ってくれます。

こちらを利用して、Webサーバーによるページ表示までやってみようと思います。

まず、いつものようにpackage.jsonへの追加です。

{
  // 前略
  "devDependencies": {
      // 中略

      "webpack": "4.41.5",
      "@types/webpack": "4.41.2",
      "html-webpack-plugin": "3.2.0",
      "@types/html-webpack-plugin": "3.2.1",

      // 以下を追加
      "webpack-dev-server": "3.10.1",
       "@types/webpack-dev-server": "3.9.0",

      // 後略
  }
}

そしてもちろんinitializeProjectsを実行します。

webpack-dev-server設定

次に、Webサーバーが起動できるよう設定を行います。
今回は、Gulp設定ファイル(gulpfile.ts)にタスクを追加して、Webサーバーを起動できるようにします。
(実はwebpack.conf.tsに設定を追加する方法もあり、そちらの方が普通だと思いますが、Windowsではゾンビプロセスが残る問題があって現在はgulpにしています……)

/**
* Gulpタスク定義.
*/

// webpackを使用するためimport
import webpack, { Configuration } from "webpack";

// これを追加
import WebpackDevServer from "webpack-dev-server";

// 中略

/**
* 開発サーバーを起動する.
*
* @return 処理結果
*/
export async function serve(): Promise<void> {
   // 開発サーバー設定
   const options: WebpackDevServer.Configuration = {
       // 起動ホスト・ポート番号設定
       host: "localhost",
       port: 3000,

       // ログ等の表示設定
       stats,
   };

   // Webpack設定を読み込む。
   const config = await importWebpackConfiguration();

   // 開発サーバー向けビルド設定に変更する。
   config.mode = "development";

   // 開発サーバー起動
   const compiler = webpack(config);
   const server = new WebpackDevServer(compiler, options);
   server.listen(options.port || 3000);
}

色々大変ですが、頑張って読んでみてください……。

開発サーバー設定のところで、ホスト・ポート番号等の設定を行っています。
こちらと importWebpackConfiguration で取り込んだWebpack設定(webpack.conf.tsの内容)を元にWebpackのインスタンス(compiler)を作り、それをWebpackDevServerが使用するようにしています。

上記の設定で、開発サーバーがwebpackの設定通りにビルドを行うようになります。

webpack-dev-server起動

ここまで出来たら、開発サーバーの起動もGradleタスクに取り込んで、Gradleから起動してみましょう。

frontendプロジェクトのbuild.gradeに、下記のタスクを追加します。

// 開発サーバー起動
task serve(dependsOn: [installTools, gulp_serve]) {
}

そして起動します。

C:\Users\ikemen\morisoba\master>.\gradlew.bat :frontend:serve

To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon: https://docs.gradle.org/5.5.1/userguide/gradle_daemon.html.
Daemon will be stopped at the end of the build stopping after processing
Configuration on demand is an incubating feature.

> Task :frontend:gulp_serve
[18:24:35] Requiring external module ts-node/register
[18:24:37] Using gulpfile ~\github\morisoba\frontend\gulpfile.ts
[18:24:37] Starting 'serve'...
[18:24:38] Finished 'serve' after 579 ms
i ?ス「wds?ス」: Project is running at http://localhost:3000/
i ?ス「wds?ス」: webpack output is served from undefined     
i ?ス「wds?ス」: Content not from webpack is served from C:\Users\ikemen\morisoba\frontend
i ?ス「wdm?ス」: Hash: 53e906a84d5cb41cc348
Version: webpack 4.41.5
Time: 1746ms
Built at: 2020-01-29 18:24:39
    Asset       Size  Chunks             Chunk Names
index.html  196 bytes          [emitted]
  main.js    360 KiB    main  [emitted]  main
Entrypoint main = main.js
[0] multi (webpack)-dev-server/client?http://localhost:3000 ./src/ts/index.ts 40 bytes {main} [built]
[./node_modules/ansi-html/index.js] 4.16 KiB {main} [built]
[./node_modules/ansi-regex/index.js] 135 bytes {main} [built]
[./node_modules/html-entities/index.js] 231 bytes {main} [built]
[./node_modules/loglevel/lib/loglevel.js] 8.36 KiB {main} [built]
[./node_modules/strip-ansi/index.js] 161 bytes {main} [built]
[./node_modules/webpack-dev-server/client/index.js?http://localhost:3000] (webpack)-dev-server/client?http://localhost:3000 4.29 KiB {main} [built]
[./node_modules/webpack-dev-server/client/overlay.js] (webpack)-dev-server/client/overlay.js 3.51 KiB {main} [built]
[./node_modules/webpack-dev-server/client/socket.js] (webpack)-dev-server/client/socket.js 1.53 KiB {main} [built]
[./node_modules/webpack-dev-server/client/utils/createSocketUrl.js] (webpack)-dev-server/client/utils/createSocketUrl.js 2.91 KiB {main} [built]
[./node_modules/webpack-dev-server/client/utils/log.js] (webpack)-dev-server/client/utils/log.js 964 bytes {main} [built]
[./node_modules/webpack-dev-server/client/utils/reloadApp.js] (webpack)-dev-server/client/utils/reloadApp.js 1.59 KiB {main} [built]
[./node_modules/webpack-dev-server/client/utils/sendMessage.js] (webpack)-dev-server/client/utils/sendMessage.js 402 bytes {main} [built]
[./node_modules/webpack/hot sync ^\.\/log$] (webpack)/hot sync nonrecursive ^\.\/log$ 170 bytes {main} [built]
[./src/ts/index.ts] 112 bytes {main} [built]
   + 18 hidden modules
Child html-webpack-plugin for "index.html":
        Asset     Size  Chunks  Chunk Names
   index.html  552 KiB       0
   Entrypoint undefined = index.html
   [./node_modules/html-webpack-plugin/lib/loader.js!./src/html/index.html] 352 bytes {0} [built]
   [./node_modules/lodash/lodash.js] 528 KiB {0} [built]
   [./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 472 bytes {0} [built]
   [./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 497 bytes {0} [built]
i ?ス「wdm?ス」: Compiled successfully.
<==-----------> 16% EXECUTING [4m 25s]
> :frontend:gulp_serve

とりあえず何かできたような感じになります。この状態で、(タスクは終了せずに) http://localhost:3000/ にアクセスしてみましょう。

画像1

やった! ようやくWebページが画面に出ました!!

なお、開発サーバーの起動はこれからたくさんやるので、VSCodeのタスクとして登録しておくと良いと思います。

{
   // See https://go.microsoft.com/fwlink/?LinkId=733558
   // for the documentation about the tasks.json format
   "version": "2.0.0",
   "tasks": [
       // 中略

       {
           "label": "serveFrontend",
           "type": "shell",
           "command": ".\\gradlew.bat :frontend:serve",
           "options": {
               "cwd": ".\\master"
           },
           "problemMatcher": []
       }
   ]
}

まとめ

ようやく、自分で書いたWebページが画面に出るところまで来ました……。

次は、いよいよVue.jsの導入を行っていきたいと思います。しばらくフロントエンドが続きそうですね。

免責事項・ライセンス

画像2

 この 作品 は クリエイティブ・コモンズ 表示 - 改変禁止 4.0 国際 ライセンスの下に提供されています。

 弊社の当コンテンツに掲載されている情報の正確性・安全性などについての保証はなく、弊社は何らの責任を負うものではありません。
 弊社の当コンテンツに掲載された内容によって生じた損害等の一切の責任を負いかねますので、ご了承ください。

Copyright © 2020 ANDCORP.

この記事が気に入ったらサポートをしてみませんか?