Advent Calendarの3日目です。近年、JavaScriptでプログラムを書くのに避けて通れない「プリコンパイル」について、Riotの文脈で3本ほど書いてみたいと思います。
- ES6で書くRiot - riot.config.js編 - (サンプルコード)
- ES6で書くRiot - Rollup編 - (サンプルコード)
- ES6で書くRiot - Felt編 - (サンプルコード)
はじめてのriotコマンド
Riotでは、次のような「タグ(tag)」ファイルを書いて、コンポーネントを作成します。
<my-tag>
<p>{ message }</p>
<script>
this.message = 'Hello Riot!'
</script>
<style>
:scope { display: block; color: #f04 }
</style>
</my-tag>
デスクトップにhello-riot
というフォルダを作り、そこにmy-tag.tag
というファイル名で保存しておいてください。残念ながら、これをそのままブラウザで開くことはできませんが、心配しないで! 実行できる形に変換するための公式コマンドがあります。それが本稿の主眼であるriot
です。
Node.jsのインストール
もし、まだNode.jsの準備ができていなければ、こちらからダウンロードして、環境を整えてください。
メモ: 執筆時現在で、
v6.9.1
またはv7.2.0
が利用可能で、どちらを使っても構いません。ただ、偶数バージョンのNodeだけが長期サポートの対象になるので、今の時点では6系が無難です。
さあ「黒い画面」を開きましょう。Macであれば「アプリケーション > ユーティリティ > ターミナル.app」にあります。次のようにコマンドnode -v
と打ってEnterキーを押すと、バージョンが表示されます。
メモ: 以下、入力すべきコマンドの前には
$
をつけますが、$
を打つ必要はありません。
$ node -v
v6.9.1
ここで、もしv0.12.0
などのように表示された場合、インストールされているNodeのバージョンが古すぎます。Node.jsのインストールからやり直しましょう。以下も参考まで。
-
v0.11.x
v0.12.x
: 古すぎます -
v1
〜v3
: io.jsはNodeに統合されました。v4
以降に上げましょう -
v4
: OK (執筆時点でv4.6.2
が最新です) -
v5
: もうメンテナンスされていません。v6
に上げましょう -
v6
: OK (執筆時点でv6.9.1
が最新です) -
v7
: OK
riotコマンドのインストール
次は、riot
コマンドのインストールです。Node.jsを入れた時点で、npm
というコマンドが利用可能になっていますので、これを使ってインストールします。
$ npm install -g riot
メモ: npmのサーバにアクセスするため、
npm
コマンドの多くはネット接続を前提にしています。オフラインだと使えないので注意してください。
数十秒から1分ほどで、ダウンロードとインストールが完了すると思います。
タグファイルをコンパイル
それでは、早速、先ほどのファイルをコンパイルしてみましょう。先ほどのファイルはデスクトップに保存したので、まずはそこに移動します。
$ cd ~/Desktop/hello-riot
次にriot
コマンド:
$ riot my-tag.tag
my-tag.tag -> my-tag.js
my-tag.js
というファイルが、生成されます。下記、見やすいように改行位置を調整していますが、これに近いものが生成されたはずです。
riot.tag2('my-tag',
'<p>{ message }</p>',
'my-tag a,[data-is="my-tag"]{ display: block; color: #f04 }',
'',
function(opts) { this.message = 'Hello Riot!'; }
);
HTMLに組み込む
JavaScriptのファイルだけでは、ブラウザに表示できないので、起点となるHTMLファイルを作成します。次の内容をindex.html
として保存してください。
<!doctype html>
<html>
<head>
<title>Hello Riot!</title>
</head>
<body>
<my-tag></my-tag>
<script src="https://cdn.jsdelivr.net/riot/3.0/riot.min.js"></script>
<script src="my-tag.js"></script>
<script> riot.mount('*') </script>
</body>
</html>
いくつかポイントがあります。
-
<my-tag></my-tag>
は、タグを「マウント」するための一種のプレースホルダです。 - RiotのライブラリをCDNから読み込んでいます。
- 先ほどコンパイルした
my-tag.js
も<script>
タグで読み込んでいます。 -
riot.mount('*')
のところで、実際にタグをマウントしています。
メモ: 「マウント」というのはRiotの用語で、指定部分が実際の「タグ」で置き換えることを言います。
このindex.html
をブラウザで開いてみましょう。「Hello Riot!」と表示されていれば成功です。
次世代標準で書く
JavaScriptは、ここ数年大きく変化しています。2015年にES6(ES2015)が勧告され、ブラウザ側の対応も進んでいます。ただ、モバイルまで含めると、足並みが揃っていないのも事実で、ES6の文法で書いてしまうと実行できない環境が、少なからずあります。
一方で、ES7(ES2017)の足音も近づいており、ブラウザの完全対応を待ってからES6
に移行すれば良いという問題ではありません。Web上に公開されているコードも、多くはES6(かES7)を前提に書いているため、古い文法だけを使い続けることはもう難しくなってしまいました。
babel とか bublé とか
未対応のブラウザと、次世代標準の間のギャップを埋めるのが「トランスパイラ」と呼ばれるツールです。これらは、ES6やES7で書かれたJavaScriptのコードを、ほぼ全てのブラウザで使えるES5に変換してくれます。
babelが有名ですが、非常に重く、設定が若干面倒なのが玉に瑕です。次項以降ではbabelの代わりに、設定が簡単でしかも高速なbubléを使おうと思います。
試しに書いてみる
さきほどのmy-tag.tag
を少し書き換えます。ES6で導入された機能はいろいろありますが、ここでは定数を宣言するconst
と、文字列の中に${変数}
を埋め込める機能を試してみます。
<my-tag>
<p>{ message }</p>
<script>
const name = 'Riot and Bublé'
this.message = `Hello ${name}!`
</script>
<style>
:scope { display: block; color: #f04 }
</style>
</my-tag>
書き換えたら保存を忘れずに。
bubléをインストール
下準備として、次のコマンドを実行します。いろいろ聞かれますが、毎回Enterキーを押すだけで今のところはOKです。全て答え終わると、package.json
というファイルが作成されます。
$ npm init
...
{
"name": "hello-riot",
"version": "1.0.0",
"description": "",
"main": "my-tag.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
次にbuble
をインストールします。
$ npm install -D buble
開発用モジュールとして利用したいので、-D
フラグをつけました。その詳しい意味について、ここでは省略しますが、次の点だけ押さえておけばOKです。
-
-g
をつけた場合と異なり「ローカル」つまり、作業中のフォルダ内にインストールされる。(node_modules
というフォルダが新しく出来ているはずです) - インストール情報は、
package.json
に追記されます。
riot.config.js
riot
コマンドでは、--config
オプションをつけて設定ファイルを読み込むことが可能です。buble
についても、この設定ファイルを介して呼び出します。
次の内容を、riot.config.js
というファイル名で保存してください。
import buble from 'buble' // bubleを読み込む -- (1)
export default {
// スクリプトのタイプとして`buble`を指定 -- (2)
type: 'buble',
parsers: {
// パーサの種類として`buble`を追加 -- (3)
js: { buble: js => buble.transform(js) }
}
}
メモ: (2)と(3)の部分で「タイプ」として「buble」を指定しましたが、任意の別の文字列でも構いません。
それでは、あらためてriot
コマンドを実行します。
$ riot my-tag.tag --config riot.config.js
すると、スクリプト部分がbuble
によって処理され、ES5に書き換えられました。
riot.tag2('my-tag',
'<p>{message}</p>',
'my-tag,[data-is="my-tag"]{ display: block; color: #f04 }',
'',
function(opts) {
var name = 'Riot and Bublé'
this.message = "Hello " + name + "!"
}
);
- 変換前:
this.message = `Hello ${name}!`
- 変換後:
this.message = "Hello " + name + "!"
まとめ
ここで紹介したコードものとほぼ同じものを、公式のExampleリポジトリにも置きました。そちらもご参考まで。
Riotで、タグファイルのコンパイルすることからはじめて、buble
を使ってES6→ES5変換をするまでを見てきました。RiotでES6を使う方法はいくつかあるのですが、ここでのやり方を知っていれば他への応用もやりやすくなるはずです。
一方で、ES6の特徴であるimport
文を使って、タグ内でモジュールを読み込む方法については触れませんでした。それをするためには、実はbuble
などの「トランスパイラ」だけでは足りず別途「バンドラ」が必要になります。その点については、次回「Rollup編」で説明したいと思います。
APPENDIX: CSSも次世代標準に対応する
CSSについては、cssnextというモジュールを使うと、現行のブラウザに対応したCSSに変換してくれます。その場合のriot.config.js
は次のようになります。
import buble from 'buble'
import postcss from 'postcss'
import cssnext from 'postcss-cssnext'
export default {
type: 'buble',
style: 'cssnext',
parsers: {
js: { buble: js => buble.transform(js) },
css: { cssnext: (tagName, css) => {
// ちょっとだけハックして、:scopeを:rootに置き換えてPostCSSに渡す
// タグの中でCSS変数を使いやすくするため
css = css.replace(/:scope/g, ':root')
css = postcss([cssnext]).process(css).css
css = css.replace(/:root/g, ':scope')
return css
}}
}
}
タグの中で、こんなふうに次世代標準のCSSを使うことができます。
<my-tag>
..略..
<style>
:scope {
--riot-color: #f04;
}
h1 {
color: var(--riot-color);
border-bottom: 1px solid var(--riot-color);
}
</style>
</my-tag>
riot
コマンドを実行する前に、次のコマンドで必要なモジュールをインストールしておきましょう。
$ npm install -D postcss postcss-cssnext
先ほどと同様に、次のコマンドでタグファイルをコンパイルできます。
$ riot my-tag.tag --config riot.config.js