Gobble up pudding

プログラミングの記事がメインのブログです。

MENU

Spring BootのThymeleafとTypeScriptを組み合わせてみたかったのでやってみた

スポンサードリンク

Web開発では最近はReact, Vue, Angularを使ってSPAが主流ですが、
とはいっても、レガシーなjQuery UIとBootstrapと組み合わせてサーバ側でレンダリングしたいこともままありますよね。 そんなわけで、僕の場合、特にjQuery UIを使いたい。
というのが一番にあって、かつTypeScriptを使いたい。
しかしながらフロントエンドフレームワーク使うまでもない。
といったところでWebpackと組み合わせて使ってみよう!ということで作りました。 サーバサイドはSpring Boot2です。別にこれじゃなくてもよいのだけれど、
最近一番慣れているフレームワークがコレなのです。
最近PHP未経験なので使いたい病にかかってるのでそっちでやればよかったかもしれない。

おおまかなプロジェクト構成はこんな感じです。 Gulpを使ったほうが実はシンプルになるのかとも考えましたが、 最近Gulp使わないっすよね。Angularでは使われてるっぽいですが。 直近までAngularを使っていたせいかAngularチックな構成になってしまい。
Angularでいいんじゃ…状態

下記をすべて置いたものをGitHubにあげています。

大まかなプロジェクト構成

src/
  main/
     java/
     client/
        ts/
           commons/
           controllers/
           models/
           services/
           pages/index.js (エントリーポイント2つ目)
           index.js (エントリーポイント1つ目)
     resources/
        public/
        static/
        resoruces/
          template/
             pages/index.html (エントリーポイント2つ目を読み込む)
             index.html(エントリーポイント1つ目を読み込む)

webpack.config.jsですが、開発用と本番用を分けたかったので、分けてます。
webpackに詳しくない人はここをやっておくとよいと思います。
Getting Started | webpack
ただ、後で少し解説しています。

webpack.common.js

const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
    entry: {
        'index': path.resolve(__dirname, 'ts/index.ts'),
        'pages/page2': path.resolve(__dirname, 'ts/pages/page2.ts'),
        // ページが増えたらここに追記
    },
    output: {
        path: path.resolve(__dirname, '../resources/static'),
        filename: '[name].js',
        publicPath: '/' // webpack-dev-server等が使うディレクトリ この場合、../resources/staticが/となる
    },
    module: {
        rules: [
            // ts -> ES2015 -> babel -> ES5
            {
                test: /\.ts$/,
                exclude: /node_modules/,
                loader: ['babel-loader'],
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: ["source-map-loader"],
                enforce: "pre",
            },
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader',
                ],
            },
            {
                test: /\.(png|svg|jpg|gif)$/,
                use: [{
                    loader: 'file-loader',
                    options: {
                        name: '[name].[ext]',
                        outputPath: 'assets/images/',
                    },
                }, ],
            },
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/,
                use: [
                    'file-loader'
                ]
            }
        ]
    },
    resolve: {
        extensions: [
            '.ts', '.js'
        ],
    },
    plugins: [
        new CleanWebpackPlugin('static', {
            root: path.resolve(__dirname, '../resources/static'),
            verbose: true,
            dry: false,
        }),
    ],
}

わかる人にはここだけでふーんだと思いますが、これだけだと意味不明な人も多いはずなので説明します。

説明

代表的な依存モジュール

  • date-fns
  • jQuery
  • jQuery UI
  • popper.js
  • jQuery cookie
  • Bootstrap

開発支援系

  • Babel
  • TypeScript
  • clean-webpack-plugin
  • css-loader
  • style-loader
  • file-loader
  • source-map-loader
  • url-loader
  • webpack
  • webpack-bundle-analyzer
  • webpack-merge

本来は必要がないもの

  • html-loader
  • html-webpack-plugin
  • webpack-dev-server

なおBabelを入れているのはIE対応等でpolyfillするため
polyfillしないとIEでPromiseやES6のArray.prototype系のメソッドが使えない
momentでなくてdate-fnsにしているのはバンドルサイズの肥大化を避けるためである

webpack-dev-serverはSpring BootのようなWeb側がなくても
独立して開発できるようにしようとしたものだが、
SPAを前提としていないこともあり、画像/CSSのパス等の調整に難があり、
それを妥協してもいいなら一応使えるようにはなっている。

開発方法

従来型のMPAで作成する。 エントリーポイントはSpring Bootのディレクトリ構成と対にし、index.tsを作成する
ここで必要なjQuery, Bootstrap等を読み込み、controllerを読み込む。
その際、webpack.common.jsにentryを追加する

なお、importするモジュールは次のように分けられたディレクトリのいずれかに配置する

- commons
  共通系のclass, functionを置く
- controllers
  Spring Bootで例えるならController相当のclassを置く(.ts)
- models
  Spring Bootで例えるならのEntity相当のclassを置く(.ts)
- services
  APIアクセスを記述する  
  $.Deferredを返すようにする(ほぼPromiseベース)

なお、index.js自体にはonclick, $(document).ready()相当の処理しか書かず、
あとはcontrollerに任せる ※AngularとかMVVM系のライブラリの影響を当時受けてこんなめんどくさい分け方にしてたようです。

その他

npm run build-dev, npm run watchしたときにeclipse側で変更が読み取れないので、
面倒だが、都度static配下をrefreshする → ※eclipseの Preferences > General > Workspace > Reflesh using native hooks or polling をすれば即時反映されそうです。

TypeScriptについて

tsconfig.jsonについて
targetはトランスパイルした結果のバージョンのこと。
ECMAScriptのバージョンでES2015(ES6)にしている。
ES5にまで一気に落とせるのだが、これはなぜかというと、あとでBabelでpolyfillするためである。 moduleはコード生成モジュールのことでimportの挙動に影響を与える。
ひとまずES2015とする。

その他についてはここを見るとよいかもしれません。

Webpack関連

基礎知識編

Webpackはビルドツールであるが、gulpと違い、コードでタスクを書くようなことはできない。 基本はentryでエントリーポイントを決め、
outputで出力先を決める MPAの構成にしたい場合は、gulpでないとできなそうだが、webpackで可能である。 entryを複数にしてoutputfilenameにて[name].jsとすると
entrykeyの値(=PATH)がそのまま出力先になる(ディレクトリ階層のあるPATHであればそれもそのまま作成される)
なお、outputのPublicPathはwebpack-dev-serverなどを使ったときに、 サーバの公開先のrootのパスになる。

__dirnameについて
これはnodeの変数で、現在コマンドを動かしているディレクトリになる。
絶対パスで指定しないとダメな場合に活用できる。

babel-loaderについて

JavaScriptをトランスパイルするもの。 babel-loaderの7からTypeScriptもトランスパイル可能。

source-map-loaderについて

source-mapを作成するもの。 これはデバッグ実行したときにトランスパイル済みコードではデバッグが厳しいので、トランスパイル前のコードを表示してくれるもの

CSS関連

style-loaderはスタイルシートをJSからlinkタグに展開してhtml内に注入してくれるもの css-loaderはCSSをバンドルするための機能

画像関連

file-loaderはビルド・バンドルしたものが参照している画像等を適切な場所に配置してくれるもの。 フロントだけの開発の場合はPATHがなかなか合わず、dev-serverと組み合わせないとうまくいかない。 url-loaderというのもあり、こちらは画像/CSSをbase64にして埋め込んでくれて、
これによりURLやPATHを気にしなくてよくなるが、ファイルサイズが肥大化するというデメリットがある。

resolveについて

resolveで指定した拡張子はimport時に拡張子が不要となる。
通常はjstsだけで良いと思われる。
Reactな人はjsxとかもあったほうが良い。
もちろんcssとかも指定可能。

あとがき

以上こんな感じで作ってみました。 てゆうか既にそんなもの、OSSで転がってるよ。っていうのがあるかもしれません。
ただ、MPA前提のはあまりみかけない。
そしてあえてレガシーなjQuery UIを使う…というのも新規で作る場合に選択肢には上がりにくいし。 ただ、便利な部品が多いんですよね。
SPAにするといろいろ問題が起こるんですよね。特に要員的な問題で…。 書いてみて思ったのですがやはりまだまだ知識不足…。

参考リンク

最新版で学ぶwebpack 5入門 - Bootstrapをバンドルする方法 - ICS MEDIA
webpack~Bootstrap4移行ガイド
webpack-dev-serverを使ってみる - のぐそんブログ
webpack-dev-serverの基本的な使い方とポイント - dackdive's blog
webpackでhtmlファイルも出力する - emahiro/b.log
npmとwebpack4でビルド - jQueryからの次のステップ - Qiita
http://js.studio-kingdom.com/typescript/tutorials/react_and_webpack
TypeScript With Babel: A Beautiful Marriage
TypeScriptでasync/await (webpackビルド) - Qiita
フロントエンド知らない私のwebpack入門 その1 - Qiita
webpackでCSSやSASSを使う - 30歳からのプログラミング
【意訳】Webpackの混乱ポイント - Qiita
今時のフロントエンド開発2017 (3. webpack編) - Qiita

MS公式のTypeScript + Babelスターター

GitHub - microsoft/TypeScript-Babel-Starter: A sample setup using Babel CLI to build TypeScript code, and using TypeScript for type-checking.

Bootstrapのサンプル

https://glacial-webwork.com/2018/05/31/bootstrap4_beginer/