LoginSignup
2
3

More than 5 years have passed since last update.

Node.jsでUnderscore.jsのtemplateを使う

Last updated at Posted at 2017-01-04

Node.jsを勉強しているのですが、何か作ろうと思った時に静的なファイルを返すだけじゃなあ……と思っていたので、テンプレートエンジンを探していました。
どうやらUnderscore.jsでテンプレートエンジンのようなことができるということなので、実際に試してみました。

環境

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.11.6
BuildVersion:   15G1212

$ node --version
v7.3.0

Underscore.js 1.8.3

準備

まずはnpm initでpackage.jsonを作ります。
その後、npm install underscore --saveでUnderscoreをインストールします。

npm init
npm install underscore --save

また、package.jsonの"scripts""start": "node index.js"を追加しておくと、npm startでサーバを起動できるのでやっておくと少し手間が省けます。

サーバ実装

まずはwebサーバを起動できないと話にならないので、サーバを実装します。

index.js
const http = require('http'),
  url = require('url');

http.createServer(onRequest).listen(8080);
console.log('Server has started on 8080.');

function onRequest(request, response){
  let pathname = url.parse(request.url).pathname;
  console.log(`Request for ${pathname} received.`);
  response.writeHead(200, {'Content-Type': 'text/html'});
  response.write(`<p>Request path name = ${pathname}</p>`);
  response.end();
}

これでlocalhost:8080にブラウザからアクセスすると、リクエストしたURLが表示されるようになりました。
試しにnpm startしてlocalhost:8080/fooにアクセスすると、 Request path name = /foo が表示されます。

次にUnderscoreのtemplateメソッドを使ってみましょう。

_.template(templateString, [settings])

templateの使い方はUnderscorejs.orgを見てもらうのが一番良いと思います。
テンプレート用の構文は3種類あり、標準では以下のように記述します。

//テンプレートに与えられた値をそのまま出力します。
let compiled = _.template('hello: <%= name %>');
compiled({name: 'moe'});
//=> "hello: moe"

//テンプレートに与えられた値をエスケープ(_.escape())して出力します。
let template = _.template('<b><%- value %></b>');
template({value: '<script>'});
//=> '<b>&lt;script&gt;</b>'

//テンプレートに与えられた値をJavaScriptとして評価します。
let compiled = _.template("<% print('Hello ' + epithet); %>");
compiled({epithet: 'stooge'});
//=> "Hello stooge"

標準のテンプレート構文(<% ... %>,<%= ... %>,<%- ... %>)が嫌だという場合は、_.templateSettingsを変更することで、構文を好きなように変更することができます。

では実際にテンプレートを返すサーバを実装しましょう。先ほどのindex.jsに変更を加えます。

index.js
const http = require('http'),
  url = require('url'),
  __ = require('underscore');

http.createServer(onRequest).listen(8080);
console.log('Server has started on 8080.');

function onRequest(request, response){
  let pathname = url.parse(request.url).pathname,
    interpolate = __.template('<p>Request path name = <%= pathname %></p>'),
    evaluate = __.template("<p>Request path name's length = <% print(pathname.length); %></p>"),
    escape = __.template('<p>Escaped request path name = <%- pathname %>');
  console.log(`Request for ${pathname} received.`);
  response.writeHead(200, {'Content-Type': 'text/html'});
  //response.write(`<p>Request path name = ${pathname}</p>`);
  response.write(interpolate({pathname: pathname}));
  response.write(evaluate({pathname: pathname}));
  response.write(escape({pathname: pathname}));
  response.end();
}

Underscoreをrequire()するとき、変数名に_は使わないほうが良いでしょう。
Node.jsのREPLで_は予約語になっているためです。そのため、ここでは__としました。
REPLを使わないということであれば、変数名に_を使っても良いかもしれません。

npm startしてサーバを起動し、localhost:8080/fooにアクセスした場合、以下の結果が得られます。

Request path name = /foo

Request path name's length = 4

Escaped request path name = /foo

パス名はエスケープされないのであまりありがたみを感じませんね……。例としては良くありませんでした。

テンプレートを外部ファイルにする

さて、一応_.template()を使って動的なHTMLを作ることには成功しました。
ですがJavaScript内にHTMLがあるのはメンテナンス上嬉しくないと思いますので、外部ファイルに切り出しましょう。
以下のようなtemplate.htmlを作成します。

template.html
<!DOCTYPE html>
<html>
  <head>
    <title><%- title %></title>
  </head>
  <body>
    <h1><%- title %></h1>
    <p><%- p %></p>
  </body>
</html>

そしてindex.jsを以下のように変更します。

index.js
const http = require('http'),
  url = require('url'),
  __ = require('underscore'),
  fs = require('fs');

http.createServer(onRequest).listen(8080);
console.log('Server has started on 8080.');

function onRequest(request, response){
  let pathname = url.parse(request.url).pathname,
    interpolate = __.template('<p>Request path name = <%= pathname %></p>'),
    evaluate = __.template("<p>Request path name's length = <% print(pathname.length); %></p>"),
    escape = __.template('<p>Escaped request path name = <%- pathname %>');
  console.log(`Request for ${pathname} received.`);
  fs.readFile('template.html', 'utf-8', (err, data) => {
    if(err) {
      response.writeHead(404, {'Content-Type': 'text/plain'});
      response.write('Not Found');
      response.end();
    }else{
      response.writeHead(200, {'Content-Type': 'text/html'});
      //response.write(`<p>Request path name = ${pathname}</p>`);
      //response.write(interpolate({pathname: pathname}));
      //response.write(evaluate({pathname: pathname}));
      //response.write(escape({pathname: pathname}));
      response.write(__.template(data)({title: 'Title', p: 'paragraph'}));
      response.end();
    }
  });
}

fsをrequire()して、ローカルのファイル(template.html)から読み込んだテンプレートを_.template()に渡しています。
サーバにアクセスすると、以下のような結果が得られます。

Title

paragraph

テンプレートを外部に切り出すことに成功しました。

最後に

一応自分が望むような外部のテンプレートを作ることはできましたが、この方法だと規模が大きくなると厳しいのではないかと思います。おとなしくフレームワーク使ったほうが良いのかも?

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3