React.js に影響を受けている新しめのJSビューフレームワーク。基本的にはモダンなAngular2やReactの謳う「コンポーネントベース・仮想DOM」。Vue.js くらいのシンプルさで実装できるらしい。
独自タグをマウントし、内部に構造(html)とデザイン(css)と機能(js)を一緒くたにかける。しかもHTMLソースコード上で(外部読込も可)。ソースがreactやangularに比べめっちゃ軽量。フルスタックならangular2だが、reactとはやってること一緒(UIだけ)なのにriotのが明らかに軽い。イベントエミッタ(オブザーバブル)とルータがついてきたり、とってもエコ。Ajaxには superagent でも使えばよいのでは。
- 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 がデフォルトに
テンプレート変数内で <BR>
などは自動的にエスケープされる。これについてなんとかするなら「改行コードが入った文字列データをプロパティに持つ小さなタグ」を作っていれこみ、内部でごにょるしかないんだけど...ぶっちゃけ <pre>
で囲んだ中にテンプレート変数で文字列ぶちこんで CSS で見た目整えるのが楽。
<pre class="message">{ this.message }</pre>
https://qiita.com/risacan/items/d782fa24ad4d4f8b73f8
<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>
// 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 は子タグ自身を指すぽい
Ver2 までは riot-tag="hoge"
でした。親タグのHTML部分で動的に子タグをマウント → 親子関係維持したいときとか使える。
<div data-is="my-tag"></div>
es6どころか、es5のサポートも怪しい環境では Polyfill が必要。
es5-shim
や babel
の es2015-loose
プリセットなど。
http://blog.lebe.jp/post/150338847590/modern-javascript-riotjs
http://riotjs.com/ja/ http://qiita.com/narikei/items/1a7fbd7895cfb4220172s
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
http://riotjs.com/ja/guide/#タグのマウント > ミックスイン
http://codepen.io/m1chuang/pen/ojoJza
別メモ参照
html5 の data-* データバインドは data-json='{"json": "desuyo"}'>みたいにJSONを渡せるがRiot.jsでは動かん。
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="{this.posts}" // イテレータブルなやつだけ? for~in形: each="{post, i in this.posts}" // 配列オブジェクト大体まわせる
DOMをいじって(例えばinputのvalueをとって)みたいなことはやりづらい。inputのvalueの入力状況を見てSubmitボタンをdisableしたりするなら
- inputで受け付けるプロパティを用意
- inputにonchangeイベントで上記プロパティへのvalue値セットメソッドをよしなに
- button(submit)側ではdisable={!property}とかやっとけばいい
jQuery使う機会はかなり減るけど「jQuery依存プラグインをRiot.js側のmountやupdateイベントでプラグインをどうこうする」みたいなときは連携が必要になる気がする const $ = require('jquery');
とか import $ from 'jquery'
とかで本体を先に読み込んでおけばRiot.jsのscript内で$とかそのまま使えます。
app.js (Browserifyのエントリjsファイル) でimportする時だけ名前をスネークかキャメルケースにする必要あるが。
独自イベントとか監視して外からデータ流し込む。 別メモ参照「riotjs で Observerパターン」
ルーティングは riot-route という別ライブラリを使用。 こっちも別メモ参照「riot-route で SPA」
ジェネレータ。いまいち使い所がわからん。
data-hoge="{}"
とかで子タグに渡した値がHTMLに反映されてないっぽかったりするけど、riotは {}
を判定して falsy だとその属性・タグ自体を隠したりする。値自体はちゃんとわたってるのでOKす。
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.
// 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>
const $ = require('jquery')
const request = require('superagent')
// 状態がわかりづらい
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 とかだと
// 一緒に更新されそうだけどうまく渡らない
{ 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: 入れ子になっているカスタムタグ
<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>
for (let hoge of self.parent.tags['my-children']) {
}
<!-- 3件のみ表示 -->
<li each={ t, i in this.arr.slice(0, 3) }>{ t.text }</li>
//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>
//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
}
var self = this
self.update()
self.root.update()
self.parent.update()
<script src="mytag.tag"></script>
<tr data-is="mytag"></tr>
<script>
riot.mount('mytag');
</script>
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);
});