Edited at

無謀にもJavaScriptなしでやってみる! Riot.js入門

More than 3 years have passed since last update.

このチュートリアルでは簡単なWebページをRiot.jsで作ってみようと思います。読むのに必要な知識は、次の二つだけ。


  • HTML

  • CSS

逆に不要な知識は、


  • JavaScript: AngularとかReactとか

  • CSSの方法論: BEMとかSMACSSとか

など。ただ、上記を把握していると、より「Riot.jsすげー」ってなるかも、なってほしいな。

Riot.jsを使うにあたって、モック作成や、静的ページだけなら、JavaScriptを書く必要はそれほどありません。このチュートリアルを通じて、次の2つのポイントを伝えられればと思います。


  • デザイン先行で作成ができる (開発後回し可能)

  • HTML+CSSだけでもコンポーネントが作成できる


Riot.jsとは

近年、AngularJSReactBackbone.jsなど、さまざまなJavaScriptフレームワークの名前を聞く機会が増えました。新しもの好きなひとは、PolymerWeb Componentsといったキーワードにも聞き覚えがあるかもしれません。Riot.jsもそういった一連の流れの一つです。

これらの技術が解決する問題は、WebのUIです。サーバでHTMLを組み上げていた時代と異なり、多かれ少なかれブラウザの中で、


  • ユーザのアクションに応じて

  • 変化するデータに応じて

画面を随時書き換えていく必要がありますが、Riot.jsもそのための方法を提供するライブラリです。従来のアプローチが、JavaScriptをゴリゴリ書いたり、新しい作法をあれやこれやと覚える必要があったのに対して、Riot.jsは「その日から使える」手軽さが魅力と言われています。


何を作る?

Artboard 1.png

簡単な、一枚物のページを考えます。どんな内容が必要でしょうか?


  • メニュー

  • ヒーローユニット

  • 関連リンク

  • フッター

このくらいあれば、体裁が整うかな。とりあえず、これで行ってみましょう(デモサイト)。それぞれのグループに名前をつけます。こういった画面上のグループを「コンポーネント」呼びます。チュートリアルでは、このそれぞれのコンポーネント(=独自タグ)をRiot.jsを使って実装することにします。

内容
コンポーネント名

メニュー
app-menu

ヒーローユニット
app-hero

関連リンク
app-links

フッター
app-footer


TIPS: もちろん、Riot.jsが本領を発揮するのは、動的に内容が変化していくようなサイトです。従来型のサイトに近いところからスタートして、慣れてきたらアプリケーション的なものにも挑戦してみましょう ;)



HTMLの復習

何を今更と言わないでください(笑)。HTMLは、<head><body>から成り立っていて、前者にはタイトルなどのメタ情報とスタイルシートを、後者にはコンテンツと最後にJavaScriptを書くのが通例です。

<!DOCTYPE html>

<html>

<head>
<meta charset="utf-8">
<title>Hello world!</title>
<link rel="stylesheet" href="style.css">
</head>

<body>
<p>Hello world!</p>
<script src="app.js"></script>
</body>

</html>

上記を基本パターンとして、ときには<head>の中にスクリプトを入れることもあったり、<body>にスタイルシートを直接書いたりすることもあります。<body>内で、CSSとJavaScriptを完結させるなら、次のような書き方ができます。

<body>

<style>
p { font-family: sans-serif }
</style>

<p>Hello world!</p>

<script>
alert('Hello world!')
</script>
</body>

それでは、続けてRiot.jsのファイル(=タグファイル)を見てみましょう。こちらは、先ほどのフッター部分に相当します。

<app-footer>

<style scoped>
:scope { display: block }
p { font-size: 90% }
</style>

<p>{ opts.message } - { year }</p>

<script>
this.year = (new Date()).getFullYear()
</script>
</app-footer>

既視感にあふれていますね。限りなく、HTMLです。この「HTMLとの完璧な相似形」がRiot.jsの大きな特徴になっています。


チュートリアル


テンプレートのダウンロード

こちらから、チュートリアル用のテンプレートをダウンロードします。

ダウンロード後に展開して、index.htmlBracketで開いてみましょう。右上の「イナズマ」アイコンをクリックして、プレビュー画面を表示します。


TIPS: 他のテキストエディタでも構いませんが、手元でWebサーバを立ち上げる必要があります。Bracketはボタンひとつでプレビューできるのが便利です。


Bracketでプレビュー

Riot.jsでは、コンポーネントをタグファイルと呼ばれる形式で書きます。拡張子を.tagにするのが標準ですが、テキストエディタで見にくいので、ここでは.htmlにしています。<script src="..." type="riot/tag"></script>で読みこんているのがそれです。

tag/app-footer.htmlについては、前述のものが入っています。この後のステップでは、それ以外のファイルを順に実装していきます。


メインコンポーネント

tag/app.htmlを開いてみてください。ファイル内の指示通り、独自タグを足していきます。


  • A:「メニュー」を追加 → app-menu

  • B:「ヒーローユニット」を追加 → app-hero

  • C:「関連リンク」を追加 → app-links

<!-- tag/app.html 作成例 -->

<app>
<app-menu items={ urls } />
<app-hero title="Hello world!" />
<app-links items={ urls } />
<app-footer message="Made in OpenSource Cafe" />

<script>
this.urls = [
{ title: 'GitHub', url: 'https://github.com/osscafe' },
{ title: 'Twitter', url: 'https://twitter.com/osscafe' },
{ title: 'Facebook', url: 'https://www.facebook.com/shimokitazawa.osscafe' }
]
</script>
</app>

items={ urls }の書き方が見慣れないと思いますが、これはRiot.jsの「書き方」です。ここだけ若干JavaScriptになってしまってますが、this.urls = [...]で用意されたデータを渡しています。Riot.jsだと、普通のHTMLと違い「属性値に文字列以外も使える!」と覚えてください。


ヒーローユニットを作る

Image.png

よくあるこんなやつ。tag/app-hero.htmlを開いて、シンプルに、h1要素で書いてみましょう。続けて、スタイルも少し直します。通常のCSSと異なり、他のコンポーネントにスタイルが適用される心配はありません。がんがんh1とかの要素セレクタ単体を使ってOKです。(もう、クラス名なんてほとんど考えなくていいんです!!)


  • A: h1タグを追加: { opts.title }を使います

  • B: 背景色を設定

  • C: h1の文字色を設定

<!-- tag/app-hero.html 作成例 -->

<app-hero>
<style scoped>
:scope {
background-color: #3ebbd3;
display: block;
padding: 12em 0;
}
h1 {
color: white;
}
</style>

<h1>{ opts.title }</h1>
</app-hero>


TIPS: 属性値はopts.hogehogeの形で渡されます。例えば、<app-hero title="Hello world!" />なら、コンポーネントの中からは{ opts.title }で、その値"Hello world!"を参照できます。

TIPS: :scope はコンポーネントのルート要素を指します。この例だと、app-heroですね。



メニューを作る

Screen.png

tag/app-menu.htmlを開いて、メニューを完成させます。each属性は、繰り返し部分です。tag/app.htmlのデータがopts.itemsに渡されているので、それが使えますね。


  • A: title変数と、url変数でテキストリンク

  • B: コンポーネントのスタイルを定義

<!-- tag/app-menu.html 作成例 -->

<app-menu>
<style scoped>
:scope { display: block }
ul {
list-style: none;
padding: 0;
margin: 0;
}
li { display: inline-block }
a {
color: inherit;
display: block;
padding: 1.2em;
text-decoration: none;
}
a:hover { background: #eee }
</style>

<ul>
<li each={ opts.items }>
<a href={ url }>{ title }</a>
</li>
</ul>
</app-menu>


関連リンクを作る

Screen.png

ほぼ同じ内容ですが、こんどはリスト要素ではなく、独自タグ<ring>で「丸いリンク要素」を作ってみましょう。まずは、 tag/app-links.htmlを編集します。


  • A: 最後の3行を、each属性と、opts.itemsを使って書き換え

<!-- tag/app-links.html 作成例 -->

<app-links>
<style scoped>
:scope {
display: block;
padding: 2em 0;
}
</style>

<ring each={ opts.items } title={ title } url={ url } />
</app-links>

続けて、tag/ring.htmlを開いて<ring>の実装にとりかかります。次のふたつを属性を持たせます。丸くするのには、CSSのborder-radiusが使えますね。


  • title

  • url

<!-- tag/ring.html 作成例 -->

<ring>
<style scoped>
:scope {
display: inline-block;
font-size: 120%;
height: 7em;
line-height: 7em;
margin: .5em;
overflow: hidden;
position: relative;
width: 7em;
}
a {
background: gray;
border-radius: 50%;
color: white;
display: block;
height: 100%;
text-decoration: none;
}
a:hover {
background: #3ebbd3;
}
</style>

<a href={ opts.url }>{ opts.title }</a>
</ring>


TIPS: <circle>はSVGのタグにあるので、ここでは<ring>としました。確実に要素名がかぶらないようにするには、ハイフン-を含む要素名にするとベターです。


はい! ここまでで完成です。改めて、index.htmlをプレビューしてみまししょう。正しく表示されましたか?


まとめ

Riot.jsの手軽さ、伝わったでしょうか? HTMLの延長なので簡単だったと思います。でも、ここまで来るのにJavaScript界では5年かかりました...長かった...。

ひとつ注意事項として、Riot.jsの2.0.x系については、まだまだアグレッシブにプルリクエストを受け付けている段階です。2.1に上がる頃には一段落するかなと思うので、それまでは素振りをしておくのが良さげです。

Web Componentsを念頭に、HTMLをコンポーネントに分けて制作するのは、今後数年の潮流になるのは間違いないです。ツールに関しては栄枯盛衰あるにしても、方向感は定まってきました。今はまだ、主に取り組んでいるのがJavaScriptな人たちなのでアレですが、最終的には、デザイン/HTML/CSSなひとが触りやすいツールに落とし込む必要があります。ほかの陣営としても、Riot.jsのシンプルさを超えられるかは今後の試金石となりそうですね。


APPENDIX: 技術的な補足


Web Componentsとの違い

Riot.jsとWeb Componentsはイコールではありません。Web Componentsのライブラリとして一番人気なのはPolymerですが、コードを見てみると<polymer-element><template>など、若干のとっつきにくさを覚えるかもしれません。

<polymer-element name="app-footer" attributes="copyright">

<template>
<style>
p { font-family: sans-serif }
</style>
<p>&copy; {{ year }} {{ copyrights }}</p>
</template>
<script>
Polymer({
year: (new Date()).getFullYear(),
copyrights: ''
});
</script>
</polymer-element>

ただ、差異はそれほど大きくないので、将来的にRiot.jsもWeb Componentsにネイティブ対応することは十分可能だろうと考えています。


Scoped CSS

Scoped CSSは、Web標準として提案されているものの一つです。scopedが設定されると、スタイル指定は<style>タグの置かれたノード以下だけに限定されます。Web Componentsでもコンポーネント内に限定したCSSがデフォルトになります。

BEMやSMACSSといったCSSの方法論でなんとか乗り切っていた部分が、一気にシンプルになり、無理がなくなります。クラス指定自体ほとんど不要になるため、個人的にも大きな期待を寄せています。興味のある方はこちらの記事もどうぞ。


コンパイルについて

単純化のためにコンパイルについては触れずにおきましたが、本格的な利用にあたっては、riotコマンド他を使ってtagファイルをJavaScriptにコンパイルしたほうがベターです。ただ、Node.js(か io.js)の環境整備が必要になるので、ここでは割愛します。またの機会に〜。


IE8に対応させる

index.html<head>に次のコードを足します。html5.addElementsの中身は、使っている独自タグに合わせて調整します。

<meta http-equiv="X-UA-Compatible" content="IE=edge">

<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.0.5/es5-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.min.js"></script>
<script>html5.addElements('app app-footer app-hero app-menu app-links ring')</script>
<![endif]-->