現在、複数のJSONファイルを読み込んでHTMLファイルを作るツールを作っています。ファイルを読み込んでHTMLを作成するといえばNext.jsですが、今回作成するHTMLはシンプルなのでNode.jsだけで作る事にしました。
ただし、HTMLを生成する部分はテンプレートエンジンのライブラリーを使うかな?と考えTop 17 Templating Engines for JavaScript To Improve and Simplify Your Workflow 2020のようなページを眺めていたのですが、
ES6でJavaScriptに入ったテンプレートリテラルを使えばテンプレートエンジンなど要らないのでは?と閃き小さなサンプルコードを作ったら、良い感じでHTMLが作成できました。
テンプレートリテラルとは
テンプレートリテラルはES2016(ES6)でJavaScriptに導入された文字列リテラルで以下のような特徴があります
- `に始まり`で終わる文字列リテラル
- 改行を含む文字列が書ける
- 文字列の中に
${式}
で式が書ける - 上の
式
の中にもテンプレートリテラルを書ける - タグ`リテラル` のようにタグと呼ばれる関数を指定できる
サンプルコード
サンプルコードは以下のような画面を作るものです。
1. まずは基本部分
このサンプルではブラウザーで表示できるように、サーバサイド・フレームワークExpressの中で使う事にしました。
- /
http://localhost:3000/
にブラウザーでアクセスするとapp.get("/",
に定義された無名関数が実行されます - その中でサーバーからHTMLを戻す
res.send()
関数にHTML文字列を渡し、呼び出します。 - テンプレートリテラルは改行を含む文字列が書けるので普通にHTMLを書きます
${moneyBook()}
の部分でmoneyBook()関数を呼び出し、戻り値をbodyの内容にしていますapp.use('/css'...)
はCSSファイルを配信するExpressの機能app.listen(3000)
はExpressサーバーをポート3000に起動します
import * as express from 'express'
const app = express()
app.get("/", (_, res) => {
res.send(`
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" type="text/css" href="css/index.css">
<title>My App</title>
</head>
<body>
${moneyBook()}
</body>
</html>
`)
})
app.use('/css', express.static(__dirname + '/../css'))
app.listen(3000)
2. moneyBook関数
小遣い帳の表(table)を表示する部分、ここではテンプレートエンジンで必要になる繰り返しを含んでいます。
- booksは小遣い帳データの入った配列です
${...}
で表のHTML作成していますbooks.map((book) => ...)
でbooks配列の内容で表の一行分を作るmoneyBookItem()関数を呼び出し、戻り値の配列が作られます- 上の結果を
.join("\n")
で結合して文字列にします
const moneyBook = (): string => {
const books: BookType[] = [
{date: "1/1", item: "お年玉", amount: 10000},
{date: "1/3", item: "ケーキ", amount: -500},
{date: "2/1", item: "小遣い", amount: 3000},
{date: "2/5", item: "マンガ", amount: -600}]
return `
<h1>小遣い帳</h1>
<table class="book">
<thead>
<tr><th>日付</th><th>項目</th><th>入金</th><th>出金</th></tr>
</thead>
<tbody>
${books.map((book) => moneyBookItem(book)).join("\n")}
</tbody>
</table>
`
}
3. moneyBookItem関数
表の一行分を作るmoneyBookItem()関数は、テンプレートエンジンで必要になる条件分岐を含んでいます
- HTMLの中に
${変数}
で変数の値を埋め込みます ${amount >= 0 ? amount : ""}
はamountが正の数の場合のみamountの値を埋め込みます
const moneyBookItem = (book: BookType): string => {
const {date, item, amount} = book
return `
<tr>
<td>${date}</td>
<td>${item}</td>
<td>${amount >= 0 ? amount : ""}</td>
<td>${amount < 0 ? -amount : ""}</td>
</tr>
`
}
まとめ
このように、テンプレートリテラルを使うことで、テンプレートエンジン等のライブラリーを使わずに可読性の高いビューのコードが書けることを理解して頂けたでしょうか。
おまけ
Reactをご存じの方は、上のテンプレートリテラルのサンプルはJSXに似ていると思ったでしょう!
JSXではプリプロセッサ等を用いてHTMLタグをJavaScriptの式として扱っていますが、ここではHTMLを文字列(テンプレートリテラル)として扱い、コンポーネントは単なる関数呼び出しに置き換えています。
React(JSX)に慣れ親しでいるので、このようなテンプレートリテラルの使い方が思いついたのです 😀
Reactのサンプルコード
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
type BookType = {
date: string
item: string
amount: number
}
const MoneyBook: React.FC = () => {
const books: BookType[] = [
{date: "1/1", item: "お年玉", amount: 10000},
{date: "1/3", item: "ケーキ", amount: -500},
{date: "2/1", item: "小遣い", amount: 3000},
{date: "2/5", item: "マンガ", amount: -600}]
return (
<div>
<h1>小遣い帳</h1>
<table className="book">
<thead>
<tr><th>日付</th><th>項目</th><th>入金</th><th>出金</th></tr>
</thead>
<tbody>
{books.map((book) =>
<MoneyBookItem book={book} key={book.date + book.item} /> )}
</tbody>
</table>
</div>
)
}
type MoneyBookItemType = {
book: BookType
}
const MoneyBookItem: React.FC<MoneyBookItemType> = (props) => {
const {date, item, amount} = props.book
return (
<tr>
<td>{date}</td>
<td>{item}</td>
<td>{amount >= 0 ? amount : null}</td>
<td>{amount < 0 ? -amount : null}</td>
</tr>
)
}
ReactDOM.render(<MoneyBook />, document.getElementById('root'))