こんにちは、katです。
前回の続編になります。
前回、express-generatorで作成したプロジェクトのサーバーサイド部分をTypeScript化する方法を紹介したのですが、
その記事ではフロントエンド部分(ブラウザ側JavaScript)のTypeScript化はしておらず、完全なTypeScript化とは呼べない状況でした。
そこで今回、フロント側にもTypeScriptを導入し、なんとか全体をTypeScript化できました。
同時に、フロント側にWebpackも導入し、ブラウザ側では1つのJavaScriptファイルのみを読み込むようにできました。
(フロント側で複数のjsファイルを用いている場合、IEブラウザではESモジュールが使用できない等、ブラウザ間で何かと差異がある。→Webpackを用いて1ファイルにしてしまえば解決)
それらについて、今回学んだ事項を紹介したく、記事を作成しました。
Githubリポジトリ(今回の作業終了時コミット)はこちら
方針
以下のような手順でTypeScript → JavaScriptへの変換をしております。
- tscでコンパイル処理(TypeScript複数ファイル → JavaScript複数ファイル)
- webpackでバンドル処理(JavaScript複数ファイル → JavaScript単一ファイル & 最適化)
ts-loaderという技術を用いて、TypeScript複数ファイル → JavaScript単一ファイル への直接変換を行う手法もあるのですが、
そうするとtscのトランスパイラ(旧バージョンのJavaScript変換等)の恩恵を受けられないことが分かったため、
一度JavaScriptファイルに変換した後にWebpack処理を行うようにしております。
手順
パッケージのインストール
yarn add -D webpack webpack-dev-server webpack-cli clean-webpack-plugin
パッケージの概要
- webpack-dev-server : 開発用のwebpack(ホットリロード機能付き)
- webpack-cli : webpack系統をコマンド操作するためのパッケージ
- clean-webpack-plugin : バンドルの際、格納先フォルダ内の不要ファイル等、ゴミを削除する
ejsの編集
scriptタグを編集し、バンドルされるJavaScriptファイルを読み込む。
今回は、bundle.jsというjsファイルを指定する。
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<script src="/javascripts/bundle.js" defer></script>
~~省略~~
</head>
~~省略~~
</html>
webpack設定ファイル(本番用)
const path = require('path')
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
module.exports = {
mode: 'production',
entry: './dist/frontend/index.js',//エントリーポイント
output: {
filename: 'bundle.js',//バンドルファイル名
path: path.resolve(__dirname, 'public', 'javascripts')//バンドル先ディレクトリ
},
resolve: {
extensions: ['.js']
//typescriptでは、import時に拡張子書かない。しかしそれだけではwebpackエラー出る。そのための拡張子の補完。
},
plugins: [
new CleanWebpackPlugin()//clean-webpack-plugin(バンドル先フォルダ内のゴミを削除)
]
}
webpack設定ファイル(開発用)
今回は、バンドル結果を実際にファイルに書き出す(ディスクに出力する)ようにしている。
(webpack-dev-serverでは、デフォルトだとバンドル結果はメモリ上に作成するだけ。バンドル結果を読み込むにはlocalhost:8080/〜〜〜/bundle.jsにアクセスする)。
const path = require('path')
module.exports = {
mode: 'development',
entry: './dist/frontend/index.js',//エントリーポイント
output: {
filename: 'bundle.js',//バンドルするファイル名
path: path.resolve(__dirname, 'public', 'javascripts'),//バンドル先ディレクトリ
publicPath: path.resolve('public', 'javascripts')//webpack-dev-serverで実行時、bundle.jsの格納場所(デフォルトだとaファイルには現れない)
},
resolve: {
extensions: ['.js']
//typescriptでは、import時に拡張子書かない。しかしそれだけではwebpackエラー出る。そのための拡張子の補完。
},
devServer: {
devMiddleware: {
writeToDisk: true//バンドルのファイルをファイル出力するための設定
}
}
}
今回、実際にファイルをディスク出力する理由として、バンドルされたjsをejs(テンプレートエンジン)で読み込ませたいからである。
ejsを使うのであれば、別ポートのlocalhost:3000を立ち上げる必要があり、その関係で開発用と本番用でscriptタグを書き換えないといけなくなってしまう、という理由があり今回はファイルを実際にディスク出力した。
~~省略~~
<head>
~~省略~~
<script src="/javascripts/bundle.js" defer></script>
~~省略~~
</head>
~~省略~~
~~省略~~
<head>
~~省略~~
<script src="localhost:8080/javascripts/bundle.js" defer></script>
~~省略~~
</head>
~~省略~~
実行方法
package.jsonを以下のように修正。
{
~~省略~~
"scripts": {
"dev": "tsc -w",
"dev:back": "ts-node-dev ./src/backend/bin/www",
"dev:front": "webpack-dev-server --config webpack.dev.js",
"build": "yarn run build:front && yarn run build:back",
"build:front": "tsc && webpack --config webpack.prod.js",
"build:back": "tsc",
"start": "yarn run build && node ./dist/backend/bin/www"
},
~~省略~~
}
開発時 実行
ターミナルを3つ立ち上げる(A,B,Cとする)。
- ターミナルAで yarn run dev
- ターミナルBで yarn run dev:back
- ターミナルCで yarn run dev:front
と実行することで、フロントエンド・バックエンドともにホットリロードで開発できる。
(ただ、ターミナル3つ立ち上げるやり方はあまりよろしくなさそう。。ここはもう少し勉強の必要あり。)
本番時 実行
ターミナルで yarn run start
おわりに
今回は以上です! ご覧いただきありがとうございました。
まだまだ改善の余地ありですが、ひとまずはexpess-generatorプロジェクトのTypeScript化は完了です。
特に、Webpackの部分は苦手意識を持っており正直腰が重かったのですが、今回0から導入したことで理解が深まりました。
ちなみに、React、Nextは既に勉強済なのですが、「実際にブラウザが読み込むjsファイルはどうなっているのだろう」というところの理解を後回しにしていました。
今回得た知識をもとにReact、Nextを復習し、点と点が繋がればと考えています。
今後とも宜しくお願いいたします。
参考記事
Udemy TypeScript講座