70
58

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Riot.jsの使い方について

Last updated at Posted at 2018-01-23

もとは社内のRiot.jsを知らない人、名前は知ってるけど使ったこと無い人向けに
Riot.jsの使い方を知って貰うために作成していた資料です。

Riot.jsとは

React.jsのようなJavascriptの軽量UIライブラリです。
カスタムタグにHTML、JS、CSSなどを記述して、それらを組み合わせてページを作成する事が出来ます。

公式サイト

前提

node.jsはインストール済み
tagファイルはプリコンパイルして使用

ディレクトリ構成

下記のような構成になるようにファイルとディレクトリを作成

sample
  ┣ js
  ┣ tags
  ┗ index.html ← 中身は空っぽ

プリコンパイルするための環境作成

まずは、riotをインストールします。

npm install riot -g

プリコンパイルしたくない!って場合

インブラウザ・コンパイルを利用する事で、htmlが呼び出されたときにブラウザでコンパイルして実行する事が出来ます。
上記のriotのインストールも不要になります。
ブラウザでコンパイルを行っても、パフォーマンスへの影響はほとんど無いようです。

プリコンパイルのメリットについては、公式サイトに記載されています。

公式:コンパイラ

※以下は、プリコンパイルする事を前提に記載しています。

app.tag作成

tagsディレクトリの直下にcoffee.tagファイルを作成。
coffeeという名前のカスタムタグの構成を作成します。
※タグ名とファイル名は異なっていても構いません。

coffee.tag
<coffee>
  <h1>{name}</h1>
  <script>
    this.name = 'コーヒー牛乳';
  </script>
</coffee>

riotコマンド実行

riot ./tags ./js

tagsディレクトリ直下の*.tagファイルが、jsディレクトリに*.jsファイルとして出力されます。
指定できるオプションについては、riot --helpで確認できます。

この時点では、下記のようなディレクトリ構成になっています。

sample
  ┣ js
  ┃  ┗ coffee.js
  ┣ tags
  ┃  ┗ coffee.tag
  ┗ index.html ← 中身は空っぽ

index.html修正

空のカスタムタグ(coffeeタグ)をhtmlに記載し、下記のように修正します。

index.html
<html>
  <head>
    <title>sample</title>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/riot/3.7.3/riot.min.js'></script>
  </head>
  <body>
    <coffee></coffee>

    <script src="./js/coffee.js"></script>

    <script>riot.mount('coffee')</script>
  </body>
</html>

index.htmlにアクセスすると、下記のように表示されます。
gyunyu.jpg

カスタムタグを使いたくない場合

htmlに空のカスタムタグを書きたくない場合、idやclass名を指定してマウントする事もできます。

ichigo.tag
<ichigo>
  <h1>{name}</h1>
  <script>
    this.name = 'いちご牛乳';
  </script>
</ichigo>
lemon.tag
<lemon>
  <h1>{name}</h1>
  <script>
    this.name = 'れもん牛乳';
  </script>
</lemon>
index.html
<html>
  <head>
    <title>sample</title>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/riot/3.7.3/riot.min.js'></script>
  </head>
  <body>
    <coffee></coffee>
    <div class="lemon"></div>
    <div id="ichigo"></div>

    <script src="./js/coffee.js"></script>
    <script src="./js/ichigo.js"></script>
    <script src="./js/lemon.js"></script>

    <script>riot.mount('coffee')</script>
    <script>riot.mount('#ichigo', 'ichigo')</script>
    <script>riot.mount('.lemon', 'lemon')</script>
  </body>
</html>
  • riot.mount('#ichigo', 'ichigo')
    idがichigoの要素にichigoタグを展開

  • riot.mount('.lemon', 'lemon')
    class名がlemonの要素にlemonタグを展開

ロジックを共有する

mixinを用いることで別ファイルに記載したロジックを実行する事ができます。

app.tag
riot.mixin('Gyunyu', {
    output : function(v) {
        console.log(v);
    }
});
coffee.tag
<coffee>
<h1>{name}</h1>
<script>
this.name = 'コーヒー牛乳';
this.mixin("Gyunyu");
this.output("これはコーヒー牛乳です。");
</script>
</coffee>
lemon.tag
<lemon>
<h1>{name}</h1>
<script>
this.name = 'れもん牛乳';
this.mixin("Gyunyu");
this.output("これはれもん牛乳です。");
</script>
</lemon>
ichigo.tag
<ichigo>
<h1>{name}</h1>
<script>
this.name = 'いちご牛乳';
this.mixin("Gyunyu");
this.output("これはいちご牛乳です。");
</script>
</ichigo>
index.html
<html>
  <head>
    <title>sample</title>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/riot/3.7.3/riot.min.js'></script>
  </head>
  <body>
    <coffee></coffee>
    <div class="lemon"></div>
    <div id="ichigo"></div>

    <script src="./js/app.js"></script>
    <script src="./js/coffee.js"></script>
    <script src="./js/ichigo.js"></script>
    <script src="./js/lemon.js"></script>

    <script>riot.mount('coffee')</script>
    <script>riot.mount('.lemon', 'lemon')</script>
    <script>riot.mount('#ichigo', 'ichigo')</script>
  </body>
</html>

tagファイルをプリコンパイルして、index.htmlにアクセスすると、
下記のようなログが出力されます。
Clipboard02.jpg

イベント処理

ボタンがクリックされたときに何らかの処理を行いたい場合は、onclick属性に実行したい関数を設定します。

coffee.tag
<coffee>
<h1>{name}</h1>
<button type="button" name="button1" onclick={print}>ボタン1</button>
<script>
this.name = 'コーヒー牛乳';
this.mixin("Gyunyu");
this.output("これはコーヒー牛乳です。");

this.print = function () {
    console.log("ボタンをクリックしました");
}
</script>
</coffee>

ボタンが押された時に、イベントオブジェクトを取得したい場合は、
下記のように、呼び出される関数に引数を記述すると、イベントオブジェクトを取得する事ができます。

coffee.tag
<coffee>
<h1>{name}</h1>
<button type="button" name="button1" onclick={print}>ボタン1</button>
<button type="button" name="button2" onclick={print}>ボタン2</button>
<script>
this.name = 'コーヒー牛乳';
this.mixin("Gyunyu");
this.output("これはコーヒー牛乳です。");

this.print = function (event) {
    console.log("クリックしたボタン:" + event.target.name);
}
</script>
</coffee>

「ボタン1」をクリックすると「クリックしたボタン:button1」、「ボタン2」をクリックすると「クリックしたボタン:button2」がコンソールに出力されます。

イベントオブジェクト以外の引数を受け取りたい場合は、bind関数を用いる事で値を渡すことが出来るようになります。

coffee.tag
<coffee>
<h1>{name}</h1>
<button type="button" name="button1" onclick={print.bind(this, "これはボタン1です")}>ボタン1</button>
<button type="button" name="button2" onclick={print.bind(this, "これはボタン2です")}>ボタン2</button>
<script>
this.name = 'コーヒー牛乳';
this.mixin("Gyunyu");
this.output("これはコーヒー牛乳です。");

this.print = function (message, event) {
    console.log("クリックしたボタン:" + event.target.name + "" + message);
}
</script>
</coffee>

入力された値を取得する

要素にref属性を設定することで、JavaScriptからアクセスする事ができるようになります。
this.refs.ref属性に指定した名前.valueで値を取得する事ができます。

coffee.tag
<coffee>
<h1>{name}</h1>
<input type="text" name="text1" value="" ref="message">
<button type="button" name="button1" onclick={print}>表示</button>
<script>
this.name = 'コーヒー牛乳';
this.mixin("Gyunyu");
this.output("これはコーヒー牛乳です。");

this.print = function () {
    this.name = this.refs.message.value
}
</script>
</coffee>

「text1」を入力して「表示」ボタンをクリックすると、h1タグのテキストに入力した値が反映されます。

データバインディング

Riot.jsにはデータバインディングの機能がありません。
しかし、oninput属性を用いることでデータバインディングみたいな機能を作る事が出来ます。

coffee.tag
<coffee>
<h1>{name}</h1>
<input type="text" name="text1" value="" ref="message" oninput={print}>
<script>
this.name = 'コーヒー牛乳';
this.mixin("Gyunyu");
this.output("これはコーヒー牛乳です。");

this.print = function () {
    this.name = this.refs.message.value
}

</script>
</coffee>

oninputイベントが発火するとprint関数を実行してnameを書き換える仕組みになってます。

他にも、update関数を使うことでデータバインディングのような機能を作ることができるようです。
参考:Riot.jsでデータバインディング

coffee.tag
<coffee>
<h1>{this.refs.message.value}</h1>
<input type="text" name="text" value="" ref="message" oninput={update}>
<script>
</script>
</coffee>

追記:上記のupdate関数を使った方法、アクセス時にthis.refs.message is undefinedってエラーが出てました。
<h1>{this.refs.message.value}</h1>をinputタグより下に書くか、下記のような書き方にしないとダメなのかもしれない。

coffee.tag
<coffee>
<h1>{name}</h1>
<input type="text" name="text" value="" ref="message" oninput={update}>
<script>
this.on("update", function() {
    this.name = this.refs.message.value;
});
</script>
</coffee>

タグベースルーティングの設定

route+tag.jsを読み込むと利用できるようになります。

index.html
<html>
  <head>
    <title>sample</title>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/riot/3.7.3/riot.min.js'></script>
    <script src='https://cdn.jsdelivr.net/npm/riot-route@3.1.2/dist/route+tag.min.js'></script>
  </head>
  <body>
    <item></item>
    <script>
      riot.mount('*');
    </script>
  </body>
</html>

マウントするタグを切り替えるための設定を記述したファイルを作成します。
下記は、「/index.html」にアクセスした場合は、topタグをマウントし、
「/gyunyu/coffee」「/gyunyu/lemon」「/gyunyu/ichigo」などにアクセスした場合は、gyunyuタグをマウントする
ルートを記載しています。

gyunyu-router.tag
<item>
    <router>
        <route path="/index.html"><top /></route>
        <route path="/gyunyu/*"><gyunyu /></route>
    </router>
</item>

カスタムタグを作成します。

app.tag
<top>
<h1>牛乳一覧</h1>
<a href="/sample/gyunyu/coffee">コーヒー牛乳</a><br>
<a href="/sample/gyunyu/lemon">レモン牛乳</a><br>
<a href="/sample/gyunyu/ichigo">いちご牛乳</a><br>
</top>

<gyunyu>
<h1>値段</h1>
</gyunyu>

index.htmlを下記のように修正。

index.html
<html>
  <head>
    <title>sample</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/riot/3.7.3/riot.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/riot-route@3.1.2/dist/route+tag.min.js"></script>
  </head>
  <body>
    <item></item>
    <script src="/sample/js/app.js"></script>
    <script src="/sample/js/gyunyu-router.js"></script>
    <script>
      route.base("/sample");
      riot.mount("*");
    </script>
  </body>
</html>

http://localhost/sample/index.htmlにアクセスするとtopタグをマウントし、
http://localhost/sample/gyunyu.htmlにアクセスするとgyunyuタグがマウントされます。

gyunyu-router.tagには、「/index.html」と「/gyunyu/*」と記載しています。
「/sample/index.html」「/sample/gyunyu/*」のように記載していない理由は、
index.htmlにroute.base("/sample")を記述しているためです。
ベースパスを/sampleに設定しているため、ルートには/sampleの後ろに続く文字列だけを記載しています。

routeイベントをフックすることで、URLを取得する事ができます。
下記の場合だと、「/gyunyu/*」の「*」に該当する値を取得する事が出来ます。

app.tag
<gyunyu>
<h1>値段</h1>
<div>{price}</div>
<script>
this.on("route", id => {
    if (id == "coffee") {
        this.price = "100円";
    } else if (id == "lemon") {
        this.price = "110円";
    } else if (id == "ichigo") {
        this.price = "120円";
    } else {
        this.price = "0円";
    }
})
</script>
</gyunyu>

その他

繰り返し

each属性を用いることで、要素の数だけループを行うことが出来ます。

app.tag
<top>
<h1>牛乳一覧</h1>
<a href="/sample/gyunyu/coffee">コーヒー牛乳</a><br>
<a href="/sample/gyunyu/lemon">レモン牛乳</a><br>
<a href="/sample/gyunyu/ichigo">いちご牛乳</a><br>
<ul>
<li each={milk}>{name}は{price}です。</li>
</ul>
<script>
this.milk = [
    {name : "コーヒーミルク", price : "100円"},
    {name : "レモンミルク", price  : "110円"},
    {name : "いちごミルク", price  : "120円"}
];
</script>
</top>

条件分岐

if属性を用いることで、条件に応じて要素を追加/削除する事が出来ます。

app.tag
<top>
<h1>牛乳一覧</h1>
<a href="/sample/gyunyu/coffee">コーヒー牛乳</a><br>
<a href="/sample/gyunyu/lemon">レモン牛乳</a><br>
<a href="/sample/gyunyu/ichigo">いちご牛乳</a><br>
<ul>
<li each={milk}>{name}は{price}です。<div if={name === "レモンミルク"}>(レモン果肉入り)</div></li>
</ul>
</div>
<script>
this.milk = [
    {name : "コーヒーミルク", price : "100円"},
    {name : "レモンミルク", price  : "110円"},
    {name : "いちごミルク", price  : "120円"}
];
</script>
</top>

showhide属性を用いることで、条件に応じて要素を表示/非表示する事ができます。

app.tag
<top>
<h1>牛乳一覧</h1>
<a href="/sample/gyunyu/coffee">コーヒー牛乳</a><br>
<a href="/sample/gyunyu/lemon">レモン牛乳</a><br>
<a href="/sample/gyunyu/ichigo">いちご牛乳</a><br>
<ul>
<li each={milk}>{name}は{price}です。<div show={name === "コーヒーミルク"}>(税込み)</div><div hide={name === "コーヒーミルク"}>(地域限定)</div><div if={name === "レモンミルク"}>(レモン果肉入り)</div></li>
</ul>
</div>
<script>
this.milk = [
    {name : "コーヒーミルク", price : "100円"},
    {name : "レモンミルク", price  : "110円"},
    {name : "いちごミルク", price  : "120円"}
];
</script>
</top>

show : 条件が真のときに、style="display: ''"として要素を表示します。
hide : 条件が真のときに、style="display: none"として要素を非表示にします。

最後に

Riot.jsは学習途中で分からない事が多々あるのですが、Riot.jsの学習は一旦置いといて、
今更な感じもしますが、PWAの学習を始めようかなと最近思ってます。

70
58
1

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
70
58

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?