Hello, world!

2015/05/19

「Cape.JS入門」連載第1回は、伝統に従って「Hello, world!」をブラウザの画面に表示するところから始めます。

ひとつ残念な(残念かもしれない)お知らせがあります。Cape.JS は、最新版の Firefox と Google Chrome、Safari 6.1 以上、そして Internet Explorer 11 のみに対応しています。特に、Internet Explorer 10 以下では動作しません。

早速ですが、適当なテキストエディタで次の内容のHTMLファイルを作成して hello_message1.html という名前で保存してください。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>HELLO WORLD</title>
  <script src="https://cdn.rawgit.com/capejs/capejs/v1.0.1/dist/cape.min.js"></script>
</head>
<body>
  <div id="main"></div>
  <script>
    var HelloMessage = Cape.createComponentClass({
      render: function(m) {
        m.p('Hello, world!');
      }
    });

    var component = new HelloMessage();
    component.mount('main');
  </script>
</body>
</html>

そして、ブラウザでこのファイルを開いてみてください。すると画面左上に小さく「Hello, world!」と表示されるはずです。

画面表示

極めて単純ですが、これで立派な Cape.JS プログラムです。

プログラムは <script> タグの内側に書かれています。

var HelloMessage = Cape.createComponentClass({
  render: function(m) {
    m.p('Hello, world!');
  }
});

var component = new HelloMessage();
component.mount('main');

最初の5行で HelloMessage というクラスを定義しています。そして、そのインスタンスを作って変数 component にセットし、最後にその mount メソッドを呼び出しています。

Cape.JS プログラミングの基本的な流れは、コンポーネントクラスを定義し、そのインスタンスを作って、Web ページのどこかに配置する、という風に進みます。コンポーネントクラスの各インスタンスは mount というメソッドを持っています。このメソッドに対して、Web ページ内のある要素の id 属性の値を指定すれば、そのコンポーネントがそこに配置(マウント)されます。

上の例では HTML ファイルの中に <div id="main"></div> という記述がありますね。この内側に HelloMessage というコンポーネントがマウントされたのです。

鋭い読者の方々は気付かれたと思いますが、Cape.JS のプログラムは React のプログラムにとてもよく似ています。Cape.JS の作者(つまり、私)は自分の開発プロジェクトで使う JavaScript UI フレームワークを探していて React と Riot に出会い、自分でも作れそうな気がして Cape.JS の開発を始めました。用語法、メソッド名の選び方から内部の実装まで、Cape.JS はこれらのフレームワークから多大な影響を受けています。

さて、Cape.JS のコンポーネントクラスは必ず render というインスタンスメソッドを持っていなければなりません。このメソッドの役割は仮想DOMツリーを作ることです。「仮想DOMツリー」が何であるかの説明は別の機会に譲ることにして、ここでは「HTML文書の断片」と理解してください。

HelloMessage クラスの render メソッドは次のように定義されています。

render: function(m) {
  m.p('Hello, world!');
}

このメソッドの唯一の引数(m)は、マークアップ・ビルダーと呼ばれるオブジェクトです。私たちはこのオブジェクトに対してHTML文書の断片を作る方法を指示します。

Cape.JS の興味深い特徴は、メソッド名がそのままHTMLの要素名に相当するということです。上の例では p というメソッドが使われていますが、これはもちろん <p> 要素に当たります。つまり、ここでは次のようなHTML文書の断片を作っていることになります。

<p>Hello, world!</p>

とすれば、<p> タグの代わりに <h1> タグで「Hello, world!」を囲む方法は容易に想像できます。そうです。次のように書きます。

render: function(m) {
  m.h1('Hello, world!');
}

では、<div class="greeting">Hello, world!</div> という仮想DOMツリーを作るにはどうすればいいでしょうか。Cape.JSでは4種類の方法が用意されています。1つ目は、div メソッドの第2引数としてHTML属性の名前と値を表す連想配列を渡す方法です。

render: function(m) {
  m.div('Hello, world!', { class: 'greeting' });
}

第2の方法は、div メソッドを呼ぶ前に class メソッドを呼び出しておくというものです。

render: function(m) {
  m.class('greeting');
  m.div('Hello, world!');
}

マークアップ・ビルダーの class メソッドは、次に仮想DOMツリーへ加えられる要素の class 属性を決めます。

第3の方法は、同じく class メソッドを呼び出しますが、引数として連想配列を渡します。

render: function(m) {
  m.class({ greeting: true });
  m.div('Hello, world!');
}

第4の方法は、elem メソッドを用いるというものです。このメソッドは、第1引数にCSSのセレクタ形式の文字列を取ります。

render: function(m) {
  m.elem('div.greeting', 'Hello, world!');
}

これら4つのやり方には一長一短があります。第1のやり方が最も直感的ですが、プログラムが複雑になってくると可読性が低下するかもしれません。条件によってHTML要素の class 属性を変化させる場合には、第2あるいは第3の方法を使うと書きやすいです。人によっては第4の方法に魅力を感じるかもしれません。

なお、マークアップ・ビルダーのメソッドはマークアップ・ビルダー自体を返しますので、次のようにメソッドを鎖のように連結することが可能です。

render: function(m) {
  m.class('greeting').div('Hello, world!');
}

初回はここまでとしましょう。次回は、コンポーネントをマウントした先の要素からデータを取得する方法とHTML要素をネストする方法について解説する予定です。