Skip to content

Instantly share code, notes, and snippets.

@yano3nora
Last active February 20, 2020 04:52
Show Gist options
  • Save yano3nora/52ad3c08ba1a6accab2e402081d7acc7 to your computer and use it in GitHub Desktop.
Save yano3nora/52ad3c08ba1a6accab2e402081d7acc7 to your computer and use it in GitHub Desktop.
[riot: Riot.js] Riot.js - Simple view fw. #riot #js

Overview

React.js に影響を受けている新しめのJSビューフレームワーク。基本的にはモダンなAngular2やReactの謳う「コンポーネントベース・仮想DOM」。Vue.js くらいのシンプルさで実装できるらしい。

Official

http://riotjs.com/guide/

Feature

独自タグをマウントし、内部に構造(html)とデザイン(css)と機能(js)を一緒くたにかける。しかもHTMLソースコード上で(外部読込も可)。ソースがreactやangularに比べめっちゃ軽量。フルスタックならangular2だが、reactとはやってること一緒(UIだけ)なのにriotのが明らかに軽い。イベントエミッタ(オブザーバブル)とルータがついてきたり、とってもエコ。Ajaxには superagent でも使えばよいのでは。

Version3 での変更点

  • riot-tag属性の廃止、data-is属性を使う
  • data-isはネストしたタグでも使用可能
  • id, name属性でのDOM参照廃止
    • ref 属性と this.refs を使う
    • 参照する場合はthis.refs.hoge
  • eachループの書き方が { value, key in obj }
  • riot.route (ルーティング) が本体から分離
    • 別途npmでriot-routeを持ってくる必要がある
    • 書き方例:riot.route.start() → route.start()
  • mount前にupdate, updatedイベント発火しない
    • マウント前はbefore-mount, mountを利用
    • update, updatedはタグのアップデート時のみに
  • 子タグイベントで、親タグをアップデートしない
  • observableはメソッドチェーンで複数イベント扱うように
  • <style> は Scoped CSS がデフォルトに

TIPS & REFERENCES

改行コードなんとかしたい

テンプレート変数内で <BR> などは自動的にエスケープされる。これについてなんとかするなら「改行コードが入った文字列データをプロパティに持つ小さなタグ」を作っていれこみ、内部でごにょるしかないんだけど...ぶっちゃけ <pre> で囲んだ中にテンプレート変数で文字列ぶちこんで CSS で見た目整えるのが楽。

<pre class="message">{ this.message }</pre>

Style Guide

https://qiita.com/risacan/items/d782fa24ad4d4f8b73f8

Dynamic Mount - 子タグの動的マウント

https://goo.gl/kUoWYV
https://goo.gl/zSUXVV

<parent>

  <button onclick="{ this.dynamicMount.bind(this, 'hoge') }">hoge</button>

  <!-- 別に virtual ぢゃなくてもいいのよ -->
  <virtual if="{ this.current == 'hoge' }">
    <hoge></hoge>
  </virtual>
  <virtual if="{ this.current == 'fuga' }">
    <fuga></fuga>
  </virtual>
  <virtual if="{ this.current == 'piyo' }">
    <piyo></piyo>
  </virtual>
  
  <script>
    this.current = null
    this.dynamicMount (name) => {
      this.current = name
      // button を押下すると hoge が マウントされる
      // hoge の onmount は当然一度だけ走る
      //( <parent> の onmount 時につられてマウントされない )
      // fuga piyo の onmount は走らない
    }
  </script>
</parent>

mixin の使い方

// parent
riot.mixin('focusIn', {
  focusIn: function (command) {
    this.isFocusIn = true
    this.parent.focusIn(this.name)
    riot.mount('.p-focusin', `terminal-${this.name}-${command}`, {parent: this})
  }
})

//child
<button onclick="{ this.focusIn.bind(this, 'reliabilities') }">hogehoge</button>

this.mixin('focusIn') //ちゃんと this は子タグ自身を指すぽい

通常のHTMLタグにマウントするタグを紐づけ

Ver2 までは riot-tag="hoge" でした。親タグのHTML部分で動的に子タグをマウント → 親子関係維持したいときとか使える。

<div data-is="my-tag"></div>

旧ブラウザ/プラットフォームで動かす

es6どころか、es5のサポートも怪しい環境では Polyfill が必要。 es5-shimbabeles2015-loose プリセットなど。

Reactを古いブラウザに対応

https://goo.gl/c4YWC9

es5-shimをつかってes5に対応させる

https://goo.gl/dmsXGC

Riot.js+Browserify+Babel

http://blog.lebe.jp/post/150338847590/modern-javascript-riotjs

React.jsではなくRiot.jsを採用した話

http://riotjs.com/ja/ http://qiita.com/narikei/items/1a7fbd7895cfb4220172s

Startup

riot.js自体は本体[riot.min.js]とriotコンパイラ[compiler.js]で動作する。CDNで入れてブラウザでレンダリング時にコンパイルしてもOK。独自タグを定義する[.tag]はインラインでも外部読込でもOK。webpackやらgulpやらで使う場合は別メモ参照。 https://byuzensen.com/javascript-framework-riotjs-start http://phiary.me/riot-js-use-sass/

基本的なテクニック

http://etc9.hatenablog.com/entry/2016/06/14/222551

mixin (タグ間の機能継承)

http://riotjs.com/ja/guide/#タグのマウント > ミックスイン

Riot.jsでPagination

http://codepen.io/m1chuang/pen/ojoJza

riotjs で モーダルウィンドウ

別メモ参照

jsonでデータバインド不可

html5 の data-* データバインドは data-json='{"json": "desuyo"}'>みたいにJSONを渡せるがRiot.jsでは動かん。

各種plugin連携時にはsetTimeoutが便利

update()で自身を更新しまくるタグの中で、動的に追加した要素へ jQuery プラグイン的なものやイベントハンドラ的なものをあてたいとき、従来の方法では対処できない(仮想DOMの動的追加ケース)。この際は this.on('update', {}) 内で setTimeout(()=>{/*何かプラグイン的アレ*/}, 1000) で対処。

// 以下 materialize.css の modals のトリガを仕込む例。
// ( webpack で各種依存はクリア済み前提 )
// @see http://materializecss.com/modals.html

this.on('update', () => {
  setTimeout(() => {
    $('.modal').modal()
  }, 1000)
})

each ループいろいろ

基本形: each="{this.posts}" // イテレータブルなやつだけ? for~in形: each="{post, i in this.posts}" // 配列オブジェクト大体まわせる

タグインスタンスはクラスです

DOMをいじって(例えばinputのvalueをとって)みたいなことはやりづらい。inputのvalueの入力状況を見てSubmitボタンをdisableしたりするなら

  1. inputで受け付けるプロパティを用意
  2. inputにonchangeイベントで上記プロパティへのvalue値セットメソッドをよしなに
  3. button(submit)側ではdisable={!property}とかやっとけばいい

jQuery連携

jQuery使う機会はかなり減るけど「jQuery依存プラグインをRiot.js側のmountやupdateイベントでプラグインをどうこうする」みたいなときは連携が必要になる気がする const $ = require('jquery'); とか import $ from 'jquery' とかで本体を先に読み込んでおけばRiot.jsのscript内で$とかそのまま使えます。

tag名は - ハイフンつなぎok

app.js (Browserifyのエントリjsファイル) でimportする時だけ名前をスネークかキャメルケースにする必要あるが。

Observable

独自イベントとか監視して外からデータ流し込む。 別メモ参照「riotjs で Observerパターン」

Route

ルーティングは riot-route という別ライブラリを使用。 こっちも別メモ参照「riot-route で SPA」

yield

ジェネレータ。いまいち使い所がわからん。

falsy 判定について

data-hoge="{}" とかで子タグに渡した値がHTMLに反映されてないっぽかったりするけど、riotは {} を判定して falsy だとその属性・タグ自体を隠したりする。値自体はちゃんとわたってるのでOKす。


Built-in API

this.on() - Life cycle.

this.on('before-mount', () => {})    // Before mounting.
this.on('mount', () => {})           // After mounting.
this.on('update', () => {})          // Before updating.
this.on('updated', () => {})         // After updating.
this.on('before-unmount', () => {})  // Before un-mounting.
this.on('unmount', () => {})         // After un-mounting.

riot.mount()

// app.js (entry.js)
import riot   from 'riot';
import sample from './sample.tag';
document.addEventListener('DOMContentLoaded', function(){
  riot.mount('*');
});

//html
<script src="app.js"></script>
<sample></sample>

other Libraries

const $ = require('jquery')  
const request = require('superagent')

share variables between tags

// 状態がわかりづらい
this.param = this.parent.param
this.tags['tag-name'].param = 'fuga'

// タグをネストしまくって直接共有すべき
<div each="{ param in this.parent.param }">{ param }</div>
this.parent.param.push('val')
this.parent.update()  // 子タグも全部update
// このとき 子タグで this.param = this.parent.param とかだと
// 一緒に更新されそうだけどうまく渡らない

Templete Variables {}

{ value }
{ title || '名称未設定' }
{ results ? '準備OK!' : '読み込み中...' }
{ new Date() }
{ message.length > 140 && 'メッセージが長すぎ' }

// タグ内はthis.なもの以外はアクセスできない?
const hoge  // { hoge } でアクセス不可

// 真偽値属性 (checked, selected など) 
// 変数がfalsy(nullなど含む)だと無視され属性自体がなくなりまする
<input checked={ isCheck }>

// Conditional で表示非表示
<div if = { error }>  // true: add , false: remove
<div show = { error }>  // true: visible
<div hide = { error }>  // true: hidden

// クラス名のショートカット構文
// 以下は done が真のとき class="completed" になる
<div class="{ completed: done }"></div> 

// ref (名前付き) 属性でDOMにアクセス
<form ref="login" onsubmit={ submit }>
const form = this.refs.login

/* Tagインスタンスが自動的に持つプロパティ */
//  refs: ref="" をもつDOM
//  opts: タグに渡されたオプション(each内で展開不可)
//  parent: 親タグ (もしあれば)
//  root: ルートになるDOMノード
//  tags: 入れ子になっているカスタムタグ

each loop (v3~)

<app>
  <!-- layout -->
  <ul>
    <!-- riot独特のループ。for in for of の合わせ技みたいなやつ -->
    <!-- 純粋なJSのループとルールが異なるので特に注意する -->
    <!-- 一次元配列, オブジェクト(hash), イテレータオブジェクトで動きが違う -->
    <li each="{ task, key in tasks }">{ key }: { task }</li>
    <li each="{ task in tasks }">{ task }</li>
  </ul>

  <!-- javascript -->
  <script>
    this.tasks = {
      'a': {'body': 'hoge'},
      'b': {'body': 'fuga'},
      'c': {'body': 'piyo'}
    };
    // each外ではおとなしく for (let key in hash) { hash[key] } で回す
  </script>
</app>

child tags loop ( for of )

for (let hoge of self.parent.tags['my-children']) {
}

loop + array.slice

<!-- 3件のみ表示 -->
<li each={ t, i in this.arr.slice(0, 3) }>{ t.text }</li>

this.opts data-binding

//weather.tag
<weather>
    <p>{ date }の天気は{ weather }です。</p>
    <script>
        this.date = opts.date
        this.weather = opts.dataState
    </script>
    <style scoped>
        :scope {
            display: block
        }
    </style>
</weather>

//html
<weather date="2016/01/01" data-state="晴れ"></weather>

on* Event Method with aug

//html (riot-tag-html)
<a href="#" onclick={ this.hello.bind(this, 'yamada') }>yamada</a>

//js (riot-tag-script)
this.hello = (name, e) => {
  console.log('hello '+name)  //hello yamada
  console.dir(e)  //Event
}

// 引数が2以上のときは data-bind した方が良い
// → bind により Event が引数のケツに強制で入るので引数の順番が狂って面倒

//html
<a href="#" data-firstname="tarou" data-lastname="yamada" onclick={ this.hello }>yamada</a>

//js
this.hello = (e) => {
  console.log(`Hello ${e.target.dataset.lastname} ${e.target.dataset.first_name}`)
  console.dir(e) //event
}

update() this, parent, root

var self = this
self.update()  
self.root.update()
self.parent.update()

data-is create Riot-Tag normal HTML-Tag

<script src="mytag.tag"></script>
<tr data-is="mytag"></tr>
<script>
  riot.mount('mytag');
</script>

Ajax with SuperAgent

POST通信時、PHPなんかはContent-typeを「application/x-www-form-urlencoded」だと勝手に解釈して $_POST とかにぶちこむ。このためAjaxライブラリとの相性次第で「POSTで送ってるのに値が入らない」ことがある。コードは superagent のサンプルコード。別メモ「SuperAgent」を参照のこと。

// get (with query)
request.get(url)
  .query({user: "Taro"})
  .set('X-Requested-With', 'XMLHttpRequest')
  .end((err, res) => {
    console.log(res.body);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment