もうhtml/templateはいらない！Goに最適なQuicktemplateとは


[![Group191.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/3c7b4c00-f2bb-4e53-8e0f-beeeaac57af0.png)](https://leapcell.io/)



**[Leapcell: The Best of Serverless Web Hosting](https://leapcell.io/)**

# Quicktemplate Template Library の使用ガイド

最近、当社のプロジェクトのコードを整理しているとき、多くのアクティビティのコードが構造的に非常に似ており、提供する機能も似ていることに気付きました。将来の開発を容易にするために、私はコードフレームワークを生成するツールを作成するために時間を費やし、繰り返しの作業を最小限に抑えました。コード自体は複雑ではなく、プロジェクトのコードと密接に関係しているため、ここでは詳細を説明しません。この過程で、Go の標準テンプレートライブラリ `text/template` と `html/template` はかなり制限があり、使いにくいことがわかりました。GitHub を通じて、機能が強力で、構文がシンプルで、使いやすいサードパーティのテンプレートライブラリ `quicktemplate` を学びました。今日は、`quicktemplate` を紹介します。

## クイックスタート
本文のコードは Go Modules を使用しています。

### プロジェクトの初期化
まず、コードディレクトリを作成して初期化します：
```bash
$ mkdir quicktemplate && cd quicktemplate
$ go mod init github.com/leapcell/quicktemplate
```

### 依存関係のインストール
`quicktemplate` は私たちが書くテンプレートコードを Go 言語のコードに変換します。そのため、`quicktemplate` パッケージと `qtc` というコンパイラをインストールする必要があります：
```bash
$ go get -u github.com/valyala/quicktemplate
$ go get -u github.com/valyala/quicktemplate/qtc
```

### テンプレートファイルの作成
まず、`quicktemplate` 形式のテンプレートファイルを作成する必要があります。テンプレートファイルのデフォルトの拡張子は `.qtpl` です。ここで、簡単なテンプレートファイル `greeting.qtpl` を作成しました：
```template
関数の外のすべてのテキストはコメントとして扱われます。

{% func Greeting(name string, count int) %}
  {% for i := 0; i < count; i++ %}
    Hello, {%s name %}
  {% endfor %}
{% endfunc %}
```

### テンプレート構文の説明
テンプレート構文は非常にシンプルです。以下の 2 点を簡単に理解すれば十分です：
- テンプレートは関数を単位としています。関数は任意の型と任意の数のパラメータを受け取ることができ、これらのパラメータは関数内で使用することができます。関数の外のすべてのテキストはコメントであり、`qtc` コンパイラはコメントを無視します。
- 関数内の内容は、構文構造以外はレンダリングされたテキストとしてそのまま出力されます。スペースや改行も含みます。

### テンプレートの生成と使用
`greeting.qtpl` を `templates` ディレクトリに保存し、その後 `qtc` コマンドを実行します。このコマンドにより、対応する Go ファイル `greeting.qtpl.go` が生成され、パッケージ名は `templates` です。これで、このテンプレートを使用することができます：
```go
package main

import (
  "fmt"

  "github.com/leapcell/quicktemplate/get-started/templates"
)

func main() {
  fmt.Println(templates.Greeting("leapcell", 5))
}
```

### コードの実行
テンプレート関数を呼び出し、パラメータを渡して、レンダリングされたテキストを返します：
```bash
$ go run .
```
出力結果：
```plaintext
Hello, leapcell
Hello, leapcell
Hello, leapcell
Hello, leapcell
Hello, leapcell
```
`{%s name %}` はテキストの置換を行い、`{% for %}` はループして繰り返しのテキストを生成します。出力に複数のスペースや改行が表示されるのは、関数内の構文構造以外は、スペースや改行を含め、他の内容はそのまま保持されるからです。

### 注意事項
`quicktemplate` はテンプレートを Go コードに変換して使用するため、テンプレートを変更した場合は、必ず `qtc` コマンドを最初に実行して Go コードを再生成する必要があります。そうしないと、変更内容が有効になりません。

## 構文構造
`quicktemplate` は一般的な Go の構文構造をサポートしており、`if`、`for`、`func`、`import`、`return` などがあります。書き方は直接 Go コードを書くのと大きく異ならず、学習コストはほとんどありません。ただし、テンプレートでこれらの構文を使用する場合は、`{%` と `%}` で囲む必要があり、`if`、`for` などは `endif`、`endfor` を追加して明確に終了を示す必要があります。

## 変数
上で、`{%s name %}` を使用して渡されたパラメータ `name` をレンダリングする方法を見ました。`name` は `string` 型であるため、`{%` の後に `s` を使用して型を指定しています。`quicktemplate` は他の型の値もサポートしています：
- 整数：`{%d int %}`, `{%dl int64 %}`, `{%dul uint64 %}`。
- 浮動小数点数：`{%f float %}`。出力精度も設定でき、`{%f.precision float %}` を使用します。例えば、`{%f.2 1.2345 %}` は `1.23` を出力します。
- バイトスライス (`[]byte`)：`{%z bytes %}`。
- 文字列：`{%q str %}` またはバイトスライス：`{%qz bytes %}`。引用符は `&quot;` にエスケープされます。
- 文字列：`{%j str %}` またはバイトスライス：`{%jz bytes %}`。引用符はありません。
- URL エンコード：`{%u str %}`, `{%uz bytes %}`。
- `{%v anything %}`：出力は `fmt.Sprintf("%v", anything)` と同等です。

### サンプルコード
まず、テンプレートを作成します：
```template
{% func Types(a int, b float64, c []byte, d string) %}
  int: {%d a %}, float64: {%f.2 b %}, bytes: {%z c %}, string with quotes: {%q d %}, string without quotes: {%j d %}.
{% endfunc %}
```
次に、それを使用します：
```go
func main() {
  fmt.Println(templates.Types(1, 5.75, []byte{'a', 'b', 'c'}, "hello"))
}
```
実行します：
```bash
$ go run .
```
出力結果：
```plaintext
int: 1, float64: 5.75, bytes: abc, string with quotes: &quot;hello&quot;, string without quotes: hello.
```

## 関数の呼び出し
`quicktemplate` はテンプレート内でテンプレート関数や標準ライブラリの関数を呼び出すことをサポートしています。`qtc` は直接 Go コードを生成するため、同じディレクトリ内に独自の関数を書いて、テンプレートから呼び出すこともできます。テンプレート A で定義された関数は、テンプレート B で定義された関数を呼び出すこともできます。

### カスタム関数の定義
まず、`templates` ディレクトリに `rank.go` ファイルを作成し、スコアを受け取り、評価を返す `Rank` 関数を定義します：
```go
package templates

func Rank(score int) string {
  if score >= 90 {
    return "A"
  } else if score >= 80 {
    return "B"
  } else if score >= 70 {
    return "C"
  } else if score >= 60 {
    return "D"
  } else {
    return "E"
  }
}
```

### テンプレート内でカスタム関数を呼び出す
次に、この関数をテンプレート内で呼び出すことができます：
```template
{% import "fmt" %}
{% func ScoreList(name2score map[string]int) %}
  {% for name, score := range name2score %}
    {%s fmt.Sprintf("%s: score-%d rank-%s", name, score, Rank(score)) %}
  {% endfor %}
{% endfunc %}
```

### テンプレートのコンパイルと使用
テンプレートをコンパイルします：
```bash
$ qtc
```
プログラムを書きます：
```go
func main() {
  name2score := make(map[string]int)
  name2score["leapcell"] = 85
  name2score["bob"] = 86
  name2score["alice"] = 22

  fmt.Println(templates.ScoreList(name2score))
}
```
プログラムを実行して出力します：
```bash
$ go run .
```
出力結果：
```plaintext
leapcell: score-85 rank-B
bob: score-86 rank-A
alice: score-22 rank-E
```
テンプレート内で `fmt` パッケージを使用しているため、`{% import %}` を使用してパッケージを最初にインポートする必要があります。テンプレート内で別のテンプレートの関数を呼び出す方法も似ています。なぜなら、テンプレートは最終的に Go コードに変換され、Go コード内に同じシグネチャの関数が存在するからです。

## ウェブアプリケーション
`quicktemplate` はよく HTML ページのテンプレートを書くために使用されます：
```template
{% func Index(name string) %}
<html>
  <head>
    <title>Awesome Web</title>
  </head>
  <body>
    <h1>Hi, {%s name %}
    <p>Welcome to the awesome web!!!</p>
  </body>
</html>
{% endfunc %}
```
次に、簡単なウェブサーバを書きます：
```go
func index(w http.ResponseWriter, r *http.Request) {
  templates.WriteIndex(w, r.FormValue("name"))
}

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", index)

  server := &http.Server{
    Handler: mux,
    Addr:    ":8080",
  }

  log.Fatal(server.ListenAndServe())
}
```
`qtc` は `Write*` メソッドを生成し、このメソッドは `io.Writer` 型のパラメータを受け取ります。テンプレートをレンダリングした結果は、この `io.Writer` に書き込まれます。私たちは `http.ResponseWriter` を直接パラメータとして渡すことができ、非常に便利です。

### ウェブサーバの実行
```bash
$ qtc
$ go run .
```
ブラウザで `localhost:8080?name=leapcell` を入力して結果を表示します。

## まとめ
`quicktemplate` には少なくとも以下の 3 つの利点があります：
- 構文は Go 言語と非常に似ており、学習コストはほとんどありません。
- 最初に Go に変換され、レンダリング速度が非常に速く、標準ライブラリ `html/template` の 20 倍以上速いです。
- セキュリティ上の理由から、いくつかのエンコードを行い、攻撃を回避します。


# [Leapcell: The Best of Serverless Web Hosting](https://leapcell.io/)

最後に、Go サービスのデプロイに最適なプラットフォームをお勧めします：**[Leapcell](https://leapcell.io/)**

[![brandpic7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/a14aac72-6364-40be-8822-c80e2f325fb6.png)](https://leapcell.io/)


### 🚀 好きな言語で構築する
JavaScript、Python、Go、または Rust で簡単に開発できます。  

### 🌍 無料で無制限のプロジェクトをデプロイする
使用する分だけ支払います — リクエストがなければ、請求はありません。  

### ⚡ 使った分だけ支払い、隠された費用はありません
アイドル料金はなく、シームレスなスケーラビリティが提供されます。  

[![Frame3-withpadding2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/7d08b7bc-9c37-497f-a68e-b4e5eac07d9d.png)](https://leapcell.io/)


[📖 ドキュメントを探索する](https://docs.leapcell.io/)  

🔹 Twitter でフォローしてください：[@LeapcellHQ](https://x.com/LeapcellHQ)