このチュートリアルでは簡単なWebページをRiot.jsで作ってみようと思います。読むのに必要な知識は、次の二つだけ。
- HTML
- CSS
逆に不要な知識は、
- JavaScript: AngularとかReactとか
- CSSの方法論: BEMとかSMACSSとか
など。ただ、上記を把握していると、より「Riot.jsすげー」ってなるかも、なってほしいな。
Riot.jsを使うにあたって、モック作成や、静的ページだけなら、JavaScriptを書く必要はそれほどありません。このチュートリアルを通じて、次の2つのポイントを伝えられればと思います。
- デザイン先行で作成ができる (開発後回し可能)
- HTML+CSSだけでもコンポーネントが作成できる
Riot.jsとは
近年、AngularJSやReact、Backbone.jsなど、さまざまなJavaScriptフレームワークの名前を聞く機会が増えました。新しもの好きなひとは、PolymerやWeb Componentsといったキーワードにも聞き覚えがあるかもしれません。Riot.jsもそういった一連の流れの一つです。
これらの技術が解決する問題は、WebのUIです。サーバでHTMLを組み上げていた時代と異なり、多かれ少なかれブラウザの中で、
- ユーザのアクションに応じて
- 変化するデータに応じて
画面を随時書き換えていく必要がありますが、Riot.jsもそのための方法を提供するライブラリです。従来のアプローチが、JavaScriptをゴリゴリ書いたり、新しい作法をあれやこれやと覚える必要があったのに対して、Riot.jsは「その日から使える」手軽さが魅力と言われています。
何を作る?
簡単な、一枚物のページを考えます。どんな内容が必要でしょうか?
- メニュー
- ヒーローユニット
- 関連リンク
- フッター
このくらいあれば、体裁が整うかな。とりあえず、これで行ってみましょう(デモサイト)。それぞれのグループに名前をつけます。こういった画面上のグループを「コンポーネント」呼びます。チュートリアルでは、このそれぞれのコンポーネント(=独自タグ)を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.html
をBracketで開いてみましょう。右上の「イナズマ」アイコンをクリックして、プレビュー画面を表示します。
TIPS: 他のテキストエディタでも構いませんが、手元でWebサーバを立ち上げる必要があります。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と違い「属性値に文字列以外も使える!」と覚えてください。
ヒーローユニットを作る
よくあるこんなやつ。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
ですね。
メニューを作る
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>
関連リンクを作る
ほぼ同じ内容ですが、こんどはリスト要素ではなく、独自タグ<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>© {{ 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]-->