フロントエンド開発勉強日誌 2
フロントエンド開発勉強日誌の続きです。前回は Thymeleaf 導入までやっています。
今回はTypeScript開発環境を作ります。
TypeScript開発環境を作る
やることは以下
- 事前勉強
- Node Install
- ProjectにScript実装用のDirectoryを用意する
- package.json用意
- Library Install
- TypeScript実装
- view
- container
- action
- entry
- 設定ファイル
- build
- ThymeleafのHeadにJavaScriptを設定
- express (proxy)
やること多い...多くない?
最終的なPackage構成はこんな感じになります。
demo ├── adapters │ └── web # controllerを記述する ├── application ├── configuration ├── domain └── script # TypeScriptとかを書く場所 ├── bin │ └── proxy.js # expressでバックエンドをProxyする ├── src │ ├── core │ └── sample │ ├── actions │ │ └── sample_action.ts # actionを記述する │ ├── entry │ │ └── entry.ts # entry point │ └── views │ ├── samle_body.ts # htmlを記述する │ └── sample_container.ts # 振る舞いを記述する ├── package.json ├── package.lock.json ├── tsconfig.arrow-js.json ├── tsconfig.base.json ├── tsconfig.json └── webpack.config.js
事前勉強
TypeScript Deep Diveを2回通しで読みました。
Node Install
既に入っていたので省略します。過去の自分がbrew install nodebrew して npm i -g npm した後で勉強を挫折したのだと思います。
script directoryを用意する
TypeScriptを実装するDirectoryをSpringのSubProjectと並列に置きます。別Projectを作ってもOK。
package.json
最低限用意しておきます。
{ "name": "script", "version": "1.0.0", "description": "", "main": "index.js" }
Library Install
webpack, TypeScript
webpackとTypeScriptを導入します。webpackはcssとかjsをバンドルしてくれる凄い奴です。
$ cd script $ npm i -D webpack webpack-cli typescript ts-loader
node, jQuery
nodeの標準moduleを使いたい && jQueryも使うのでこのあたりも入れます。
$ npm i -D @types/jquery $ npm i -D @types/node
express
node製の軽量Webアプリケーション。詳細はここを参照。今回はproxyに利用します。
$ npm i -D express $ npm i -D http-proxy-middleware
lit-html
Tagged Templatesでviewを管理するライブラリ。HTMLコーディング用に入れます。
$ npm i lit-html
TypeScript実装
ここまできて、ようやくTypeScriptの実装に入ります。
SampleBody.ts
ページレンダリング時に表示するHTMLをlit-htmlで記述します
// sample/views/sample_body.ts import { html } from 'lit-html'; export const SampleHtml = () => html` <div> render html!! </div> `;
SampleContainer.ts
Presentational/Container Components Patternで実装します。
SampleContainer.tsに今後振る舞いを定義していく予定です。まずは初期表示時のRenderingを実装します。
// sample/views/sample_container.ts import { render } from 'lit-html'; import { SampleHtml } from './sample_body'; export class SampleContainer { constructor() { // #js-sample-jsonにSampleHtmlをrender render(SampleHtml(), document.getElementById('js-sample-json')) } }
SampleAction.ts
Eventを定義するAction Classを用意します。まずはContainerの初期化を実装します。
// sample/actions/sample_action.ts export class SampleAction { static init(containerClass: any) { // sample-containerの初期化 new containerClass(); } }
entry.ts
webpack.config.tsに設定するentry pointを用意します。 ここでEventListenerを設定します。SampleActionのinitを呼び、その中で引数のContainerをnewします。
// sample/entry/entry.ts import { SampleContainer } from '../views/sample_container'; import { SampleAction } from '../actions/sample_action'; document.addEventListener('DOMContentLoaded', () => { SampleAction.init(SampleContainer); });
設定ファイル
tsconfig.json, webpack.config.js を準備します。
tsconfig.base.json
{ "compilerOptions": { "target": "es5", "module": "commonjs", "incremental": true, "tsBuildInfoFile": "./dist/.tsbuildinfo", "outDir": "dist", "lib": [ "es5", "es2015.promise", "es2015.collection", "dom" ], "sourceMap": true, "moduleResolution": "node", "importHelpers": true } }
tsconfig.json
{ "extends": "./tsconfig.base", "compilerOptions": { "allowJs": true }, "include": [ "src/**/*", "test/**/*", "node_modules/lit-html" ], "exclude": [ "node_modules" ] }
tsconfig.arrow-js.json
{ "extends": "./tsconfig.base", "compilerOptions": { "allowJs": true }, "include": [ "node_modules/lit-html" ] }
webpack.config.js
/* eslint-env es6, node */ const path = require('path'); const { SourceMapDevToolPlugin } = require('webpack'); module.exports = (env, argv) => { return { mode: argv.mode || 'development', entry: { sample: './src/sample/entry/entry.ts' }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'target') }, module: { rules: [ { test: /\.ts$/, use: 'ts-loader' }, // https://github.com/Polymer/lit-html/issues/516#issuecomment-475261804 // lit-htmlがES5にtranspileされるように設定する { test: /\.js$/, include: [/node_modules\/lit-html/], use: { loader: 'ts-loader', options: { configFile: path.resolve(__dirname, 'tsconfig.allow-js.json') } } } ] }, plugins: [ new SourceMapDevToolPlugin({ filename: 'sourcemaps/[file].[hash].map', publicPath: 'http://localhost:3150/target/', append: `\n//# sourceMappingURL=[url]` }) ], resolve: { extensions: ['.ts', '.js'] }, // https://github.com/Microsoft/typed-rest-client/issues/139 // https://github.com/jsoma/tabletop/issues/158#issuecomment-398733851 externals: ['tls', 'net', 'fs'] }; };
build
TypeScriptをwebpackでbuildします。
$ ./node_modules/.bin/webpack Built at: 2099-12-31 23:59:59 Asset Size Chunks Chunk Names sample.bundle.js 82.8 KiB sample [emitted] sample sourcemaps/sample.bundle.js.8606896b6aa71955ad07.map 3.54 KiB sample [emitted] [dev] sample Entrypoint sample = sample.bundle.js sourcemaps/sample.bundle.js.8606896b6aa71955ad07.map [./src/sample/actions/sample_action.ts] 358 bytes {sample} [built] [./src/sample/entry/entry.ts] 338 bytes {sample} [built] [./src/sample/views/samle_body.ts] 466 bytes {sample} [built] [./src/sample/views/sample_container.ts] 469 bytes {sample} [built] + 12 hidden modules
Thymeleaf
ここでSpringに戻り、ThymeleafのHeadにbuildしたJavascriptを設定します。またターゲットのDOMを用意します。
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" lang="ja"> <head> <title>top page</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <script src="/static/sample.bundle.js" crossorigin="anonymous" defer=""></script> </head> <body> <div> <p>Presenterで出力を加工する</p> <p th:text="${view.fooLabel()}" /> </div> <div> <p>sample-json</p> <!-- TypeScriptで何かをRenderingする場所 --> <div id="js-sample-json"></div> </div> </body> </html>
express
expressを使って、Spring (サーバサイド) へのリクエストをproxyします。
// bin/proxy.js 'use strict'; const path = require('path'); const express = require('express'); const httpProxyMiddleware = require('http-proxy-middleware'); const app = express(); // arrow function // 引数1つなら()省略可能 one linerなら {} 省略可能 const proxy = host => httpProxyMiddleware({ target: host, changeOrigin: true }); const FRONT_HOST = 'http://localhost:8080'; const SCRIPT_TARGET_DIR = path.join(__dirname, '..', 'target'); // static files // https://expressjs.com/ja/starter/static-files.html app.use('/static', express.static(SCRIPT_TARGET_DIR)); // サーバサイドへのリクエストをproxyする app.use('/', proxy(FRONT_HOST)); // listen 最高のサイトなのでPORTは3150 app.listen(process.env.PORT || 3150);
expressを起動します。
$ node bin/proxy.js [HPM] Proxy created: / -> http://localhost:8080
IntelliJでSpringも起動してアクセスします。
http://localhost:3150/front/practice
HTMLが表示されます。TypeScriptで挿入した render html!! が表示されたのでとりあえず動いています!
まとめ
TypeScriptの実装環境まで用意しました。難しい...難しくない?導入までの障壁高すぎではないだろうか。。フロントエンドエンジニアって大変ですね 😑