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
を複数にしてoutput
のfilename
にて[name].js
とすると
entry
のkey
の値(=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時に拡張子が不要となる。
通常はjs
とts
だけで良いと思われる。
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