EY-Office ブログ

ES6のテンプレートリテラルがあればテンプレートエンジン要らないよね

現在、複数の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'))

- about -

EY-Office代表取締役
・プログラマー
吉田裕美の
開発者向けブログ